fat: Greatly simplify and clean up dosfs_get_file_map().

There's no real need to distinguish between first/middle/last parts of
the file and much of the initialization can be done simpler.

This also checks for overflows when truncating the requested length. On
64 bit platforms this would always happen due to the kernel file map
code requesting (size_t)-1 (i.e. unlimited) extents. This lead to the
file end being reached when building the map from a position unequal 0,
which would happen for files fragmented enough to need more than the
default of 8 supplied vectors. An IO error was returned in that case,
rendering the file partially unreadable.
This commit is contained in:
Michael Lotz
2015-07-04 23:32:04 +02:00
parent 7e24b659c4
commit 17a45292ec
+51 -84
View File
@@ -1535,22 +1535,22 @@ dosfs_write_pages(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
status_t status_t
dosfs_get_file_map(fs_volume *_vol, fs_vnode *_node, off_t pos, size_t len, dosfs_get_file_map(fs_volume *_vol, fs_vnode *_node, off_t position,
struct file_io_vec *vecs, size_t *_count) size_t length, struct file_io_vec *vecs, size_t *_count)
{ {
nspace *vol = (nspace *)_vol->private_volume; nspace *vol = (nspace *)_vol->private_volume;
vnode *node = (vnode *)_node->private_node; vnode *node = (vnode *)_node->private_node;
struct csi iter; struct csi iter;
int result = B_OK; status_t result;
size_t bytes_read = 0; uint32 skipSectors;
uint32 cluster1; off_t offset;
off_t diff; size_t index = 0;
size_t index = 0, max = *_count; size_t max = *_count;
LOCK_VOL(vol); LOCK_VOL(vol);
*_count = 0; *_count = 0;
if (node->mode & FAT_SUBDIR) { if ((node->mode & FAT_SUBDIR) != 0) {
DPRINTF(0, ("dosfs_get_file_map called on subdirectory %" B_PRIdINO DPRINTF(0, ("dosfs_get_file_map called on subdirectory %" B_PRIdINO
"\n", node->vnid)); "\n", node->vnid));
UNLOCK_VOL(vol); UNLOCK_VOL(vol);
@@ -1558,84 +1558,71 @@ dosfs_get_file_map(fs_volume *_vol, fs_vnode *_node, off_t pos, size_t len,
} }
DPRINTF(0, ("dosfs_get_file_map called %" B_PRIuSIZE " bytes at %" B_PRIdOFF DPRINTF(0, ("dosfs_get_file_map called %" B_PRIuSIZE " bytes at %" B_PRIdOFF
" (vnode id %" B_PRIdINO ")\n", len, pos, node->vnid)); " (vnode id %" B_PRIdINO ")\n", length, position, node->vnid));
if (pos < 0) pos = 0; if (position < 0)
position = 0;
if ((node->st_size == 0) || (len == 0) || (pos >= node->st_size)) { if (node->st_size == 0 || length == 0 || position >= node->st_size) {
bytes_read = 0; result = B_OK;
goto bi; goto bi;
} }
// truncate bytes to read to file size // Truncate to file size, taking overflow into account.
if (pos + len >= node->st_size) if (position + length >= node->st_size || position + length < position)
len = node->st_size - pos; length = node->st_size - position;
/* the fat chain changed, so we have to start from the beginning */ result = init_csi(vol, node->cluster, 0, &iter);
cluster1 = node->cluster; if (result != B_OK) {
diff = pos;
diff /= vol->bytes_per_sector; /* convert to sectors */
if ((result = init_csi(vol, cluster1, 0, &iter)) != B_OK) {
dprintf("dosfs_get_file_map: invalid starting cluster (%" B_PRIu32 dprintf("dosfs_get_file_map: invalid starting cluster (%" B_PRIu32
")\n", cluster1); ")\n", node->cluster);
goto bi;
}
if (diff && ((result = iter_csi(&iter, diff)) != B_OK)) {
dprintf("dosfs_get_file_map: end of file reached (init)\n");
result = EIO; result = EIO;
goto bi; goto bi;
} }
ASSERT(iter.cluster == get_nth_fat_entry(vol, node->cluster, pos / vol->bytes_per_sector / vol->sectors_per_cluster)); skipSectors = position / vol->bytes_per_sector;
if (skipSectors > 0) {
if ((pos % vol->bytes_per_sector) != 0) { result = iter_csi(&iter, skipSectors);
// read in partial first sector if necessary if (result != B_OK) {
size_t amt = vol->bytes_per_sector - (pos % vol->bytes_per_sector); dprintf("dosfs_get_file_map: end of file reached (init)\n");
if (amt > len) result = EIO;
amt = len;
vecs[index].offset = csi_to_block(&iter) * vol->bytes_per_sector + (pos % vol->bytes_per_sector);
vecs[index].length = amt;
index++;
if (index >= max) {
// we're out of file_io_vecs; let's bail out
result = B_BUFFER_OVERFLOW;
goto bi; goto bi;
} }
}
bytes_read += amt; ASSERT(iter.cluster == get_nth_fat_entry(vol, node->cluster,
position / vol->bytes_per_sector / vol->sectors_per_cluster));
if (bytes_read < len) offset = position % vol->bytes_per_sector;
if ((result = iter_csi(&iter, 1)) != B_OK) { while (length > 0) {
off_t block = csi_to_block(&iter);
uint32 sectors = 1;
length -= min(length, vol->bytes_per_sector - offset);
while (length > 0) {
result = iter_csi(&iter, 1);
if (result != B_OK) {
dprintf("dosfs_get_file_map: end of file reached\n"); dprintf("dosfs_get_file_map: end of file reached\n");
result = EIO; result = EIO;
goto bi; goto bi;
} }
}
// read middle sectors if (block + sectors != csi_to_block(&iter)) {
while (bytes_read + vol->bytes_per_sector <= len) { // Disjoint sectors, need to flush and begin a new vector.
struct csi old_csi; break;
uint32 sectors = 1; }
off_t block = csi_to_block(&iter);
status_t err;
while (1) { length -= min(length, vol->bytes_per_sector);
old_csi = iter;
err = iter_csi(&iter, 1);
if ((len - bytes_read) < (sectors + 1) * iter.vol->bytes_per_sector)
break;
if ((err < B_OK) || (block + sectors != csi_to_block(&iter)))
break;
sectors++; sectors++;
} }
vecs[index].offset = block * vol->bytes_per_sector; vecs[index].offset = block * vol->bytes_per_sector + offset;
vecs[index].length = sectors * vol->bytes_per_sector; vecs[index].length = sectors * vol->bytes_per_sector - offset;
bytes_read += sectors * vol->bytes_per_sector;
index++; index++;
iter = old_csi;
if (length == 0)
break;
if (index >= max) { if (index >= max) {
// we're out of file_io_vecs; let's bail out // we're out of file_io_vecs; let's bail out
@@ -1643,27 +1630,7 @@ dosfs_get_file_map(fs_volume *_vol, fs_vnode *_node, off_t pos, size_t len,
goto bi; goto bi;
} }
if (bytes_read < len) offset = 0;
if ((result = iter_csi(&iter, 1)) != B_OK) {
dprintf("dosfs_get_file_map: end of file reached\n");
result = EIO;
goto bi;
}
}
// read part of remaining sector if needed
if (bytes_read < len) {
size_t amt = len - bytes_read;
vecs[index].offset = csi_to_block(&iter) * vol->bytes_per_sector;
vecs[index].length = amt;
index++;
bytes_read += amt;
if (index >= max) {
// we're out of file_io_vecs; let's bail out
result = B_BUFFER_OVERFLOW;
goto bi;
}
} }
result = B_OK; result = B_OK;