aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxim Patlasov <mpatlasov@parallels.com>2012-12-18 05:05:08 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2013-04-18 04:55:24 -0400
commitefb9fa9e911b23c7ea5330215bda778a7c69dba8 (patch)
tree7af69df4b3f3c5b89dc0e245a974fec358f23028
parent439ee5f0c5080d4fd15fda0c5bbee1fb3a57894e (diff)
fuse: truncate file if async dio failed
The patch improves error handling in fuse_direct_IO(): if we successfully submitted several fuse requests on behalf of synchronous direct write extending file and some of them failed, let's try to do our best to clean-up. Changed in v2: reuse fuse_do_setattr(). Thanks to Brian for suggestion. Signed-off-by: Maxim Patlasov <mpatlasov@parallels.com> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
-rw-r--r--fs/fuse/dir.c17
-rw-r--r--fs/fuse/file.c22
-rw-r--r--fs/fuse/fuse_i.h3
3 files changed, 32 insertions, 10 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index ff15522481d4..254df56b847b 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1562,10 +1562,9 @@ void fuse_release_nowrite(struct inode *inode)
1562 * vmtruncate() doesn't allow for this case, so do the rlimit checking 1562 * vmtruncate() doesn't allow for this case, so do the rlimit checking
1563 * and the actual truncation by hand. 1563 * and the actual truncation by hand.
1564 */ 1564 */
1565static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, 1565int fuse_do_setattr(struct inode *inode, struct iattr *attr,
1566 struct file *file) 1566 struct file *file)
1567{ 1567{
1568 struct inode *inode = entry->d_inode;
1569 struct fuse_conn *fc = get_fuse_conn(inode); 1568 struct fuse_conn *fc = get_fuse_conn(inode);
1570 struct fuse_req *req; 1569 struct fuse_req *req;
1571 struct fuse_setattr_in inarg; 1570 struct fuse_setattr_in inarg;
@@ -1574,9 +1573,6 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
1574 loff_t oldsize; 1573 loff_t oldsize;
1575 int err; 1574 int err;
1576 1575
1577 if (!fuse_allow_current_process(fc))
1578 return -EACCES;
1579
1580 if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) 1576 if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
1581 attr->ia_valid |= ATTR_FORCE; 1577 attr->ia_valid |= ATTR_FORCE;
1582 1578
@@ -1671,10 +1667,15 @@ error:
1671 1667
1672static int fuse_setattr(struct dentry *entry, struct iattr *attr) 1668static int fuse_setattr(struct dentry *entry, struct iattr *attr)
1673{ 1669{
1670 struct inode *inode = entry->d_inode;
1671
1672 if (!fuse_allow_current_process(get_fuse_conn(inode)))
1673 return -EACCES;
1674
1674 if (attr->ia_valid & ATTR_FILE) 1675 if (attr->ia_valid & ATTR_FILE)
1675 return fuse_do_setattr(entry, attr, attr->ia_file); 1676 return fuse_do_setattr(inode, attr, attr->ia_file);
1676 else 1677 else
1677 return fuse_do_setattr(entry, attr, NULL); 1678 return fuse_do_setattr(inode, attr, NULL);
1678} 1679}
1679 1680
1680static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, 1681static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 8f39f7b8cef2..1f8e3d60dc07 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2352,6 +2352,20 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
2352 return 0; 2352 return 0;
2353} 2353}
2354 2354
2355static void fuse_do_truncate(struct file *file)
2356{
2357 struct inode *inode = file->f_mapping->host;
2358 struct iattr attr;
2359
2360 attr.ia_valid = ATTR_SIZE;
2361 attr.ia_size = i_size_read(inode);
2362
2363 attr.ia_file = file;
2364 attr.ia_valid |= ATTR_FILE;
2365
2366 fuse_do_setattr(inode, &attr, file);
2367}
2368
2355static ssize_t 2369static ssize_t
2356fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, 2370fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
2357 loff_t offset, unsigned long nr_segs) 2371 loff_t offset, unsigned long nr_segs)
@@ -2419,8 +2433,12 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
2419 kfree(io); 2433 kfree(io);
2420 } 2434 }
2421 2435
2422 if (rw == WRITE && ret > 0) 2436 if (rw == WRITE) {
2423 fuse_write_update_size(inode, pos); 2437 if (ret > 0)
2438 fuse_write_update_size(inode, pos);
2439 else if (ret < 0 && offset + count > i_size)
2440 fuse_do_truncate(file);
2441 }
2424 2442
2425 return ret; 2443 return ret;
2426} 2444}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 337169a406c9..53b830e3b38f 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -863,4 +863,7 @@ int fuse_dev_release(struct inode *inode, struct file *file);
863 863
864void fuse_write_update_size(struct inode *inode, loff_t pos); 864void fuse_write_update_size(struct inode *inode, loff_t pos);
865 865
866int fuse_do_setattr(struct inode *inode, struct iattr *attr,
867 struct file *file);
868
866#endif /* _FS_FUSE_I_H */ 869#endif /* _FS_FUSE_I_H */