Commit f6075c79 authored by Russell King's avatar Russell King Committed by Al Viro

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: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent ae5df413
...@@ -64,12 +64,8 @@ int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src, ...@@ -64,12 +64,8 @@ int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
return 0; 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; dir->nr_buffers = 0;
if (dir->bhs != dir->bh) if (dir->bhs != dir->bh)
...@@ -78,6 +74,26 @@ void adfs_dir_relse(struct adfs_dir *dir) ...@@ -78,6 +74,26 @@ void adfs_dir_relse(struct adfs_dir *dir)
dir->sb = NULL; 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, int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
unsigned int size, struct adfs_dir *dir) unsigned int size, struct adfs_dir *dir)
{ {
...@@ -288,20 +304,28 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) ...@@ -288,20 +304,28 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
goto unlock; goto unlock;
ret = ops->update(&dir, obj); ret = ops->update(&dir, obj);
if (ret)
goto forget;
up_write(&adfs_dir_rwsem); up_write(&adfs_dir_rwsem);
if (ret == 0) adfs_dir_mark_dirty(&dir);
adfs_dir_mark_dirty(&dir);
if (wait) { if (wait)
int err = adfs_dir_sync(&dir); ret = adfs_dir_sync(&dir);
if (!ret)
ret = err;
}
adfs_dir_relse(&dir); adfs_dir_relse(&dir);
return ret; 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: unlock:
up_write(&adfs_dir_rwsem); up_write(&adfs_dir_rwsem);
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment