summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2019-12-09 11:10:01 +0000
committerAl Viro <viro@zeniv.linux.org.uk>2020-01-20 20:12:41 -0500
commitf6075c79074378910e131bbebc9d1dab53fd9986 (patch)
tree1940350c9ba18ed956b9c94db8e398edf5398777
parentae5df41390eb1c40b9a5c220673d8c31a4cb57db (diff)
fs/adfs: dir: improve update failure handling
When we update a directory, a number of errors may happen. If we failed to find the entry to update, we can just release the directory buffers as normal. However, if we have some other error, we may have partially updated the buffers, resulting in an invalid directory. In this case, we need to discard the buffers to avoid writing the contents back to the media, and later re-read the directory from the media. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/adfs/dir.c48
1 files changed, 36 insertions, 12 deletions
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
index ff9c921be31c..5e5d344bae7c 100644
--- a/fs/adfs/dir.c
+++ b/fs/adfs/dir.c
@@ -64,12 +64,8 @@ int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
return 0;
}
-void adfs_dir_relse(struct adfs_dir *dir)
+static void __adfs_dir_cleanup(struct adfs_dir *dir)
{
- unsigned int i;
-
- for (i = 0; i < dir->nr_buffers; i++)
- brelse(dir->bhs[i]);
dir->nr_buffers = 0;
if (dir->bhs != dir->bh)
@@ -78,6 +74,26 @@ void adfs_dir_relse(struct adfs_dir *dir)
dir->sb = NULL;
}
+void adfs_dir_relse(struct adfs_dir *dir)
+{
+ unsigned int i;
+
+ for (i = 0; i < dir->nr_buffers; i++)
+ brelse(dir->bhs[i]);
+
+ __adfs_dir_cleanup(dir);
+}
+
+static void adfs_dir_forget(struct adfs_dir *dir)
+{
+ unsigned int i;
+
+ for (i = 0; i < dir->nr_buffers; i++)
+ bforget(dir->bhs[i]);
+
+ __adfs_dir_cleanup(dir);
+}
+
int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
unsigned int size, struct adfs_dir *dir)
{
@@ -288,20 +304,28 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
goto unlock;
ret = ops->update(&dir, obj);
+ if (ret)
+ goto forget;
up_write(&adfs_dir_rwsem);
- if (ret == 0)
- adfs_dir_mark_dirty(&dir);
+ adfs_dir_mark_dirty(&dir);
- if (wait) {
- int err = adfs_dir_sync(&dir);
- if (!ret)
- ret = err;
- }
+ if (wait)
+ ret = adfs_dir_sync(&dir);
adfs_dir_relse(&dir);
return ret;
+ /*
+ * If the updated failed because the entry wasn't found, we can
+ * just release the buffers. If it was any other error, forget
+ * the dirtied buffers so they aren't written back to the media.
+ */
+forget:
+ if (ret == -ENOENT)
+ adfs_dir_relse(&dir);
+ else
+ adfs_dir_forget(&dir);
unlock:
up_write(&adfs_dir_rwsem);
#endif