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

fuse: fix fsync on directory

Commit ab2257e9941b ("fuse: reduce size of struct fuse_inode") moved parts
of fields related to writeback on regular file and to directory caching
into a union. However fuse_fsync_common() called from fuse_dir_fsync()
touches some writeback related fields, resulting in a crash.

Move writeback related parts from fuse_fsync_common() to fuse_fysnc().

Reported-by: Brett Girton <btgirton@gmail.com>
Tested-by: Brett Girton <btgirton@gmail.com>
Fixes: ab2257e9941b ("fuse: reduce size of struct fuse_inode")
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

+42 -23
+19 -1
fs/fuse/dir.c
··· 1249 1249 static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end, 1250 1250 int datasync) 1251 1251 { 1252 - return fuse_fsync_common(file, start, end, datasync, 1); 1252 + struct inode *inode = file->f_mapping->host; 1253 + struct fuse_conn *fc = get_fuse_conn(inode); 1254 + int err; 1255 + 1256 + if (is_bad_inode(inode)) 1257 + return -EIO; 1258 + 1259 + if (fc->no_fsyncdir) 1260 + return 0; 1261 + 1262 + inode_lock(inode); 1263 + err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNCDIR); 1264 + if (err == -ENOSYS) { 1265 + fc->no_fsyncdir = 1; 1266 + err = 0; 1267 + } 1268 + inode_unlock(inode); 1269 + 1270 + return err; 1253 1271 } 1254 1272 1255 1273 static long fuse_dir_ioctl(struct file *file, unsigned int cmd,
+22 -21
fs/fuse/file.c
··· 441 441 } 442 442 443 443 int fuse_fsync_common(struct file *file, loff_t start, loff_t end, 444 - int datasync, int isdir) 444 + int datasync, int opcode) 445 445 { 446 446 struct inode *inode = file->f_mapping->host; 447 447 struct fuse_conn *fc = get_fuse_conn(inode); 448 448 struct fuse_file *ff = file->private_data; 449 449 FUSE_ARGS(args); 450 450 struct fuse_fsync_in inarg; 451 + 452 + memset(&inarg, 0, sizeof(inarg)); 453 + inarg.fh = ff->fh; 454 + inarg.fsync_flags = datasync ? 1 : 0; 455 + args.in.h.opcode = opcode; 456 + args.in.h.nodeid = get_node_id(inode); 457 + args.in.numargs = 1; 458 + args.in.args[0].size = sizeof(inarg); 459 + args.in.args[0].value = &inarg; 460 + return fuse_simple_request(fc, &args); 461 + } 462 + 463 + static int fuse_fsync(struct file *file, loff_t start, loff_t end, 464 + int datasync) 465 + { 466 + struct inode *inode = file->f_mapping->host; 467 + struct fuse_conn *fc = get_fuse_conn(inode); 451 468 int err; 452 469 453 470 if (is_bad_inode(inode)) ··· 496 479 if (err) 497 480 goto out; 498 481 499 - if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) 482 + if (fc->no_fsync) 500 483 goto out; 501 484 502 - memset(&inarg, 0, sizeof(inarg)); 503 - inarg.fh = ff->fh; 504 - inarg.fsync_flags = datasync ? 1 : 0; 505 - args.in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC; 506 - args.in.h.nodeid = get_node_id(inode); 507 - args.in.numargs = 1; 508 - args.in.args[0].size = sizeof(inarg); 509 - args.in.args[0].value = &inarg; 510 - err = fuse_simple_request(fc, &args); 485 + err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNC); 511 486 if (err == -ENOSYS) { 512 - if (isdir) 513 - fc->no_fsyncdir = 1; 514 - else 515 - fc->no_fsync = 1; 487 + fc->no_fsync = 1; 516 488 err = 0; 517 489 } 518 490 out: 519 491 inode_unlock(inode); 520 - return err; 521 - } 522 492 523 - static int fuse_fsync(struct file *file, loff_t start, loff_t end, 524 - int datasync) 525 - { 526 - return fuse_fsync_common(file, start, end, datasync, 0); 493 + return err; 527 494 } 528 495 529 496 void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
+1 -1
fs/fuse/fuse_i.h
··· 828 828 * Send FSYNC or FSYNCDIR request 829 829 */ 830 830 int fuse_fsync_common(struct file *file, loff_t start, loff_t end, 831 - int datasync, int isdir); 831 + int datasync, int opcode); 832 832 833 833 /** 834 834 * Notify poll wakeup