Skip to content

Commit 15c83d2

Browse files
committed
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse bugfixes from Miklos Szeredi: "This contains two more fixes by Maxim for writeback/truncate races and fixes for RCU walk in fuse_dentry_revalidate()" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: no RCU mode in fuse_access() fuse: readdirplus: fix RCU walk fuse: don't check_submounts_and_drop() in RCU walk fuse: fix fallocate vs. ftruncate race fuse: wait for writeback in fuse_file_fallocate()
2 parents 8e1a254 + 698fa1d commit 15c83d2

File tree

3 files changed

+32
-13
lines changed

3 files changed

+32
-13
lines changed

fs/fuse/dir.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
182182
struct inode *inode;
183183
struct dentry *parent;
184184
struct fuse_conn *fc;
185+
struct fuse_inode *fi;
185186
int ret;
186187

187188
inode = ACCESS_ONCE(entry->d_inode);
@@ -228,7 +229,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
228229
if (!err && !outarg.nodeid)
229230
err = -ENOENT;
230231
if (!err) {
231-
struct fuse_inode *fi = get_fuse_inode(inode);
232+
fi = get_fuse_inode(inode);
232233
if (outarg.nodeid != get_node_id(inode)) {
233234
fuse_queue_forget(fc, forget, outarg.nodeid, 1);
234235
goto invalid;
@@ -246,8 +247,11 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
246247
attr_version);
247248
fuse_change_entry_timeout(entry, &outarg);
248249
} else if (inode) {
249-
fc = get_fuse_conn(inode);
250-
if (fc->readdirplus_auto) {
250+
fi = get_fuse_inode(inode);
251+
if (flags & LOOKUP_RCU) {
252+
if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
253+
return -ECHILD;
254+
} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
251255
parent = dget_parent(entry);
252256
fuse_advise_use_readdirplus(parent->d_inode);
253257
dput(parent);
@@ -259,7 +263,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
259263

260264
invalid:
261265
ret = 0;
262-
if (check_submounts_and_drop(entry) != 0)
266+
267+
if (!(flags & LOOKUP_RCU) && check_submounts_and_drop(entry) != 0)
263268
ret = 1;
264269
goto out;
265270
}
@@ -1063,6 +1068,8 @@ static int fuse_access(struct inode *inode, int mask)
10631068
struct fuse_access_in inarg;
10641069
int err;
10651070

1071+
BUG_ON(mask & MAY_NOT_BLOCK);
1072+
10661073
if (fc->no_access)
10671074
return 0;
10681075

@@ -1150,9 +1157,6 @@ static int fuse_permission(struct inode *inode, int mask)
11501157
noticed immediately, only after the attribute
11511158
timeout has expired */
11521159
} else if (mask & (MAY_ACCESS | MAY_CHDIR)) {
1153-
if (mask & MAY_NOT_BLOCK)
1154-
return -ECHILD;
1155-
11561160
err = fuse_access(inode, mask);
11571161
} else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
11581162
if (!(inode->i_mode & S_IXUGO)) {
@@ -1291,6 +1295,8 @@ static int fuse_direntplus_link(struct file *file,
12911295
}
12921296

12931297
found:
1298+
if (fc->readdirplus_auto)
1299+
set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
12941300
fuse_change_entry_timeout(dentry, o);
12951301

12961302
err = 0;

fs/fuse/file.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,6 +2467,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
24672467
{
24682468
struct fuse_file *ff = file->private_data;
24692469
struct inode *inode = file->f_inode;
2470+
struct fuse_inode *fi = get_fuse_inode(inode);
24702471
struct fuse_conn *fc = ff->fc;
24712472
struct fuse_req *req;
24722473
struct fuse_fallocate_in inarg = {
@@ -2484,10 +2485,20 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
24842485

24852486
if (lock_inode) {
24862487
mutex_lock(&inode->i_mutex);
2487-
if (mode & FALLOC_FL_PUNCH_HOLE)
2488-
fuse_set_nowrite(inode);
2488+
if (mode & FALLOC_FL_PUNCH_HOLE) {
2489+
loff_t endbyte = offset + length - 1;
2490+
err = filemap_write_and_wait_range(inode->i_mapping,
2491+
offset, endbyte);
2492+
if (err)
2493+
goto out;
2494+
2495+
fuse_sync_writes(inode);
2496+
}
24892497
}
24902498

2499+
if (!(mode & FALLOC_FL_KEEP_SIZE))
2500+
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
2501+
24912502
req = fuse_get_req_nopages(fc);
24922503
if (IS_ERR(req)) {
24932504
err = PTR_ERR(req);
@@ -2520,11 +2531,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
25202531
fuse_invalidate_attr(inode);
25212532

25222533
out:
2523-
if (lock_inode) {
2524-
if (mode & FALLOC_FL_PUNCH_HOLE)
2525-
fuse_release_nowrite(inode);
2534+
if (!(mode & FALLOC_FL_KEEP_SIZE))
2535+
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
2536+
2537+
if (lock_inode)
25262538
mutex_unlock(&inode->i_mutex);
2527-
}
25282539

25292540
return err;
25302541
}

fs/fuse/fuse_i.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ struct fuse_inode {
115115
enum {
116116
/** Advise readdirplus */
117117
FUSE_I_ADVISE_RDPLUS,
118+
/** Initialized with readdirplus */
119+
FUSE_I_INIT_RDPLUS,
118120
/** An operation changing file size is in progress */
119121
FUSE_I_SIZE_UNSTABLE,
120122
};

0 commit comments

Comments
 (0)