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:
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user