fuse: Trust kernel i_mtime only

Let the kernel maintain i_mtime locally:
 - clear S_NOCMTIME
 - implement i_op->update_time()
 - flush mtime on fsync and last close
 - update i_mtime explicitly on truncate and fallocate

Fuse inode flag FUSE_I_MTIME_DIRTY serves as indication that local i_mtime
should be flushed to the server eventually.

Signed-off-by: Maxim Patlasov <MPatlasov@parallels.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
This commit is contained in:
Maxim Patlasov
2013-12-26 19:51:11 +04:00
committed by Miklos Szeredi
parent 8373200b12
commit b0aa760652
4 changed files with 132 additions and 25 deletions

View File

@@ -308,6 +308,9 @@ static int fuse_open(struct inode *inode, struct file *file)
static int fuse_release(struct inode *inode, struct file *file)
{
if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state))
fuse_flush_mtime(file, true);
fuse_release_common(file, FUSE_RELEASE);
/* return value is ignored by VFS */
@@ -475,6 +478,12 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
fuse_sync_writes(inode);
if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) {
int err = fuse_flush_mtime(file, false);
if (err)
goto out;
}
req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -960,16 +969,21 @@ static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io,
return req->misc.write.out.size;
}
void fuse_write_update_size(struct inode *inode, loff_t pos)
bool fuse_write_update_size(struct inode *inode, loff_t pos)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
bool ret = false;
spin_lock(&fc->lock);
fi->attr_version = ++fc->attr_version;
if (pos > inode->i_size)
if (pos > inode->i_size) {
i_size_write(inode, pos);
ret = true;
}
spin_unlock(&fc->lock);
return ret;
}
static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
@@ -2877,8 +2891,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
goto out;
/* we could have extended the file */
if (!(mode & FALLOC_FL_KEEP_SIZE))
fuse_write_update_size(inode, offset + length);
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
bool changed = fuse_write_update_size(inode, offset + length);
if (changed && fc->writeback_cache) {
struct fuse_inode *fi = get_fuse_inode(inode);
inode->i_mtime = current_fs_time(inode->i_sb);
set_bit(FUSE_I_MTIME_DIRTY, &fi->state);
}
}
if (mode & FALLOC_FL_PUNCH_HOLE)
truncate_pagecache_range(inode, offset, offset + length - 1);