Commit 793fe82e authored by David Howells's avatar David Howells

afs: Fix truncation issues and mmap writeback size

Fix the following issues:

 (1) Fix writeback to reduce the size of a store operation to i_size,
     effectively discarding the extra data.

     The problem comes when afs_page_mkwrite() records that a page is about
     to be modified by mmap().  It doesn't know what bits of the page are
     going to be modified, so it records the whole page as being dirty
     (this is stored in page->private as start and end offsets).

     Without this, the marshalling for the store to the server extends the
     size of the file to the end of the page (in afs_fs_store_data() and
     yfs_fs_store_data()).

 (2) Fix setattr to actually truncate the pagecache, thereby clearing
     the discarded part of a file.

 (3) Fix setattr to check that the new size is okay and to disable
     ATTR_SIZE if i_size wouldn't change.

 (4) Force i_size to be updated as the result of a truncate.

 (5) Don't truncate if ATTR_SIZE is not set.

 (6) Call pagecache_isize_extended() if the file was enlarged.

Note that truncate_set_size() isn't used because the setting of i_size is
done inside afs_vnode_commit_status() under the vnode->cb_lock.

Found with the generic/029 and generic/393 xfstests.

Fixes: 31143d5d ("AFS: implement basic file write support")
Fixes: 4343d008 ("afs: Get rid of the afs_writeback record")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent da8d0755
...@@ -169,7 +169,7 @@ static void afs_apply_status(struct afs_operation *op, ...@@ -169,7 +169,7 @@ static void afs_apply_status(struct afs_operation *op,
struct timespec64 t; struct timespec64 t;
umode_t mode; umode_t mode;
bool data_changed = false; bool data_changed = false;
bool change_size = false; bool change_size = vp->set_size;
_enter("{%llx:%llu.%u} %s", _enter("{%llx:%llu.%u} %s",
vp->fid.vid, vp->fid.vnode, vp->fid.unique, vp->fid.vid, vp->fid.vnode, vp->fid.unique,
...@@ -799,7 +799,15 @@ void afs_evict_inode(struct inode *inode) ...@@ -799,7 +799,15 @@ void afs_evict_inode(struct inode *inode)
static void afs_setattr_success(struct afs_operation *op) static void afs_setattr_success(struct afs_operation *op)
{ {
struct inode *inode = &op->file[0].vnode->vfs_inode;
afs_vnode_commit_status(op, &op->file[0]); afs_vnode_commit_status(op, &op->file[0]);
if (op->setattr.attr->ia_valid & ATTR_SIZE) {
loff_t i_size = inode->i_size, size = op->setattr.attr->ia_size;
if (size > i_size)
pagecache_isize_extended(inode, i_size, size);
truncate_pagecache(inode, size);
}
} }
static const struct afs_operation_ops afs_setattr_operation = { static const struct afs_operation_ops afs_setattr_operation = {
...@@ -815,6 +823,7 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -815,6 +823,7 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
{ {
struct afs_operation *op; struct afs_operation *op;
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
int ret;
_enter("{%llx:%llu},{n=%pd},%x", _enter("{%llx:%llu},{n=%pd},%x",
vnode->fid.vid, vnode->fid.vnode, dentry, vnode->fid.vid, vnode->fid.vnode, dentry,
...@@ -827,6 +836,18 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -827,6 +836,18 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
return 0; return 0;
} }
if (attr->ia_valid & ATTR_SIZE) {
if (!S_ISREG(vnode->vfs_inode.i_mode))
return -EISDIR;
ret = inode_newsize_ok(&vnode->vfs_inode, attr->ia_size);
if (ret)
return ret;
if (attr->ia_size == i_size_read(&vnode->vfs_inode))
attr->ia_valid &= ~ATTR_SIZE;
}
/* flush any dirty data outstanding on a regular file */ /* flush any dirty data outstanding on a regular file */
if (S_ISREG(vnode->vfs_inode.i_mode)) if (S_ISREG(vnode->vfs_inode.i_mode))
filemap_write_and_wait(vnode->vfs_inode.i_mapping); filemap_write_and_wait(vnode->vfs_inode.i_mapping);
...@@ -840,8 +861,10 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -840,8 +861,10 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
afs_op_set_vnode(op, 0, vnode); afs_op_set_vnode(op, 0, vnode);
op->setattr.attr = attr; op->setattr.attr = attr;
if (attr->ia_valid & ATTR_SIZE) if (attr->ia_valid & ATTR_SIZE) {
op->file[0].dv_delta = 1; op->file[0].dv_delta = 1;
op->file[0].set_size = true;
}
op->ctime = attr->ia_ctime; op->ctime = attr->ia_ctime;
op->file[0].update_ctime = 1; op->file[0].update_ctime = 1;
......
...@@ -744,9 +744,10 @@ struct afs_vnode_param { ...@@ -744,9 +744,10 @@ struct afs_vnode_param {
afs_dataversion_t dv_before; /* Data version before the call */ afs_dataversion_t dv_before; /* Data version before the call */
unsigned int cb_break_before; /* cb_break + cb_s_break before the call */ unsigned int cb_break_before; /* cb_break + cb_s_break before the call */
u8 dv_delta; /* Expected change in data version */ u8 dv_delta; /* Expected change in data version */
bool put_vnode; /* T if we have a ref on the vnode */ bool put_vnode:1; /* T if we have a ref on the vnode */
bool need_io_lock; /* T if we need the I/O lock on this */ bool need_io_lock:1; /* T if we need the I/O lock on this */
bool update_ctime; /* Need to update the ctime */ bool update_ctime:1; /* Need to update the ctime */
bool set_size:1; /* Must update i_size */
}; };
/* /*
......
...@@ -492,6 +492,7 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -492,6 +492,7 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
unsigned long count, priv; unsigned long count, priv;
unsigned n, offset, to, f, t; unsigned n, offset, to, f, t;
pgoff_t start, first, last; pgoff_t start, first, last;
loff_t i_size, end;
int loop, ret; int loop, ret;
_enter(",%lx", primary_page->index); _enter(",%lx", primary_page->index);
...@@ -592,7 +593,12 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -592,7 +593,12 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
first = primary_page->index; first = primary_page->index;
last = first + count - 1; last = first + count - 1;
end = (loff_t)last * PAGE_SIZE + to;
i_size = i_size_read(&vnode->vfs_inode);
_debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to); _debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to);
if (end > i_size)
to = i_size & ~PAGE_MASK;
ret = afs_store_data(mapping, first, last, offset, to); ret = afs_store_data(mapping, first, last, offset, to);
switch (ret) { switch (ret) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment