]>
Commit | Line | Data |
---|---|---|
c430f7d2 GKH |
1 | From 0ab08f576b9e6a6b689fc6b4e632079b978e619b Mon Sep 17 00:00:00 2001 |
2 | From: Maxim Patlasov <MPatlasov@parallels.com> | |
3 | Date: Fri, 13 Sep 2013 19:20:16 +0400 | |
4 | Subject: fuse: fix fallocate vs. ftruncate race | |
5 | ||
6 | From: Maxim Patlasov <MPatlasov@parallels.com> | |
7 | ||
8 | commit 0ab08f576b9e6a6b689fc6b4e632079b978e619b upstream. | |
9 | ||
10 | A former patch introducing FUSE_I_SIZE_UNSTABLE flag provided detailed | |
11 | description of races between ftruncate and anyone who can extend i_size: | |
12 | ||
13 | > 1. As in the previous scenario fuse_dentry_revalidate() discovered that i_size | |
14 | > changed (due to our own fuse_do_setattr()) and is going to call | |
15 | > truncate_pagecache() for some 'new_size' it believes valid right now. But by | |
16 | > the time that particular truncate_pagecache() is called ... | |
17 | > 2. fuse_do_setattr() returns (either having called truncate_pagecache() or | |
18 | > not -- it doesn't matter). | |
19 | > 3. The file is extended either by write(2) or ftruncate(2) or fallocate(2). | |
20 | > 4. mmap-ed write makes a page in the extended region dirty. | |
21 | ||
22 | This patch adds necessary bits to fuse_file_fallocate() to protect from that | |
23 | race. | |
24 | ||
25 | Signed-off-by: Maxim Patlasov <mpatlasov@parallels.com> | |
26 | Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> | |
27 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
28 | ||
29 | --- | |
30 | fs/fuse/file.c | 7 +++++++ | |
31 | 1 file changed, 7 insertions(+) | |
32 | ||
33 | --- a/fs/fuse/file.c | |
34 | +++ b/fs/fuse/file.c | |
35 | @@ -2468,6 +2468,7 @@ static long fuse_file_fallocate(struct f | |
36 | { | |
37 | struct fuse_file *ff = file->private_data; | |
38 | struct inode *inode = file->f_inode; | |
39 | + struct fuse_inode *fi = get_fuse_inode(inode); | |
40 | struct fuse_conn *fc = ff->fc; | |
41 | struct fuse_req *req; | |
42 | struct fuse_fallocate_in inarg = { | |
43 | @@ -2496,6 +2497,9 @@ static long fuse_file_fallocate(struct f | |
44 | } | |
45 | } | |
46 | ||
47 | + if (!(mode & FALLOC_FL_KEEP_SIZE)) | |
48 | + set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); | |
49 | + | |
50 | req = fuse_get_req_nopages(fc); | |
51 | if (IS_ERR(req)) { | |
52 | err = PTR_ERR(req); | |
53 | @@ -2528,6 +2532,9 @@ static long fuse_file_fallocate(struct f | |
54 | fuse_invalidate_attr(inode); | |
55 | ||
56 | out: | |
57 | + if (!(mode & FALLOC_FL_KEEP_SIZE)) | |
58 | + clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); | |
59 | + | |
60 | if (lock_inode) | |
61 | mutex_unlock(&inode->i_mutex); | |
62 |