195 |
* It's better to ignore such fs and continue. */ |
* It's better to ignore such fs and continue. */ |
196 |
void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) |
void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) |
197 |
{ |
{ |
198 |
ssize_t buf_len; |
uint8_t *dst; |
199 |
|
unsigned small_off; |
200 |
|
ssize_t read_len; |
201 |
|
|
202 |
|
dbg("get buffer off 0x%llx(%llu), len 0x%zx", |
203 |
|
(unsigned long long) off, (unsigned long long) off, len); |
204 |
|
|
|
dbg("get buffer off 0x%llx(%llu), len 0x%zx", (unsigned long long) off, (unsigned long long) off, len); |
|
205 |
/* check if requested area fits in superblock buffer */ |
/* check if requested area fits in superblock buffer */ |
206 |
if (off + len <= SB_BUFFER_SIZE) { |
if (off + len <= SB_BUFFER_SIZE |
207 |
|
/* && off <= SB_BUFFER_SIZE - want this paranoid overflow check? */ |
208 |
|
) { |
209 |
if (id->sbbuf == NULL) { |
if (id->sbbuf == NULL) { |
210 |
id->sbbuf = xmalloc(SB_BUFFER_SIZE); |
id->sbbuf = xmalloc(SB_BUFFER_SIZE); |
211 |
} |
} |
212 |
|
small_off = off; |
213 |
|
dst = id->sbbuf; |
214 |
|
|
215 |
/* check if we need to read */ |
/* check if we need to read */ |
216 |
if ((off + len) > id->sbbuf_len) { |
len += off; |
217 |
dbg("read sbbuf len:0x%llx", (unsigned long long) (off + len)); |
if (len <= id->sbbuf_len) |
218 |
if (lseek(id->fd, 0, SEEK_SET) != 0) { |
goto ret; /* we already have it */ |
219 |
dbg("seek(0) failed"); |
|
220 |
return NULL; |
dbg("read sbbuf len:0x%x", (unsigned) len); |
221 |
} |
id->sbbuf_len = len; |
222 |
buf_len = full_read(id->fd, id->sbbuf, off + len); |
off = 0; |
223 |
if (buf_len < 0) { |
goto do_read; |
|
dbg("read failed (%s)", strerror(errno)); |
|
|
return NULL; |
|
|
} |
|
|
dbg("got 0x%zx (%zi) bytes", buf_len, buf_len); |
|
|
id->sbbuf_len = buf_len; |
|
|
if ((uint64_t)buf_len < off + len) { |
|
|
dbg("requested 0x%zx bytes, got only 0x%zx bytes", len, buf_len); |
|
|
return NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
return &(id->sbbuf[off]); |
|
224 |
} |
} |
225 |
|
|
226 |
if (len > SEEK_BUFFER_SIZE) { |
if (len > SEEK_BUFFER_SIZE) { |
227 |
dbg("seek buffer too small %d", SEEK_BUFFER_SIZE); |
dbg("seek buffer too small %d", SEEK_BUFFER_SIZE); |
228 |
return NULL; |
return NULL; |
229 |
} |
} |
230 |
|
dst = id->seekbuf; |
|
/* get seek buffer */ |
|
|
if (id->seekbuf == NULL) { |
|
|
id->seekbuf = xmalloc(SEEK_BUFFER_SIZE); |
|
|
} |
|
231 |
|
|
232 |
/* check if we need to read */ |
/* check if we need to read */ |
233 |
if ((off < id->seekbuf_off) || ((off + len) > (id->seekbuf_off + id->seekbuf_len))) { |
if ((off >= id->seekbuf_off) |
234 |
dbg("read seekbuf off:0x%llx len:0x%zx", (unsigned long long) off, len); |
&& ((off + len) <= (id->seekbuf_off + id->seekbuf_len)) |
235 |
if (lseek(id->fd, off, SEEK_SET) != off) { |
) { |
236 |
dbg("seek(0x%llx) failed", (unsigned long long) off); |
small_off = off - id->seekbuf_off; /* can't overflow */ |
237 |
return NULL; |
goto ret; /* we already have it */ |
|
} |
|
|
buf_len = full_read(id->fd, id->seekbuf, len); |
|
|
if (buf_len < 0) { |
|
|
dbg("read failed (%s)", strerror(errno)); |
|
|
return NULL; |
|
|
} |
|
|
dbg("got 0x%zx (%zi) bytes", buf_len, buf_len); |
|
|
id->seekbuf_off = off; |
|
|
id->seekbuf_len = buf_len; |
|
|
if ((size_t)buf_len < len) { |
|
|
dbg("requested 0x%zx bytes, got only 0x%zx bytes", len, buf_len); |
|
|
return NULL; |
|
|
} |
|
238 |
} |
} |
239 |
|
|
240 |
return &(id->seekbuf[off - id->seekbuf_off]); |
id->seekbuf_off = off; |
241 |
|
id->seekbuf_len = len; |
242 |
|
id->seekbuf = xrealloc(id->seekbuf, len); |
243 |
|
small_off = 0; |
244 |
|
dst = id->seekbuf; |
245 |
|
dbg("read seekbuf off:0x%llx len:0x%zx", |
246 |
|
(unsigned long long) off, len); |
247 |
|
do_read: |
248 |
|
if (lseek(id->fd, off, SEEK_SET) != off) { |
249 |
|
dbg("seek(0x%llx) failed", (unsigned long long) off); |
250 |
|
goto err; |
251 |
|
} |
252 |
|
read_len = full_read(id->fd, dst, len); |
253 |
|
if (read_len != len) { |
254 |
|
dbg("requested 0x%x bytes, got 0x%x bytes", |
255 |
|
(unsigned) len, (unsigned) read_len); |
256 |
|
err: |
257 |
|
/* No filesystem can be this tiny. It's most likely |
258 |
|
* non-associated loop device, empty drive and so on. |
259 |
|
* Flag it, making it possible to short circuit future |
260 |
|
* accesses. Rationale: |
261 |
|
* users complained of slow blkid due to empty floppy drives. |
262 |
|
*/ |
263 |
|
if (off < 64*1024) |
264 |
|
id->error = 1; |
265 |
|
/* id->seekbuf_len or id->sbbuf_len is wrong now! Fixing. */ |
266 |
|
volume_id_free_buffer(id); |
267 |
|
return NULL; |
268 |
|
} |
269 |
|
ret: |
270 |
|
return dst + small_off; |
271 |
} |
} |
272 |
|
|
273 |
void volume_id_free_buffer(struct volume_id *id) |
void volume_id_free_buffer(struct volume_id *id) |
278 |
free(id->seekbuf); |
free(id->seekbuf); |
279 |
id->seekbuf = NULL; |
id->seekbuf = NULL; |
280 |
id->seekbuf_len = 0; |
id->seekbuf_len = 0; |
281 |
|
id->seekbuf_off = 0; /* paranoia */ |
282 |
} |
} |