Commit da8d0755 authored by David Howells's avatar David Howells
Browse files

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: default avatarDavid Howells <dhowells@redhat.com>
parent 3f4aa981
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -1268,6 +1268,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);
	op->ctime = op->file[0].scb.status.mtime_client;
	afs_check_for_remote_deletion(op, op->file[0].vnode);
	afs_vnode_commit_status(op, &op->file[0]);
	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);
	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,6 +1352,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);
	op->ctime = op->file[0].scb.status.mtime_client;
	afs_check_for_remote_deletion(op, op->file[0].vnode);
	afs_vnode_commit_status(op, &op->file[0]);
	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);
	op->file[0].dv_delta = 1;
	op->file[0].update_ctime = true;

	op->dentry	= dentry;
	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)
{
	_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_vnode_commit_status(op, &op->file[0]);
	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);
	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,6 +1567,7 @@ 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->dentry	= dentry;
	op->ops		= &afs_unlink_operation;
	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);
	op->file[0].dv_delta = 1;
	op->file[0].update_ctime = true;

	op->dentry	= dentry;
	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];

	_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);
@@ -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, 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;
@@ -1740,10 +1751,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_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 +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 */
	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;
+18 −11
Original line number Diff line number Diff line
@@ -165,6 +165,7 @@ 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;
@@ -187,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;
@@ -239,15 +240,18 @@ static void afs_apply_status(struct afs_operation *op,
	}

	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
		 * 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)
		if (change_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);

	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;
	}
@@ -837,6 +842,8 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)

	if (attr->ia_valid & ATTR_SIZE)
		op->file[0].dv_delta = 1;
	op->ctime = attr->ia_ctime;
	op->file[0].update_ctime = 1;

	op->ops = &afs_setattr_operation;
	return afs_do_sync_operation(op);
+2 −0
Original line number Diff line number Diff line
@@ -746,6 +746,7 @@ struct afs_vnode_param {
	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			update_ctime;	/* Need to update the ctime */
};

/*
@@ -766,6 +767,7 @@ 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;
+1 −0
Original line number Diff line number Diff line
@@ -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);