diff options
author | Jan Kara <jack@suse.cz> | 2007-06-16 10:16:14 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-06-16 13:16:16 -0700 |
commit | 74584ae509befc2ed711810e7df4b075473869b2 (patch) | |
tree | 9cb1b4a06b8583895c96d9b1a959d81df3950a06 /fs/udf/inode.c | |
parent | 4b356be019d0c28f67af02809df7072c1c8f7d32 (diff) | |
download | linux-74584ae509befc2ed711810e7df4b075473869b2.tar.gz linux-74584ae509befc2ed711810e7df4b075473869b2.tar.bz2 linux-74584ae509befc2ed711810e7df4b075473869b2.zip |
udf: fix possible leakage of blocks
We have to take care that when we call udf_discard_prealloc() from
udf_clear_inode() we have to write inode ourselves afterwards (otherwise,
some changes might be lost leading to leakage of blocks, use of free blocks
or improperly aligned extents).
Also udf_discard_prealloc() does two different things - it removes
preallocated blocks and truncates the last extent to exactly match i_size.
We move the latter functionality to udf_truncate_tail_extent(), call
udf_discard_prealloc() when last reference to a file is dropped and call
udf_truncate_tail_extent() when inode is being removed from inode cache
(udf_clear_inode() call).
We cannot call udf_truncate_tail_extent() earlier as subsequent open+write
would find the last block of the file mapped and happily write to the end
of it, although the last extent says it's shorter.
[akpm@linux-foundation.org: Make checkpatch.pl happier]
Signed-off-by: Jan Kara <jack@suse.cz>
Cc: Eric Sandeen <sandeen@sandeen.net>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/udf/inode.c')
-rw-r--r-- | fs/udf/inode.c | 11 |
1 files changed, 10 insertions, 1 deletions
diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 1f0129405cf4..bf7de0bdbab3 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -100,14 +100,23 @@ no_delete: clear_inode(inode); } +/* + * If we are going to release inode from memory, we discard preallocation and + * truncate last inode extent to proper length. We could use drop_inode() but + * it's called under inode_lock and thus we cannot mark inode dirty there. We + * use clear_inode() but we have to make sure to write inode as it's not written + * automatically. + */ void udf_clear_inode(struct inode *inode) { if (!(inode->i_sb->s_flags & MS_RDONLY)) { lock_kernel(); + /* Discard preallocation for directories, symlinks, etc. */ udf_discard_prealloc(inode); + udf_truncate_tail_extent(inode); unlock_kernel(); + write_inode_now(inode, 1); } - kfree(UDF_I_DATA(inode)); UDF_I_DATA(inode) = NULL; } |