afs: Concoct ctimes
The in-kernel afs filesystem ignores ctime because the AFS fileserver protocol doesn't support ctimes. This, however, causes various xfstests to fail. Work around this by: (1) Setting ctime to attr->ia_ctime in afs_setattr(). (2) Not ignoring ATTR_MTIME_SET, ATTR_TIMES_SET and ATTR_TOUCH settings. (3) Setting the ctime from the server mtime when on the target file when creating a hard link to it. (4) Setting the ctime on directories from their revised mtimes when renaming/moving a file. Found by the generic/221 and generic/309 xfstests. Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
parent
3f4aa98181
commit
da8d075512
18
fs/afs/dir.c
18
fs/afs/dir.c
|
@ -1268,6 +1268,7 @@ static void afs_vnode_new_inode(struct afs_operation *op)
|
||||||
static void afs_create_success(struct afs_operation *op)
|
static void afs_create_success(struct afs_operation *op)
|
||||||
{
|
{
|
||||||
_enter("op=%08x", op->debug_id);
|
_enter("op=%08x", op->debug_id);
|
||||||
|
op->ctime = op->file[0].scb.status.mtime_client;
|
||||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||||
afs_vnode_commit_status(op, &op->file[0]);
|
afs_vnode_commit_status(op, &op->file[0]);
|
||||||
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
||||||
|
@ -1325,6 +1326,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
|
|
||||||
afs_op_set_vnode(op, 0, dvnode);
|
afs_op_set_vnode(op, 0, dvnode);
|
||||||
op->file[0].dv_delta = 1;
|
op->file[0].dv_delta = 1;
|
||||||
|
op->file[0].update_ctime = true;
|
||||||
op->dentry = dentry;
|
op->dentry = dentry;
|
||||||
op->create.mode = S_IFDIR | mode;
|
op->create.mode = S_IFDIR | mode;
|
||||||
op->create.reason = afs_edit_dir_for_mkdir;
|
op->create.reason = afs_edit_dir_for_mkdir;
|
||||||
|
@ -1350,6 +1352,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry)
|
||||||
static void afs_rmdir_success(struct afs_operation *op)
|
static void afs_rmdir_success(struct afs_operation *op)
|
||||||
{
|
{
|
||||||
_enter("op=%08x", op->debug_id);
|
_enter("op=%08x", op->debug_id);
|
||||||
|
op->ctime = op->file[0].scb.status.mtime_client;
|
||||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||||
afs_vnode_commit_status(op, &op->file[0]);
|
afs_vnode_commit_status(op, &op->file[0]);
|
||||||
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
||||||
|
@ -1404,6 +1407,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
|
|
||||||
afs_op_set_vnode(op, 0, dvnode);
|
afs_op_set_vnode(op, 0, dvnode);
|
||||||
op->file[0].dv_delta = 1;
|
op->file[0].dv_delta = 1;
|
||||||
|
op->file[0].update_ctime = true;
|
||||||
|
|
||||||
op->dentry = dentry;
|
op->dentry = dentry;
|
||||||
op->ops = &afs_rmdir_operation;
|
op->ops = &afs_rmdir_operation;
|
||||||
|
@ -1479,6 +1483,7 @@ static void afs_dir_remove_link(struct afs_operation *op)
|
||||||
static void afs_unlink_success(struct afs_operation *op)
|
static void afs_unlink_success(struct afs_operation *op)
|
||||||
{
|
{
|
||||||
_enter("op=%08x", op->debug_id);
|
_enter("op=%08x", op->debug_id);
|
||||||
|
op->ctime = op->file[0].scb.status.mtime_client;
|
||||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||||
afs_vnode_commit_status(op, &op->file[0]);
|
afs_vnode_commit_status(op, &op->file[0]);
|
||||||
afs_vnode_commit_status(op, &op->file[1]);
|
afs_vnode_commit_status(op, &op->file[1]);
|
||||||
|
@ -1537,6 +1542,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
|
|
||||||
afs_op_set_vnode(op, 0, dvnode);
|
afs_op_set_vnode(op, 0, dvnode);
|
||||||
op->file[0].dv_delta = 1;
|
op->file[0].dv_delta = 1;
|
||||||
|
op->file[0].update_ctime = true;
|
||||||
|
|
||||||
/* Try to make sure we have a callback promise on the victim. */
|
/* Try to make sure we have a callback promise on the victim. */
|
||||||
ret = afs_validate(vnode, op->key);
|
ret = afs_validate(vnode, op->key);
|
||||||
|
@ -1561,6 +1567,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
|
|
||||||
op->file[1].vnode = vnode;
|
op->file[1].vnode = vnode;
|
||||||
|
op->file[1].update_ctime = true;
|
||||||
op->dentry = dentry;
|
op->dentry = dentry;
|
||||||
op->ops = &afs_unlink_operation;
|
op->ops = &afs_unlink_operation;
|
||||||
return afs_do_sync_operation(op);
|
return afs_do_sync_operation(op);
|
||||||
|
@ -1601,6 +1608,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
|
|
||||||
afs_op_set_vnode(op, 0, dvnode);
|
afs_op_set_vnode(op, 0, dvnode);
|
||||||
op->file[0].dv_delta = 1;
|
op->file[0].dv_delta = 1;
|
||||||
|
op->file[0].update_ctime = true;
|
||||||
|
|
||||||
op->dentry = dentry;
|
op->dentry = dentry;
|
||||||
op->create.mode = S_IFREG | mode;
|
op->create.mode = S_IFREG | mode;
|
||||||
|
@ -1620,6 +1628,7 @@ static void afs_link_success(struct afs_operation *op)
|
||||||
struct afs_vnode_param *vp = &op->file[1];
|
struct afs_vnode_param *vp = &op->file[1];
|
||||||
|
|
||||||
_enter("op=%08x", op->debug_id);
|
_enter("op=%08x", op->debug_id);
|
||||||
|
op->ctime = dvp->scb.status.mtime_client;
|
||||||
afs_vnode_commit_status(op, dvp);
|
afs_vnode_commit_status(op, dvp);
|
||||||
afs_vnode_commit_status(op, vp);
|
afs_vnode_commit_status(op, vp);
|
||||||
afs_update_dentry_version(op, dvp, op->dentry);
|
afs_update_dentry_version(op, dvp, op->dentry);
|
||||||
|
@ -1672,6 +1681,8 @@ static int afs_link(struct dentry *from, struct inode *dir,
|
||||||
afs_op_set_vnode(op, 0, dvnode);
|
afs_op_set_vnode(op, 0, dvnode);
|
||||||
afs_op_set_vnode(op, 1, vnode);
|
afs_op_set_vnode(op, 1, vnode);
|
||||||
op->file[0].dv_delta = 1;
|
op->file[0].dv_delta = 1;
|
||||||
|
op->file[0].update_ctime = true;
|
||||||
|
op->file[1].update_ctime = true;
|
||||||
|
|
||||||
op->dentry = dentry;
|
op->dentry = dentry;
|
||||||
op->dentry_2 = from;
|
op->dentry_2 = from;
|
||||||
|
@ -1740,10 +1751,13 @@ static void afs_rename_success(struct afs_operation *op)
|
||||||
{
|
{
|
||||||
_enter("op=%08x", op->debug_id);
|
_enter("op=%08x", op->debug_id);
|
||||||
|
|
||||||
|
op->ctime = op->file[0].scb.status.mtime_client;
|
||||||
afs_vnode_commit_status(op, &op->file[0]);
|
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]);
|
afs_vnode_commit_status(op, &op->file[1]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void afs_rename_edit_dir(struct afs_operation *op)
|
static void afs_rename_edit_dir(struct afs_operation *op)
|
||||||
{
|
{
|
||||||
|
@ -1860,6 +1874,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 */
|
afs_op_set_vnode(op, 1, new_dvnode); /* May be same as orig_dvnode */
|
||||||
op->file[0].dv_delta = 1;
|
op->file[0].dv_delta = 1;
|
||||||
op->file[1].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 = old_dentry;
|
||||||
op->dentry_2 = new_dentry;
|
op->dentry_2 = new_dentry;
|
||||||
|
|
|
@ -165,6 +165,7 @@ static void afs_apply_status(struct afs_operation *op,
|
||||||
{
|
{
|
||||||
struct afs_file_status *status = &vp->scb.status;
|
struct afs_file_status *status = &vp->scb.status;
|
||||||
struct afs_vnode *vnode = vp->vnode;
|
struct afs_vnode *vnode = vp->vnode;
|
||||||
|
struct inode *inode = &vnode->vfs_inode;
|
||||||
struct timespec64 t;
|
struct timespec64 t;
|
||||||
umode_t mode;
|
umode_t mode;
|
||||||
bool data_changed = false;
|
bool data_changed = false;
|
||||||
|
@ -187,25 +188,25 @@ static void afs_apply_status(struct afs_operation *op,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status->nlink != vnode->status.nlink)
|
if (status->nlink != vnode->status.nlink)
|
||||||
set_nlink(&vnode->vfs_inode, status->nlink);
|
set_nlink(inode, status->nlink);
|
||||||
|
|
||||||
if (status->owner != vnode->status.owner)
|
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)
|
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) {
|
if (status->mode != vnode->status.mode) {
|
||||||
mode = vnode->vfs_inode.i_mode;
|
mode = inode->i_mode;
|
||||||
mode &= ~S_IALLUGO;
|
mode &= ~S_IALLUGO;
|
||||||
mode |= status->mode;
|
mode |= status->mode;
|
||||||
WRITE_ONCE(vnode->vfs_inode.i_mode, mode);
|
WRITE_ONCE(inode->i_mode, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
t = status->mtime_client;
|
t = status->mtime_client;
|
||||||
vnode->vfs_inode.i_ctime = t;
|
inode->i_mtime = t;
|
||||||
vnode->vfs_inode.i_mtime = t;
|
if (vp->update_ctime)
|
||||||
vnode->vfs_inode.i_atime = t;
|
inode->i_ctime = op->ctime;
|
||||||
|
|
||||||
if (vnode->status.data_version != status->data_version)
|
if (vnode->status.data_version != status->data_version)
|
||||||
data_changed = true;
|
data_changed = true;
|
||||||
|
@ -239,15 +240,18 @@ static void afs_apply_status(struct afs_operation *op,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_changed) {
|
if (data_changed) {
|
||||||
inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
|
inode_set_iversion_raw(inode, status->data_version);
|
||||||
|
|
||||||
/* Only update the size if the data version jumped. If the
|
/* Only update the size if the data version jumped. If the
|
||||||
* file is being modified locally, then we might have our own
|
* file is being modified locally, then we might have our own
|
||||||
* idea of what the size should be that's not the same as
|
* idea of what the size should be that's not the same as
|
||||||
* what's on the server.
|
* what's on the server.
|
||||||
*/
|
*/
|
||||||
if (change_size)
|
if (change_size) {
|
||||||
afs_set_i_size(vnode, status->size);
|
afs_set_i_size(vnode, status->size);
|
||||||
|
inode->i_ctime = t;
|
||||||
|
inode->i_atime = t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -817,7 +821,8 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
attr->ia_valid);
|
attr->ia_valid);
|
||||||
|
|
||||||
if (!(attr->ia_valid & (ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID |
|
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]");
|
_leave(" = 0 [unsupported]");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -837,6 +842,8 @@ int afs_setattr(struct dentry *dentry, struct iattr *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->ctime = attr->ia_ctime;
|
||||||
|
op->file[0].update_ctime = 1;
|
||||||
|
|
||||||
op->ops = &afs_setattr_operation;
|
op->ops = &afs_setattr_operation;
|
||||||
return afs_do_sync_operation(op);
|
return afs_do_sync_operation(op);
|
||||||
|
|
|
@ -746,6 +746,7 @@ struct afs_vnode_param {
|
||||||
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; /* 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; /* T if we need the I/O lock on this */
|
||||||
|
bool update_ctime; /* Need to update the ctime */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -766,6 +767,7 @@ struct afs_operation {
|
||||||
struct dentry *dentry; /* Dentry to be altered */
|
struct dentry *dentry; /* Dentry to be altered */
|
||||||
struct dentry *dentry_2; /* Second dentry to be altered */
|
struct dentry *dentry_2; /* Second dentry to be altered */
|
||||||
struct timespec64 mtime; /* Modification time to record */
|
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 nr_files; /* Number of entries in file[], more_files */
|
||||||
short error;
|
short error;
|
||||||
unsigned int abort_code;
|
unsigned int abort_code;
|
||||||
|
|
|
@ -393,6 +393,7 @@ static void afs_store_data_success(struct afs_operation *op)
|
||||||
{
|
{
|
||||||
struct afs_vnode *vnode = op->file[0].vnode;
|
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]);
|
afs_vnode_commit_status(op, &op->file[0]);
|
||||||
if (op->error == 0) {
|
if (op->error == 0) {
|
||||||
afs_pages_written_back(vnode, op->store.first, op->store.last);
|
afs_pages_written_back(vnode, op->store.first, op->store.last);
|
||||||
|
|
Loading…
Reference in New Issue