buf_size = dir_entries * sizeof(struct vfat_dir_entry);
buf = volume_id_get_buffer(id, fat_partition_off + root_start_off, buf_size);
if (buf == NULL)
- goto found;
+ goto ret;
label = get_attr_volume_id((struct vfat_dir_entry*) buf, dir_entries);
volume_id_set_label_string(id, vs->type.fat.label, 11);
}
volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS);
- goto found;
+ goto ret;
fat32:
/* FAT32 root dir is a cluster chain like any other directory */
next_cluster = root_cluster;
maxloop = 100;
while (--maxloop) {
- uint32_t next_off_sct;
+ uint64_t next_off_sct;
uint64_t next_off;
uint64_t fat_entry_off;
int count;
dbg("next_cluster 0x%x", (unsigned)next_cluster);
- next_off_sct = (next_cluster - 2) * vs->sectors_per_cluster;
+ next_off_sct = (uint64_t)(next_cluster - 2) * vs->sectors_per_cluster;
next_off = (start_data_sct + next_off_sct) * sector_size_bytes;
dbg("cluster offset 0x%llx", (unsigned long long) next_off);
/* get cluster */
buf = volume_id_get_buffer(id, fat_partition_off + next_off, buf_size);
if (buf == NULL)
- goto found;
+ goto ret;
dir = (struct vfat_dir_entry*) buf;
count = buf_size / sizeof(struct vfat_dir_entry);
dbg("fat_entry_off 0x%llx", (unsigned long long)fat_entry_off);
buf = volume_id_get_buffer(id, fat_partition_off + fat_entry_off, buf_size);
if (buf == NULL)
- goto found;
+ goto ret;
/* set next cluster */
next_cluster = le32_to_cpu(*(uint32_t*)buf) & 0x0fffffff;
}
volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS);
- found:
+ ret:
// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
// id->type = "vfat";
* It's better to ignore such fs and continue. */
void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len)
{
- ssize_t buf_len;
+ uint8_t *dst;
+ unsigned small_off;
+ ssize_t read_len;
+
+ dbg("get buffer off 0x%llx(%llu), len 0x%zx",
+ (unsigned long long) off, (unsigned long long) off, len);
- dbg("get buffer off 0x%llx(%llu), len 0x%zx", (unsigned long long) off, (unsigned long long) off, len);
/* check if requested area fits in superblock buffer */
- if (off + len <= SB_BUFFER_SIZE) {
+ if (off + len <= SB_BUFFER_SIZE
+ /* && off <= SB_BUFFER_SIZE - want this paranoid overflow check? */
+ ) {
if (id->sbbuf == NULL) {
id->sbbuf = xmalloc(SB_BUFFER_SIZE);
}
+ small_off = off;
+ dst = id->sbbuf;
/* check if we need to read */
- if ((off + len) > id->sbbuf_len) {
- dbg("read sbbuf len:0x%llx", (unsigned long long) (off + len));
- if (lseek(id->fd, 0, SEEK_SET) != 0) {
- dbg("seek(0) failed");
- return NULL;
- }
- buf_len = full_read(id->fd, id->sbbuf, off + len);
- if (buf_len < 0) {
- 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;
- }
- }
+ len += off;
+ if (len <= id->sbbuf_len)
+ goto ret; /* we already have it */
- return &(id->sbbuf[off]);
+ dbg("read sbbuf len:0x%x", (unsigned) len);
+ id->sbbuf_len = len;
+ off = 0;
+ goto do_read;
}
if (len > SEEK_BUFFER_SIZE) {
dbg("seek buffer too small %d", SEEK_BUFFER_SIZE);
return NULL;
}
-
- /* get seek buffer */
- if (id->seekbuf == NULL) {
- id->seekbuf = xmalloc(SEEK_BUFFER_SIZE);
- }
+ dst = id->seekbuf;
/* check if we need to read */
- if ((off < id->seekbuf_off) || ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
- dbg("read seekbuf off:0x%llx len:0x%zx", (unsigned long long) off, len);
- if (lseek(id->fd, off, SEEK_SET) != off) {
- dbg("seek(0x%llx) failed", (unsigned long long) off);
- return NULL;
- }
- 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;
- }
+ if ((off >= id->seekbuf_off)
+ && ((off + len) <= (id->seekbuf_off + id->seekbuf_len))
+ ) {
+ small_off = off - id->seekbuf_off; /* can't overflow */
+ goto ret; /* we already have it */
}
- return &(id->seekbuf[off - id->seekbuf_off]);
+ id->seekbuf_off = off;
+ id->seekbuf_len = len;
+ id->seekbuf = xrealloc(id->seekbuf, len);
+ small_off = 0;
+ dst = id->seekbuf;
+ dbg("read seekbuf off:0x%llx len:0x%zx",
+ (unsigned long long) off, len);
+ do_read:
+ if (lseek(id->fd, off, SEEK_SET) != off) {
+ dbg("seek(0x%llx) failed", (unsigned long long) off);
+ goto err;
+ }
+ read_len = full_read(id->fd, dst, len);
+ if (read_len != len) {
+ dbg("requested 0x%x bytes, got 0x%x bytes",
+ (unsigned) len, (unsigned) read_len);
+ err:
+ /* id->seekbuf_len or id->sbbuf_len is wrong now! Fixing.
+ * Most likely user will not do any additional
+ * calls anyway, it's a corrupted fs or something. */
+ volume_id_free_buffer(id);
+ return NULL;
+ }
+ ret:
+ return dst + small_off;
}
void volume_id_free_buffer(struct volume_id *id)
free(id->seekbuf);
id->seekbuf = NULL;
id->seekbuf_len = 0;
+ id->seekbuf_off = 0; /* paranoia */
}