From b241080102d9f26e8a6ec862ed7cd33450858059 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Thu, 14 May 2015 14:16:57 +0200 Subject: [PATCH] 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. --- src/add-ons/kernel/file_systems/fat/dir.c | 7 +++++-- src/add-ons/kernel/file_systems/fat/fat.c | 21 ++++++++++++++------- src/add-ons/kernel/file_systems/fat/fat.h | 6 ++++-- src/add-ons/kernel/file_systems/fat/file.c | 14 ++++++++------ 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/add-ons/kernel/file_systems/fat/dir.c b/src/add-ons/kernel/file_systems/fat/dir.c index 4a0b77cb32..4ea3190133 100644 --- a/src/add-ons/kernel/file_systems/fat/dir.c +++ b/src/add-ons/kernel/file_systems/fat/dir.c @@ -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++; } diff --git a/src/add-ons/kernel/file_systems/fat/fat.c b/src/add-ons/kernel/file_systems/fat/fat.c index cb48df30a9..6abc26688b 100644 --- a/src/add-ons/kernel/file_systems/fat/fat.c +++ b/src/add-ons/kernel/file_systems/fat/fat.c @@ -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); } diff --git a/src/add-ons/kernel/file_systems/fat/fat.h b/src/add-ons/kernel/file_systems/fat/fat.h index 4ffc0d0dbc..edbcb70785 100644 --- a/src/add-ons/kernel/file_systems/fat/fat.h +++ b/src/add-ons/kernel/file_systems/fat/fat.h @@ -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); diff --git a/src/add-ons/kernel/file_systems/fat/file.c b/src/add-ons/kernel/file_systems/fat/file.c index b304e370ca..b15377bdcb 100644 --- a/src/add-ons/kernel/file_systems/fat/file.c +++ b/src/add-ons/kernel/file_systems/fat/file.c @@ -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)