Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fuse: continue to send FUSE_RELEASEDIR when FUSE_OPEN returns ENOSYS

When FUSE_OPEN returns ENOSYS, the no_open bit is set on the connection.

Because the FUSE_RELEASE and FUSE_RELEASEDIR paths share code, this
incorrectly caused the FUSE_RELEASEDIR request to be dropped and never sent
to userspace.

Pass an isdir bool to distinguish between FUSE_RELEASE and FUSE_RELEASEDIR
inside of fuse_file_put.

Fixes: 7678ac50615d ("fuse: support clients that don't implement 'open'")
Cc: <stable@vger.kernel.org> # v3.14
Signed-off-by: Chad Austin <chadaustin@fb.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

authored by

Chad Austin and committed by
Miklos Szeredi
2e64ff15 d72f70da

+13 -12
+1 -1
fs/fuse/dir.c
··· 1243 1243 1244 1244 static int fuse_dir_release(struct inode *inode, struct file *file) 1245 1245 { 1246 - fuse_release_common(file, FUSE_RELEASEDIR); 1246 + fuse_release_common(file, true); 1247 1247 1248 1248 return 0; 1249 1249 }
+11 -10
fs/fuse/file.c
··· 89 89 iput(req->misc.release.inode); 90 90 } 91 91 92 - static void fuse_file_put(struct fuse_file *ff, bool sync) 92 + static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir) 93 93 { 94 94 if (refcount_dec_and_test(&ff->count)) { 95 95 struct fuse_req *req = ff->reserved_req; 96 96 97 - if (ff->fc->no_open) { 97 + if (ff->fc->no_open && !isdir) { 98 98 /* 99 99 * Drop the release request when client does not 100 100 * implement 'open' ··· 247 247 req->in.args[0].value = inarg; 248 248 } 249 249 250 - void fuse_release_common(struct file *file, int opcode) 250 + void fuse_release_common(struct file *file, bool isdir) 251 251 { 252 252 struct fuse_file *ff = file->private_data; 253 253 struct fuse_req *req = ff->reserved_req; 254 + int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; 254 255 255 256 fuse_prepare_release(ff, file->f_flags, opcode); 256 257 ··· 273 272 * synchronous RELEASE is allowed (and desirable) in this case 274 273 * because the server can be trusted not to screw up. 275 274 */ 276 - fuse_file_put(ff, ff->fc->destroy_req != NULL); 275 + fuse_file_put(ff, ff->fc->destroy_req != NULL, isdir); 277 276 } 278 277 279 278 static int fuse_open(struct inode *inode, struct file *file) ··· 289 288 if (fc->writeback_cache) 290 289 write_inode_now(inode, 1); 291 290 292 - fuse_release_common(file, FUSE_RELEASE); 291 + fuse_release_common(file, false); 293 292 294 293 /* return value is ignored by VFS */ 295 294 return 0; ··· 303 302 * iput(NULL) is a no-op and since the refcount is 1 and everything's 304 303 * synchronous, we are fine with not doing igrab() here" 305 304 */ 306 - fuse_file_put(ff, true); 305 + fuse_file_put(ff, true, false); 307 306 } 308 307 EXPORT_SYMBOL_GPL(fuse_sync_release); 309 308 ··· 809 808 put_page(page); 810 809 } 811 810 if (req->ff) 812 - fuse_file_put(req->ff, false); 811 + fuse_file_put(req->ff, false, false); 813 812 } 814 813 815 814 static void fuse_send_readpages(struct fuse_req *req, struct file *file) ··· 1462 1461 __free_page(req->pages[i]); 1463 1462 1464 1463 if (req->ff) 1465 - fuse_file_put(req->ff, false); 1464 + fuse_file_put(req->ff, false, false); 1466 1465 } 1467 1466 1468 1467 static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) ··· 1621 1620 ff = __fuse_write_file_get(fc, fi); 1622 1621 err = fuse_flush_times(inode, ff); 1623 1622 if (ff) 1624 - fuse_file_put(ff, 0); 1623 + fuse_file_put(ff, false, false); 1625 1624 1626 1625 return err; 1627 1626 } ··· 1942 1941 err = 0; 1943 1942 } 1944 1943 if (data.ff) 1945 - fuse_file_put(data.ff, false); 1944 + fuse_file_put(data.ff, false, false); 1946 1945 1947 1946 kfree(data.orig_pages); 1948 1947 out:
+1 -1
fs/fuse/fuse_i.h
··· 822 822 /** 823 823 * Send RELEASE or RELEASEDIR request 824 824 */ 825 - void fuse_release_common(struct file *file, int opcode); 825 + void fuse_release_common(struct file *file, bool isdir); 826 826 827 827 /** 828 828 * Send FSYNC or FSYNCDIR request