fat: Fix corruption of file data with removed directory entries.
Directory entries are cached using the block cache, so the cached blocks need to be discarded when directories are removed/shrunk. Otherwise freed directory blocks that were reused for file data would later be clobbered when the block cache was flushed. The forced cache sync for removable media hid the problem for these devices. It was easily triggered on non-removable media however.
This commit is contained in:
@@ -532,7 +532,7 @@ compact_directory(nspace *vol, vnode *dir)
|
||||
< dir->st_size) {
|
||||
DPRINTF(0, ("shrinking directory to %" B_PRIu32 " clusters\n",
|
||||
clusters));
|
||||
error = set_fat_chain_length(vol, dir, clusters);
|
||||
error = set_fat_chain_length(vol, dir, clusters, true);
|
||||
dir->st_size = clusters * vol->bytes_per_sector
|
||||
* vol->sectors_per_cluster;
|
||||
dir->iteration++;
|
||||
@@ -698,8 +698,11 @@ _create_dir_entry_(nspace *vol, vnode *dir, struct _entry_info_ *info,
|
||||
DPRINTF(0, ("expanding directory from %" B_PRIdOFF " to %" B_PRIu32
|
||||
" clusters\n", dir->st_size / vol->bytes_per_sector
|
||||
/ vol->sectors_per_cluster, clusters_needed));
|
||||
if ((error = set_fat_chain_length(vol, dir, clusters_needed)) < 0)
|
||||
if ((error = set_fat_chain_length(vol, dir, clusters_needed, false))
|
||||
< 0) {
|
||||
return error;
|
||||
}
|
||||
|
||||
dir->st_size = vol->bytes_per_sector*vol->sectors_per_cluster*clusters_needed;
|
||||
dir->iteration++;
|
||||
}
|
||||
|
||||
@@ -331,12 +331,12 @@ bi:
|
||||
DPRINTF(0, ("pooh. there is a problem. clearing chain (%" B_PRIu32
|
||||
")\n", first));
|
||||
if (first != 0)
|
||||
clear_fat_chain(vol, first);
|
||||
clear_fat_chain(vol, first, false);
|
||||
} else if (n != N) {
|
||||
DPRINTF(0, ("not enough free entries (%" B_PRId32 "/%" B_PRId32
|
||||
" found)\n", n, N));
|
||||
if (first != 0)
|
||||
clear_fat_chain(vol, first);
|
||||
clear_fat_chain(vol, first, false);
|
||||
result = B_DEVICE_FULL;
|
||||
} else if (result == 0) {
|
||||
vol->last_allocated = cluster;
|
||||
@@ -447,7 +447,7 @@ count_clusters(nspace *vol, int32 cluster)
|
||||
|
||||
|
||||
status_t
|
||||
clear_fat_chain(nspace *vol, uint32 cluster)
|
||||
clear_fat_chain(nspace *vol, uint32 cluster, bool discardBlockCache)
|
||||
{
|
||||
int32 c;
|
||||
status_t result;
|
||||
@@ -474,6 +474,12 @@ clear_fat_chain(nspace *vol, uint32 cluster)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (discardBlockCache) {
|
||||
block_cache_discard(vol->fBlockCache,
|
||||
vol->data_start + (cluster - 2) * vol->sectors_per_cluster,
|
||||
vol->sectors_per_cluster);
|
||||
}
|
||||
|
||||
vol->free_clusters++;
|
||||
cluster = c;
|
||||
DPRINTF(2, (", %" B_PRIu32, cluster));
|
||||
@@ -514,7 +520,8 @@ allocate_n_fat_entries(nspace *vol, int32 n, int32 *start)
|
||||
|
||||
|
||||
status_t
|
||||
set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters)
|
||||
set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters,
|
||||
bool discardBlockCache)
|
||||
{
|
||||
status_t result;
|
||||
int32 i, c, n;
|
||||
@@ -535,7 +542,7 @@ set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters)
|
||||
return B_OK;
|
||||
|
||||
c = node->cluster;
|
||||
if ((result = clear_fat_chain(vol, c)) != B_OK)
|
||||
if ((result = clear_fat_chain(vol, c, discardBlockCache)) != B_OK)
|
||||
return result;
|
||||
|
||||
node->cluster = 0;
|
||||
@@ -594,7 +601,7 @@ set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters)
|
||||
|
||||
result = set_fat_entry(vol, node->end_cluster, n);
|
||||
if (result < B_OK) {
|
||||
clear_fat_chain(vol, n);
|
||||
clear_fat_chain(vol, n, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -631,7 +638,7 @@ set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters)
|
||||
return result;
|
||||
|
||||
node->end_cluster = c;
|
||||
return clear_fat_chain(vol, n);
|
||||
return clear_fat_chain(vol, n, discardBlockCache);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,13 +20,15 @@ int32 get_nth_fat_entry(nspace *vol, int32 cluster, uint32 n);
|
||||
uint32 count_clusters(nspace *vol, int32 cluster);
|
||||
|
||||
/* remember to update vnode iteration after calling this function */
|
||||
status_t clear_fat_chain(nspace *vol, uint32 cluster);
|
||||
status_t clear_fat_chain(nspace *vol, uint32 cluster,
|
||||
bool discardBlockCache);
|
||||
|
||||
/* remember to set end of chain field when merging into a vnode */
|
||||
status_t allocate_n_fat_entries(nspace *vol, int32 n, int32 *start);
|
||||
|
||||
/* remember to update vnode iteration after calling this function */
|
||||
status_t set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters);
|
||||
status_t set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters,
|
||||
bool discardBlockCache);
|
||||
|
||||
void dump_fat_chain(nspace *vol, uint32 cluster);
|
||||
|
||||
|
||||
@@ -239,7 +239,8 @@ dosfs_wstat(fs_volume *_vol, fs_vnode *_node, const struct stat *st,
|
||||
/ vol->sectors_per_cluster;
|
||||
DPRINTF(0, ("setting fat chain length to %" B_PRIu32 " clusters\n",
|
||||
clusters));
|
||||
if ((err = set_fat_chain_length(vol, node, clusters)) == B_OK) {
|
||||
if ((err = set_fat_chain_length(vol, node, clusters, false))
|
||||
== B_OK) {
|
||||
node->st_size = st->st_size;
|
||||
node->iteration++;
|
||||
dirty = true;
|
||||
@@ -321,7 +322,7 @@ dosfs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **_cookie)
|
||||
|
||||
if (omode & O_TRUNC) {
|
||||
DPRINTF(0, ("dosfs_open called with O_TRUNC set\n"));
|
||||
if ((result = set_fat_chain_length(vol, node, 0)) != B_OK) {
|
||||
if ((result = set_fat_chain_length(vol, node, 0, false)) != B_OK) {
|
||||
dprintf("dosfs_open: error truncating file\n");
|
||||
goto error;
|
||||
}
|
||||
@@ -446,7 +447,8 @@ dosfs_write(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
|
||||
if (pos + *len > node->st_size) {
|
||||
uint32 clusters = (pos + *len + vol->bytes_per_sector*vol->sectors_per_cluster - 1) / vol->bytes_per_sector / vol->sectors_per_cluster;
|
||||
if (node->st_size <= (clusters - 1) * vol->sectors_per_cluster * vol->bytes_per_sector) {
|
||||
if ((result = set_fat_chain_length(vol, node, clusters)) != B_OK) {
|
||||
if ((result = set_fat_chain_length(vol, node, clusters, false))
|
||||
!= B_OK) {
|
||||
goto bi;
|
||||
}
|
||||
node->iteration++;
|
||||
@@ -588,7 +590,7 @@ dosfs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode,
|
||||
}
|
||||
|
||||
if (omode & O_TRUNC) {
|
||||
set_fat_chain_length(vol, file, 0);
|
||||
set_fat_chain_length(vol, file, 0, false);
|
||||
file->st_size = 0;
|
||||
file->iteration++;
|
||||
}
|
||||
@@ -807,7 +809,7 @@ bi3:
|
||||
if (IS_ARTIFICIAL_VNID(dummy.vnid))
|
||||
remove_from_vcache(vol, dummy.vnid);
|
||||
bi2:
|
||||
clear_fat_chain(vol, dummy.cluster);
|
||||
clear_fat_chain(vol, dummy.cluster, false);
|
||||
if (vol->fs_flags & FS_FLAGS_OP_SYNC)
|
||||
_dosfs_sync(vol);
|
||||
bi:
|
||||
@@ -1064,7 +1066,7 @@ dosfs_remove_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter)
|
||||
/* XXX: the following assertion was tripped */
|
||||
ASSERT((node->cluster != 0) || (node->st_size == 0));
|
||||
if (node->cluster != 0)
|
||||
clear_fat_chain(vol, node->cluster);
|
||||
clear_fat_chain(vol, node->cluster, (node->mode & FAT_SUBDIR) != 0);
|
||||
|
||||
/* remove vnode id from the cache */
|
||||
if (find_vnid_in_vcache(vol, node->vnid) == B_OK)
|
||||
|
||||
Reference in New Issue
Block a user