mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
AFS fixes
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEqG5UsNXhtOCrfGQP+7dXa6fLC2sFAl7pMu8ACgkQ+7dXa6fL C2sgNRAAnOCq281ojebwVSIkRDVGlxBODNeJtcgOOC4ib3jZM++vhdnnJgJIr8kc UOQ+LF4E5hNgwELubCrLOx/AjIzVuzfrreFNOPh3P3TSjyxW/7AU+tFGkdnLkYun NyadOXxI9Dk84UBN1LrmRm3ccAbF6nDf/KcPykS0oAEh12LVm6sDpVJz9+1uclnK Xq0rgl+zrR0+SPplPYz4P/OEPTgNfpLV9DHVYfkvsvEhwb/TaUmiLj9SEgndp+fg L3CT66QXoG9zds9hYFVODQM3devaXOpGNU0vsc9+Xg57BWuYvVed24eH5oBrcBQo F5kon+mcZlHtmTG87UJ6vFUwfHGeYqKKRb9XTbKbATtIWvkB3XM4Jz/XUlaAIE+R y0njNYEoIn4wHkleL/KeHmFPFSYG7pZpAN3wqhXZ9wVptXRDSB10OK3vpgLD/2rM V68FmBin6eStE5qZ8Mu9qMQxXb1buknoef37FIXUozjc+VMPrg5dbG6GjcW/CqIC LynaNUvrQOvF0ZFVzMt7ffZPrdDYlqqzyN0bReMdibys4BPKo24gSr5aVMLt7YXf ZaJeApcSdsphs4uUmtHKlHYgUQrSEl9pSGmc4hcq9bNIKHo9S618LG9uuUplOjdP j0L8N6uWBHQCjAvu6kDm8Wp5pRPPUnTgaXDsok7yP2GLRqBEm3Q= =bYOZ -----END PGP SIGNATURE----- Merge tag 'afs-fixes-20200616' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs Pull AFS fixes from David Howells: "I've managed to get xfstests kind of working with afs. Here are a set of patches that fix most of the bugs found. There are a number of primary issues: - Incorrect handling of mtime and non-handling of ctime. It might be argued, that the latter isn't a bug since the AFS protocol doesn't support ctime, but I should probably still update it locally. - Shared-write mmap, truncate and writeback bugs. This includes not changing i_size under the callback lock, overwriting local i_size with the reply from the server after a partial writeback, not limiting the writeback from an mmapped page to EOF. - Checks for an abort code indicating that the primary vnode in an operation was deleted by a third-party are done in the wrong place. - Silly rename bugs. This includes an incomplete conversion to the new operation handling, duplicate nlink handling, nlink changing not being done inside the callback lock and insufficient handling of third-party conflicting directory changes. And some secondary ones: - The UAEOVERFLOW abort code should map to EOVERFLOW not EREMOTEIO. - Remove a couple of unused or incompletely used bits. - Remove a couple of redundant success checks. These seem to fix all the data-corruption bugs found by ./check -afs -g quick along with the obvious silly rename bugs and time bugs. There are still some test failures, but they seem to fall into two classes: firstly, the authentication/security model is different to the standard UNIX model and permission is arbitrated by the server and cached locally; and secondly, there are a number of features that AFS does not support (such as mknod). But in these cases, the tests themselves need to be adapted or skipped. Using the in-kernel afs client with xfstests also found a bug in the AuriStor AFS server that has been fixed for a future release" * tag 'afs-fixes-20200616' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: afs: Fix silly rename afs: afs_vnode_commit_status() doesn't need to check the RPC error afs: Fix use of afs_check_for_remote_deletion() afs: Remove afs_operation::abort_code afs: Fix yfs_fs_fetch_status() to honour vnode selector afs: Remove yfs_fs_fetch_file_status() as it's not used afs: Fix the mapping of the UAEOVERFLOW abort code afs: Fix truncation issues and mmap writeback size afs: Concoct ctimes afs: Fix EOF corruption afs: afs_write_end() should change i_size under the right lock afs: Fix non-setting of mtime when writing into mmap
This commit is contained in:
commit
26c20ffcb5
62
fs/afs/dir.c
62
fs/afs/dir.c
@ -648,7 +648,7 @@ static void afs_do_lookup_success(struct afs_operation *op)
|
||||
vp = &op->file[0];
|
||||
abort_code = vp->scb.status.abort_code;
|
||||
if (abort_code != 0) {
|
||||
op->abort_code = abort_code;
|
||||
op->ac.abort_code = abort_code;
|
||||
op->error = afs_abort_to_error(abort_code);
|
||||
}
|
||||
break;
|
||||
@ -696,10 +696,11 @@ static const struct afs_operation_ops afs_inline_bulk_status_operation = {
|
||||
.success = afs_do_lookup_success,
|
||||
};
|
||||
|
||||
static const struct afs_operation_ops afs_fetch_status_operation = {
|
||||
static const struct afs_operation_ops afs_lookup_fetch_status_operation = {
|
||||
.issue_afs_rpc = afs_fs_fetch_status,
|
||||
.issue_yfs_rpc = yfs_fs_fetch_status,
|
||||
.success = afs_do_lookup_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1236,6 +1237,17 @@ void afs_d_release(struct dentry *dentry)
|
||||
_enter("%pd", dentry);
|
||||
}
|
||||
|
||||
void afs_check_for_remote_deletion(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
switch (op->ac.abort_code) {
|
||||
case VNOVNODE:
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
afs_break_callback(vnode, afs_cb_break_for_deleted);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new inode for create/mkdir/symlink
|
||||
*/
|
||||
@ -1268,7 +1280,7 @@ static void afs_vnode_new_inode(struct afs_operation *op)
|
||||
static void afs_create_success(struct afs_operation *op)
|
||||
{
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||
op->ctime = op->file[0].scb.status.mtime_client;
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
||||
afs_vnode_new_inode(op);
|
||||
@ -1302,6 +1314,7 @@ static const struct afs_operation_ops afs_mkdir_operation = {
|
||||
.issue_afs_rpc = afs_fs_make_dir,
|
||||
.issue_yfs_rpc = yfs_fs_make_dir,
|
||||
.success = afs_create_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.edit_dir = afs_create_edit_dir,
|
||||
.put = afs_create_put,
|
||||
};
|
||||
@ -1325,6 +1338,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
op->dentry = dentry;
|
||||
op->create.mode = S_IFDIR | mode;
|
||||
op->create.reason = afs_edit_dir_for_mkdir;
|
||||
@ -1350,7 +1364,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry)
|
||||
static void afs_rmdir_success(struct afs_operation *op)
|
||||
{
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||
op->ctime = op->file[0].scb.status.mtime_client;
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
||||
}
|
||||
@ -1382,6 +1396,7 @@ static const struct afs_operation_ops afs_rmdir_operation = {
|
||||
.issue_afs_rpc = afs_fs_remove_dir,
|
||||
.issue_yfs_rpc = yfs_fs_remove_dir,
|
||||
.success = afs_rmdir_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.edit_dir = afs_rmdir_edit_dir,
|
||||
.put = afs_rmdir_put,
|
||||
};
|
||||
@ -1404,6 +1419,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
|
||||
op->dentry = dentry;
|
||||
op->ops = &afs_rmdir_operation;
|
||||
@ -1479,7 +1495,8 @@ static void afs_dir_remove_link(struct afs_operation *op)
|
||||
static void afs_unlink_success(struct afs_operation *op)
|
||||
{
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||
op->ctime = op->file[0].scb.status.mtime_client;
|
||||
afs_check_dir_conflict(op, &op->file[0]);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
afs_vnode_commit_status(op, &op->file[1]);
|
||||
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
||||
@ -1511,6 +1528,7 @@ static const struct afs_operation_ops afs_unlink_operation = {
|
||||
.issue_afs_rpc = afs_fs_remove_file,
|
||||
.issue_yfs_rpc = yfs_fs_remove_file,
|
||||
.success = afs_unlink_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.edit_dir = afs_unlink_edit_dir,
|
||||
.put = afs_unlink_put,
|
||||
};
|
||||
@ -1537,6 +1555,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
|
||||
/* Try to make sure we have a callback promise on the victim. */
|
||||
ret = afs_validate(vnode, op->key);
|
||||
@ -1561,9 +1580,25 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
op->file[1].vnode = vnode;
|
||||
op->file[1].update_ctime = true;
|
||||
op->file[1].op_unlinked = true;
|
||||
op->dentry = dentry;
|
||||
op->ops = &afs_unlink_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
|
||||
/* If there was a conflict with a third party, check the status of the
|
||||
* unlinked vnode.
|
||||
*/
|
||||
if (op->error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) {
|
||||
op->file[1].update_ctime = false;
|
||||
op->fetch_status.which = 1;
|
||||
op->ops = &afs_fetch_status_operation;
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
}
|
||||
|
||||
return afs_put_operation(op);
|
||||
|
||||
error:
|
||||
return afs_put_operation(op);
|
||||
@ -1573,6 +1608,7 @@ static const struct afs_operation_ops afs_create_operation = {
|
||||
.issue_afs_rpc = afs_fs_create_file,
|
||||
.issue_yfs_rpc = yfs_fs_create_file,
|
||||
.success = afs_create_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.edit_dir = afs_create_edit_dir,
|
||||
.put = afs_create_put,
|
||||
};
|
||||
@ -1601,6 +1637,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
|
||||
op->dentry = dentry;
|
||||
op->create.mode = S_IFREG | mode;
|
||||
@ -1620,6 +1657,7 @@ static void afs_link_success(struct afs_operation *op)
|
||||
struct afs_vnode_param *vp = &op->file[1];
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
op->ctime = dvp->scb.status.mtime_client;
|
||||
afs_vnode_commit_status(op, dvp);
|
||||
afs_vnode_commit_status(op, vp);
|
||||
afs_update_dentry_version(op, dvp, op->dentry);
|
||||
@ -1640,6 +1678,7 @@ static const struct afs_operation_ops afs_link_operation = {
|
||||
.issue_afs_rpc = afs_fs_link,
|
||||
.issue_yfs_rpc = yfs_fs_link,
|
||||
.success = afs_link_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.edit_dir = afs_create_edit_dir,
|
||||
.put = afs_link_put,
|
||||
};
|
||||
@ -1672,6 +1711,8 @@ static int afs_link(struct dentry *from, struct inode *dir,
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
afs_op_set_vnode(op, 1, vnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
op->file[1].update_ctime = true;
|
||||
|
||||
op->dentry = dentry;
|
||||
op->dentry_2 = from;
|
||||
@ -1689,6 +1730,7 @@ static const struct afs_operation_ops afs_symlink_operation = {
|
||||
.issue_afs_rpc = afs_fs_symlink,
|
||||
.issue_yfs_rpc = yfs_fs_symlink,
|
||||
.success = afs_create_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.edit_dir = afs_create_edit_dir,
|
||||
.put = afs_create_put,
|
||||
};
|
||||
@ -1740,9 +1782,13 @@ static void afs_rename_success(struct afs_operation *op)
|
||||
{
|
||||
_enter("op=%08x", op->debug_id);
|
||||
|
||||
op->ctime = op->file[0].scb.status.mtime_client;
|
||||
afs_check_dir_conflict(op, &op->file[1]);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
if (op->file[1].vnode != op->file[0].vnode)
|
||||
if (op->file[1].vnode != op->file[0].vnode) {
|
||||
op->ctime = op->file[1].scb.status.mtime_client;
|
||||
afs_vnode_commit_status(op, &op->file[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void afs_rename_edit_dir(struct afs_operation *op)
|
||||
@ -1860,6 +1906,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
afs_op_set_vnode(op, 1, new_dvnode); /* May be same as orig_dvnode */
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[1].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
op->file[1].update_ctime = true;
|
||||
|
||||
op->dentry = old_dentry;
|
||||
op->dentry_2 = new_dentry;
|
||||
|
@ -16,6 +16,7 @@ static void afs_silly_rename_success(struct afs_operation *op)
|
||||
{
|
||||
_enter("op=%08x", op->debug_id);
|
||||
|
||||
afs_check_dir_conflict(op, &op->file[0]);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
}
|
||||
|
||||
@ -69,6 +70,11 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
|
||||
return PTR_ERR(op);
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
afs_op_set_vnode(op, 1, dvnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[1].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
op->file[1].update_ctime = true;
|
||||
|
||||
op->dentry = old;
|
||||
op->dentry_2 = new;
|
||||
@ -129,6 +135,7 @@ int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
|
||||
switch (ret) {
|
||||
case 0:
|
||||
/* The rename succeeded. */
|
||||
set_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags);
|
||||
d_move(dentry, sdentry);
|
||||
break;
|
||||
case -ERESTARTSYS:
|
||||
@ -148,19 +155,11 @@ out:
|
||||
|
||||
static void afs_silly_unlink_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[1].vnode;
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||
afs_check_dir_conflict(op, &op->file[0]);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
afs_vnode_commit_status(op, &op->file[1]);
|
||||
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
||||
|
||||
drop_nlink(&vnode->vfs_inode);
|
||||
if (vnode->vfs_inode.i_nlink == 0) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void afs_silly_unlink_edit_dir(struct afs_operation *op)
|
||||
@ -181,6 +180,7 @@ static const struct afs_operation_ops afs_silly_unlink_operation = {
|
||||
.issue_afs_rpc = afs_fs_remove_file,
|
||||
.issue_yfs_rpc = yfs_fs_remove_file,
|
||||
.success = afs_silly_unlink_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.edit_dir = afs_silly_unlink_edit_dir,
|
||||
};
|
||||
|
||||
@ -200,12 +200,30 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
afs_op_set_vnode(op, 1, vnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->file[0].update_ctime = true;
|
||||
op->file[1].op_unlinked = true;
|
||||
op->file[1].update_ctime = true;
|
||||
|
||||
op->dentry = dentry;
|
||||
op->ops = &afs_silly_unlink_operation;
|
||||
|
||||
trace_afs_silly_rename(vnode, true);
|
||||
return afs_do_sync_operation(op);
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
|
||||
/* If there was a conflict with a third party, check the status of the
|
||||
* unlinked vnode.
|
||||
*/
|
||||
if (op->error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) {
|
||||
op->file[1].update_ctime = false;
|
||||
op->fetch_status.which = 1;
|
||||
op->ops = &afs_fetch_status_operation;
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
}
|
||||
|
||||
return afs_put_operation(op);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -225,7 +225,6 @@ static void afs_fetch_data_success(struct afs_operation *op)
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, vnode);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
afs_stat_v(vnode, n_fetches);
|
||||
atomic_long_add(op->fetch.req->actual_len, &op->net->n_fetch_bytes);
|
||||
@ -240,6 +239,7 @@ static const struct afs_operation_ops afs_fetch_data_operation = {
|
||||
.issue_afs_rpc = afs_fs_fetch_data,
|
||||
.issue_yfs_rpc = yfs_fs_fetch_data,
|
||||
.success = afs_fetch_data_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.put = afs_fetch_data_put,
|
||||
};
|
||||
|
||||
|
@ -175,10 +175,7 @@ static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
|
||||
|
||||
static void afs_lock_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, vnode);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
}
|
||||
|
||||
@ -186,6 +183,7 @@ static const struct afs_operation_ops afs_set_lock_operation = {
|
||||
.issue_afs_rpc = afs_fs_set_lock,
|
||||
.issue_yfs_rpc = yfs_fs_set_lock,
|
||||
.success = afs_lock_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -187,9 +187,17 @@ void afs_wait_for_operation(struct afs_operation *op)
|
||||
op->error = afs_wait_for_call_to_complete(op->call, &op->ac);
|
||||
}
|
||||
|
||||
if (op->error == 0) {
|
||||
switch (op->error) {
|
||||
case 0:
|
||||
_debug("success");
|
||||
op->ops->success(op);
|
||||
break;
|
||||
case -ECONNABORTED:
|
||||
if (op->ops->aborted)
|
||||
op->ops->aborted(op);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
afs_end_vnode_operation(op);
|
||||
|
@ -165,9 +165,11 @@ static void afs_apply_status(struct afs_operation *op,
|
||||
{
|
||||
struct afs_file_status *status = &vp->scb.status;
|
||||
struct afs_vnode *vnode = vp->vnode;
|
||||
struct inode *inode = &vnode->vfs_inode;
|
||||
struct timespec64 t;
|
||||
umode_t mode;
|
||||
bool data_changed = false;
|
||||
bool change_size = vp->set_size;
|
||||
|
||||
_enter("{%llx:%llu.%u} %s",
|
||||
vp->fid.vid, vp->fid.vnode, vp->fid.unique,
|
||||
@ -186,25 +188,25 @@ static void afs_apply_status(struct afs_operation *op,
|
||||
}
|
||||
|
||||
if (status->nlink != vnode->status.nlink)
|
||||
set_nlink(&vnode->vfs_inode, status->nlink);
|
||||
set_nlink(inode, status->nlink);
|
||||
|
||||
if (status->owner != vnode->status.owner)
|
||||
vnode->vfs_inode.i_uid = make_kuid(&init_user_ns, status->owner);
|
||||
inode->i_uid = make_kuid(&init_user_ns, status->owner);
|
||||
|
||||
if (status->group != vnode->status.group)
|
||||
vnode->vfs_inode.i_gid = make_kgid(&init_user_ns, status->group);
|
||||
inode->i_gid = make_kgid(&init_user_ns, status->group);
|
||||
|
||||
if (status->mode != vnode->status.mode) {
|
||||
mode = vnode->vfs_inode.i_mode;
|
||||
mode = inode->i_mode;
|
||||
mode &= ~S_IALLUGO;
|
||||
mode |= status->mode;
|
||||
WRITE_ONCE(vnode->vfs_inode.i_mode, mode);
|
||||
WRITE_ONCE(inode->i_mode, mode);
|
||||
}
|
||||
|
||||
t = status->mtime_client;
|
||||
vnode->vfs_inode.i_ctime = t;
|
||||
vnode->vfs_inode.i_mtime = t;
|
||||
vnode->vfs_inode.i_atime = t;
|
||||
inode->i_mtime = t;
|
||||
if (vp->update_ctime)
|
||||
inode->i_ctime = op->ctime;
|
||||
|
||||
if (vnode->status.data_version != status->data_version)
|
||||
data_changed = true;
|
||||
@ -226,6 +228,7 @@ static void afs_apply_status(struct afs_operation *op,
|
||||
} else {
|
||||
set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
|
||||
}
|
||||
change_size = true;
|
||||
} else if (vnode->status.type == AFS_FTYPE_DIR) {
|
||||
/* Expected directory change is handled elsewhere so
|
||||
* that we can locally edit the directory and save on a
|
||||
@ -233,11 +236,22 @@ static void afs_apply_status(struct afs_operation *op,
|
||||
*/
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
|
||||
data_changed = false;
|
||||
change_size = true;
|
||||
}
|
||||
|
||||
if (data_changed) {
|
||||
inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
|
||||
afs_set_i_size(vnode, status->size);
|
||||
inode_set_iversion_raw(inode, status->data_version);
|
||||
|
||||
/* Only update the size if the data version jumped. If the
|
||||
* file is being modified locally, then we might have our own
|
||||
* idea of what the size should be that's not the same as
|
||||
* what's on the server.
|
||||
*/
|
||||
if (change_size) {
|
||||
afs_set_i_size(vnode, status->size);
|
||||
inode->i_ctime = t;
|
||||
inode->i_atime = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,32 +281,39 @@ void afs_vnode_commit_status(struct afs_operation *op, struct afs_vnode_param *v
|
||||
|
||||
_enter("");
|
||||
|
||||
ASSERTCMP(op->error, ==, 0);
|
||||
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
if (vp->scb.have_error) {
|
||||
/* A YFS server will return this from RemoveFile2 and AFS and
|
||||
* YFS will return this from InlineBulkStatus.
|
||||
*/
|
||||
if (vp->scb.status.abort_code == VNOVNODE) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
clear_nlink(&vnode->vfs_inode);
|
||||
__afs_break_callback(vnode, afs_cb_break_for_deleted);
|
||||
op->flags &= ~AFS_OPERATION_DIR_CONFLICT;
|
||||
}
|
||||
} else {
|
||||
if (vp->scb.have_status)
|
||||
afs_apply_status(op, vp);
|
||||
} else if (vp->scb.have_status) {
|
||||
afs_apply_status(op, vp);
|
||||
if (vp->scb.have_cb)
|
||||
afs_apply_callback(op, vp);
|
||||
} else if (vp->op_unlinked && !(op->flags & AFS_OPERATION_DIR_CONFLICT)) {
|
||||
drop_nlink(&vnode->vfs_inode);
|
||||
if (vnode->vfs_inode.i_nlink == 0) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
__afs_break_callback(vnode, afs_cb_break_for_deleted);
|
||||
}
|
||||
}
|
||||
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
|
||||
if (op->error == 0 && vp->scb.have_status)
|
||||
if (vp->scb.have_status)
|
||||
afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb);
|
||||
}
|
||||
|
||||
static void afs_fetch_status_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode_param *vp = &op->file[0];
|
||||
struct afs_vnode_param *vp = &op->file[op->fetch_status.which];
|
||||
struct afs_vnode *vnode = vp->vnode;
|
||||
int ret;
|
||||
|
||||
@ -306,10 +327,11 @@ static void afs_fetch_status_success(struct afs_operation *op)
|
||||
}
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_fetch_status_operation = {
|
||||
const struct afs_operation_ops afs_fetch_status_operation = {
|
||||
.issue_afs_rpc = afs_fs_fetch_status,
|
||||
.issue_yfs_rpc = yfs_fs_fetch_status,
|
||||
.success = afs_fetch_status_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -716,6 +738,9 @@ int afs_getattr(const struct path *path, struct kstat *stat,
|
||||
do {
|
||||
read_seqbegin_or_lock(&vnode->cb_lock, &seq);
|
||||
generic_fillattr(inode, stat);
|
||||
if (test_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags) &&
|
||||
stat->nlink > 0)
|
||||
stat->nlink -= 1;
|
||||
} while (need_seqretry(&vnode->cb_lock, seq));
|
||||
|
||||
done_seqretry(&vnode->cb_lock, seq);
|
||||
@ -785,7 +810,15 @@ void afs_evict_inode(struct inode *inode)
|
||||
|
||||
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]);
|
||||
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 = {
|
||||
@ -801,17 +834,31 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
{
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
|
||||
int ret;
|
||||
|
||||
_enter("{%llx:%llu},{n=%pd},%x",
|
||||
vnode->fid.vid, vnode->fid.vnode, dentry,
|
||||
attr->ia_valid);
|
||||
|
||||
if (!(attr->ia_valid & (ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID |
|
||||
ATTR_MTIME))) {
|
||||
ATTR_MTIME | ATTR_MTIME_SET | ATTR_TIMES_SET |
|
||||
ATTR_TOUCH))) {
|
||||
_leave(" = 0 [unsupported]");
|
||||
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 */
|
||||
if (S_ISREG(vnode->vfs_inode.i_mode))
|
||||
filemap_write_and_wait(vnode->vfs_inode.i_mapping);
|
||||
@ -825,8 +872,12 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
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].set_size = true;
|
||||
}
|
||||
op->ctime = attr->ia_ctime;
|
||||
op->file[0].update_ctime = 1;
|
||||
|
||||
op->ops = &afs_setattr_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
|
@ -634,6 +634,7 @@ struct afs_vnode {
|
||||
#define AFS_VNODE_AUTOCELL 6 /* set if Vnode is an auto mount point */
|
||||
#define AFS_VNODE_PSEUDODIR 7 /* set if Vnode is a pseudo directory */
|
||||
#define AFS_VNODE_NEW_CONTENT 8 /* Set if file has new content (create/trunc-0) */
|
||||
#define AFS_VNODE_SILLY_DELETED 9 /* Set if file has been silly-deleted */
|
||||
|
||||
struct list_head wb_keys; /* List of keys available for writeback */
|
||||
struct list_head pending_locks; /* locks waiting to be granted */
|
||||
@ -744,8 +745,11 @@ struct afs_vnode_param {
|
||||
afs_dataversion_t dv_before; /* Data version before the call */
|
||||
unsigned int cb_break_before; /* cb_break + cb_s_break before the call */
|
||||
u8 dv_delta; /* Expected change in data version */
|
||||
bool put_vnode; /* T if we have a ref on the vnode */
|
||||
bool need_io_lock; /* T if we need the I/O lock on this */
|
||||
bool put_vnode:1; /* T if we have a ref on the vnode */
|
||||
bool need_io_lock:1; /* T if we need the I/O lock on this */
|
||||
bool update_ctime:1; /* Need to update the ctime */
|
||||
bool set_size:1; /* Must update i_size */
|
||||
bool op_unlinked:1; /* True if file was unlinked by op */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -766,9 +770,9 @@ struct afs_operation {
|
||||
struct dentry *dentry; /* Dentry to be altered */
|
||||
struct dentry *dentry_2; /* Second dentry to be altered */
|
||||
struct timespec64 mtime; /* Modification time to record */
|
||||
struct timespec64 ctime; /* Change time to set */
|
||||
short nr_files; /* Number of entries in file[], more_files */
|
||||
short error;
|
||||
unsigned int abort_code;
|
||||
unsigned int debug_id;
|
||||
|
||||
unsigned int cb_v_break; /* Volume break counter before op */
|
||||
@ -837,6 +841,7 @@ struct afs_operation {
|
||||
#define AFS_OPERATION_LOCK_1 0x0200 /* Set if have io_lock on file[1] */
|
||||
#define AFS_OPERATION_TRIED_ALL 0x0400 /* Set if we've tried all the fileservers */
|
||||
#define AFS_OPERATION_RETRY_SERVER 0x0800 /* Set if we should retry the current server */
|
||||
#define AFS_OPERATION_DIR_CONFLICT 0x1000 /* Set if we detected a 3rd-party dir change */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -932,6 +937,7 @@ extern const struct address_space_operations afs_dir_aops;
|
||||
extern const struct dentry_operations afs_fs_dentry_operations;
|
||||
|
||||
extern void afs_d_release(struct dentry *);
|
||||
extern void afs_check_for_remote_deletion(struct afs_operation *);
|
||||
|
||||
/*
|
||||
* dir_edit.c
|
||||
@ -1063,6 +1069,8 @@ extern int afs_wait_for_one_fs_probe(struct afs_server *, bool);
|
||||
/*
|
||||
* inode.c
|
||||
*/
|
||||
extern const struct afs_operation_ops afs_fetch_status_operation;
|
||||
|
||||
extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *);
|
||||
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *);
|
||||
extern int afs_ilookup5_test_by_fid(struct inode *, void *);
|
||||
@ -1435,7 +1443,6 @@ extern ssize_t afs_listxattr(struct dentry *, char *, size_t);
|
||||
/*
|
||||
* yfsclient.c
|
||||
*/
|
||||
extern void yfs_fs_fetch_file_status(struct afs_operation *);
|
||||
extern void yfs_fs_fetch_data(struct afs_operation *);
|
||||
extern void yfs_fs_create_file(struct afs_operation *);
|
||||
extern void yfs_fs_make_dir(struct afs_operation *);
|
||||
@ -1481,15 +1488,6 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
|
||||
return &vnode->vfs_inode;
|
||||
}
|
||||
|
||||
static inline void afs_check_for_remote_deletion(struct afs_operation *op,
|
||||
struct afs_vnode *vnode)
|
||||
{
|
||||
if (op->error == -ENOENT) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
afs_break_callback(vnode, afs_cb_break_for_deleted);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that a dentry got changed. We need to set d_fsdata to the data version
|
||||
* number derived from the result of the operation. It doesn't matter if
|
||||
@ -1504,6 +1502,18 @@ static inline void afs_update_dentry_version(struct afs_operation *op,
|
||||
(void *)(unsigned long)dir_vp->scb.status.data_version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for a conflicting operation on a directory that we just unlinked from.
|
||||
* If someone managed to sneak a link or an unlink in on the file we just
|
||||
* unlinked, we won't be able to trust nlink on an AFS file (but not YFS).
|
||||
*/
|
||||
static inline void afs_check_dir_conflict(struct afs_operation *op,
|
||||
struct afs_vnode_param *dvp)
|
||||
{
|
||||
if (dvp->dv_before + dvp->dv_delta != dvp->scb.status.data_version)
|
||||
op->flags |= AFS_OPERATION_DIR_CONFLICT;
|
||||
}
|
||||
|
||||
static inline int afs_io_error(struct afs_call *call, enum afs_io_error where)
|
||||
{
|
||||
trace_afs_io_error(call->debug_id, -EIO, where);
|
||||
|
@ -83,6 +83,7 @@ int afs_abort_to_error(u32 abort_code)
|
||||
case UAENOLCK: return -ENOLCK;
|
||||
case UAENOTEMPTY: return -ENOTEMPTY;
|
||||
case UAELOOP: return -ELOOP;
|
||||
case UAEOVERFLOW: return -EOVERFLOW;
|
||||
case UAENOMEDIUM: return -ENOMEDIUM;
|
||||
case UAEDQUOT: return -EDQUOT;
|
||||
|
||||
|
@ -194,11 +194,11 @@ int afs_write_end(struct file *file, struct address_space *mapping,
|
||||
|
||||
i_size = i_size_read(&vnode->vfs_inode);
|
||||
if (maybe_i_size > i_size) {
|
||||
spin_lock(&vnode->wb_lock);
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
i_size = i_size_read(&vnode->vfs_inode);
|
||||
if (maybe_i_size > i_size)
|
||||
i_size_write(&vnode->vfs_inode, maybe_i_size);
|
||||
spin_unlock(&vnode->wb_lock);
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
}
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
@ -393,6 +393,7 @@ static void afs_store_data_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
op->ctime = op->file[0].scb.status.mtime_client;
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
if (op->error == 0) {
|
||||
afs_pages_written_back(vnode, op->store.first, op->store.last);
|
||||
@ -491,6 +492,7 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
|
||||
unsigned long count, priv;
|
||||
unsigned n, offset, to, f, t;
|
||||
pgoff_t start, first, last;
|
||||
loff_t i_size, end;
|
||||
int loop, ret;
|
||||
|
||||
_enter(",%lx", primary_page->index);
|
||||
@ -591,7 +593,12 @@ no_more:
|
||||
first = primary_page->index;
|
||||
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);
|
||||
if (end > i_size)
|
||||
to = i_size & ~PAGE_MASK;
|
||||
|
||||
ret = afs_store_data(mapping, first, last, offset, to);
|
||||
switch (ret) {
|
||||
@ -844,6 +851,7 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
|
||||
vmf->page->index, priv);
|
||||
SetPagePrivate(vmf->page);
|
||||
set_page_private(vmf->page, priv);
|
||||
file_update_time(file);
|
||||
|
||||
sb_end_pagefault(inode->i_sb);
|
||||
return VM_FAULT_LOCKED;
|
||||
|
@ -329,29 +329,6 @@ static void xdr_decode_YFSFetchVolumeStatus(const __be32 **_bp,
|
||||
*_bp += sizeof(*x) / sizeof(__be32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deliver a reply that's a status, callback and volsync.
|
||||
*/
|
||||
static int yfs_deliver_fs_status_cb_and_volsync(struct afs_call *call)
|
||||
{
|
||||
struct afs_operation *op = call->op;
|
||||
const __be32 *bp;
|
||||
int ret;
|
||||
|
||||
ret = afs_transfer_reply(call);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_YFSFetchStatus(&bp, call, &op->file[0].scb);
|
||||
xdr_decode_YFSCallBack(&bp, call, &op->file[0].scb);
|
||||
xdr_decode_YFSVolSync(&bp, &op->volsync);
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deliver reply data to operations that just return a file status and a volume
|
||||
* sync record.
|
||||
@ -374,48 +351,6 @@ static int yfs_deliver_status_and_volsync(struct afs_call *call)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* YFS.FetchStatus operation type
|
||||
*/
|
||||
static const struct afs_call_type yfs_RXYFSFetchStatus_vnode = {
|
||||
.name = "YFS.FetchStatus(vnode)",
|
||||
.op = yfs_FS_FetchStatus,
|
||||
.deliver = yfs_deliver_fs_status_cb_and_volsync,
|
||||
.destructor = afs_flat_call_destructor,
|
||||
};
|
||||
|
||||
/*
|
||||
* Fetch the status information for a file.
|
||||
*/
|
||||
void yfs_fs_fetch_file_status(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode_param *vp = &op->file[0];
|
||||
struct afs_call *call;
|
||||
__be32 *bp;
|
||||
|
||||
_enter(",%x,{%llx:%llu},,",
|
||||
key_serial(op->key), vp->fid.vid, vp->fid.vnode);
|
||||
|
||||
call = afs_alloc_flat_call(op->net, &yfs_RXYFSFetchStatus_vnode,
|
||||
sizeof(__be32) * 2 +
|
||||
sizeof(struct yfs_xdr_YFSFid),
|
||||
sizeof(struct yfs_xdr_YFSFetchStatus) +
|
||||
sizeof(struct yfs_xdr_YFSCallBack) +
|
||||
sizeof(struct yfs_xdr_YFSVolSync));
|
||||
if (!call)
|
||||
return afs_op_nomem(op);
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
bp = xdr_encode_u32(bp, YFSFETCHSTATUS);
|
||||
bp = xdr_encode_u32(bp, 0); /* RPC flags */
|
||||
bp = xdr_encode_YFSFid(bp, &vp->fid);
|
||||
yfs_check_req(call, bp);
|
||||
|
||||
trace_afs_make_fs_call(call, &vp->fid);
|
||||
afs_make_op_call(op, call, GFP_NOFS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deliver reply data to an YFS.FetchData64.
|
||||
*/
|
||||
@ -1604,13 +1539,37 @@ void yfs_fs_release_lock(struct afs_operation *op)
|
||||
afs_make_op_call(op, call, GFP_NOFS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deliver a reply to YFS.FetchStatus
|
||||
*/
|
||||
static int yfs_deliver_fs_fetch_status(struct afs_call *call)
|
||||
{
|
||||
struct afs_operation *op = call->op;
|
||||
struct afs_vnode_param *vp = &op->file[op->fetch_status.which];
|
||||
const __be32 *bp;
|
||||
int ret;
|
||||
|
||||
ret = afs_transfer_reply(call);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_YFSFetchStatus(&bp, call, &vp->scb);
|
||||
xdr_decode_YFSCallBack(&bp, call, &vp->scb);
|
||||
xdr_decode_YFSVolSync(&bp, &op->volsync);
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* YFS.FetchStatus operation type
|
||||
*/
|
||||
static const struct afs_call_type yfs_RXYFSFetchStatus = {
|
||||
.name = "YFS.FetchStatus",
|
||||
.op = yfs_FS_FetchStatus,
|
||||
.deliver = yfs_deliver_fs_status_cb_and_volsync,
|
||||
.deliver = yfs_deliver_fs_fetch_status,
|
||||
.destructor = afs_flat_call_destructor,
|
||||
};
|
||||
|
||||
@ -1619,7 +1578,7 @@ static const struct afs_call_type yfs_RXYFSFetchStatus = {
|
||||
*/
|
||||
void yfs_fs_fetch_status(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode_param *vp = &op->file[0];
|
||||
struct afs_vnode_param *vp = &op->file[op->fetch_status.which];
|
||||
struct afs_call *call;
|
||||
__be32 *bp;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user