fuse: hold i_mutex in fuse_file_fallocate()
PATCH 1/1 fuse: hold i_mutex in fuse_file_fallocate()
Changing size of a file on server and local update (fuse_write_update_size) should be always protected by inode->i_mutex. Otherwise a race like this is possible:
1. Process 'A' calls fallocate(2) to extend file (~FALLOC_FL_KEEP_SIZE). fuse_file_fallocate() sends FUSE_FALLOCATE request to the server.
2. Process 'B' performs ordinary buffered write(2) with a length big enough to extend the file beyond "offset + length" of fallocate call.
3. Process 'A' resumes execution of fuse_file_fallocate() and calls fuse_write_update_size(inode, offset + length). But 'offset + length' was obsoleted by write from previous step.
--- fs/fuse/file.c | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-)
fs/fuse/file.c +6 -4
@@ -2470,14 +2470,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, .mode = mode };int err; +bool lock_inode = !(mode &FALLOC_FL_KEEP_SIZE ) || + (mode &FALLOC_FL_PUNCH_HOLE );if (fc->no_fallocate)return -EOPNOTSUPP ; -if (mode &FALLOC_FL_PUNCH_HOLE ) { +if (lock_inode)mutex_lock (&inode->i_mutex); +if (mode &FALLOC_FL_PUNCH_HOLE )fuse_set_nowrite (inode); - } req =fuse_get_req_nopages (fc);if (IS_ERR (req)) {
@@ -2511,10 +2513,10 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,fuse_invalidate_attr (inode); out: -if (mode &FALLOC_FL_PUNCH_HOLE ) { +if (mode &FALLOC_FL_PUNCH_HOLE )fuse_release_nowrite (inode); +if (lock_inode)mutex_unlock (&inode->i_mutex); - }return err; }
Quoted 4 lines · Maxim Patlasov
Changing size of a file on server and local update (
fuse_write_update_size) should be always protected byinode->i_mutex. Otherwise a race like this is possible:
Quoted 8 lines
1. Process 'A' calls fallocate(2) to extend file (~FALLOC_FL_KEEP_SIZE).
fuse_file_fallocate()sendsFUSE_FALLOCATErequest to the server.
2. Process 'B' performs ordinary buffered write(2) with a length big enough to extend the file beyond "offset + length" of fallocate call.
3. Process 'A' resumes execution offuse_file_fallocate()and callsfuse_write_update_size(inode, offset + length). But 'offset + length' was obsoleted by write from previous step.
Hi Maxim,
Doesn't fuse_write_update_size() already handle this particular case by only ever extending the size?
Brian
Quoted 42 lines
Signed-off-by Maxim V. Patlasov <[email protected]>--- fs/fuse/file.c | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-)fs/fuse/file.c +6
-4@@ -2470,14 +2470,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, .mode = mode };int err; +bool lock_inode = !(mode &FALLOC_FL_KEEP_SIZE ) || + (mode &FALLOC_FL_PUNCH_HOLE );if (fc->no_fallocate)return -EOPNOTSUPP ; -if (mode &FALLOC_FL_PUNCH_HOLE ) { +if (lock_inode)mutex_lock (&inode->i_mutex); +if (mode &FALLOC_FL_PUNCH_HOLE )fuse_set_nowrite (inode); - } req =fuse_get_req_nopages (fc);if (IS_ERR (req)) {@@ -2511,10 +2513,10 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,fuse_invalidate_attr (inode); out: -if (mode &FALLOC_FL_PUNCH_HOLE ) { +if (mode &FALLOC_FL_PUNCH_HOLE )fuse_release_nowrite (inode); +if (lock_inode)mutex_unlock (&inode->i_mutex); - }return err; }
Quoted 14 lines · Brian Foster
Quoted 4 lines · Maxim Patlasov
Changing size of a file on server and local update (
fuse_write_update_size) should be always protected byinode->i_mutex. Otherwise a race like this is possible:Quoted 8 lines
1. Process 'A' calls fallocate(2) to extend file (~FALLOC_FL_KEEP_SIZE).
fuse_file_fallocate()sendsFUSE_FALLOCATErequest to the server.
2. Process 'B' performs ordinary buffered write(2) with a length big enough to extend the file beyond "offset + length" of fallocate call.
3. Process 'A' resumes execution offuse_file_fallocate()and callsfuse_write_update_size(inode, offset + length). But 'offset + length' was obsoleted by write from previous step.
Quoted 5 lines
Hi Maxim,
Doesn't
fuse_write_update_size()already handle this particular case by only ever extending the size?
As you say, fuse_write_update_size() does seem to protect against the case Maxim writes in the commit log.
However, there is still an issue with with truncate(shrinking_offset) and fallocate(growing_offset,~FALLOC_FL_KEEP_SIZE) racing, and changing inode size in opposing order between file server and in core ->i_size. Therefore, grabbing i_mutex is making fallocate and truncate atomic against each other.
I guess we just need an updated commit log, and same code change?
Avati
fs/fuse/file.c static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
Quoted 7 lines · Maxim Patlasov
fs/fuse/file.c +2
-2-if (mode &FALLOC_FL_PUNCH_HOLE ) { +if (lock_inode)mutex_lock (&inode->i_mutex); +if (mode &FALLOC_FL_PUNCH_HOLE )fuse_set_nowrite (inode); - }
Just for clarity, can you make the condition to check FALLOC_FL_PUNCH_HOLE and call to fuse_set_nowrite() nested within the larger if (lock_inode) { .. } block? fuse_set_nowrite() should not be called without i_mutex acquired. The current style of calling mutex_lock() and fuse_set_nowrite() in separate conditions can potentially cause bugs in the future if they were to get re-ordered due to some unrelated patch. Nesting them makes the relation more explicit and clear.
Thanks,
Avati
Anand, Brian,
06/12/2013 11:04 PM, Anand Avati пишет:
Quoted 11 lines · Maxim Patlasov
fs/fuse/file.c static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
Quoted 8 lines
fs/fuse/file.c +2
-2-if (mode &FALLOC_FL_PUNCH_HOLE ) { +if (lock_inode)mutex_lock (&inode->i_mutex); +if (mode &FALLOC_FL_PUNCH_HOLE )fuse_set_nowrite (inode); - }Just for clarity, can you make the condition to check
FALLOC_FL_PUNCH_HOLEand call tofuse_set_nowrite()nested within the larger if (lock_inode) { .. } block?fuse_set_nowrite()should not be called withouti_mutexacquired. The current style of callingmutex_lock()andfuse_set_nowrite()in separate conditions can potentially cause bugs in the future if they were to get re-ordered due to some unrelated patch. Nesting them makes the relation more explicit and clear.
Thanks a lot for review. I'll post updated patch soon.
Thanks,
Maxim