xref: /openbmc/linux/fs/afs/dir_silly.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
279ddbfa5SDavid Howells /* AFS silly rename handling
379ddbfa5SDavid Howells  *
479ddbfa5SDavid Howells  * Copyright (C) 2019 Red Hat, Inc. All Rights Reserved.
579ddbfa5SDavid Howells  * Written by David Howells (dhowells@redhat.com)
679ddbfa5SDavid Howells  * - Derived from NFS's sillyrename.
779ddbfa5SDavid Howells  */
879ddbfa5SDavid Howells 
979ddbfa5SDavid Howells #include <linux/kernel.h>
1079ddbfa5SDavid Howells #include <linux/fs.h>
1179ddbfa5SDavid Howells #include <linux/namei.h>
1279ddbfa5SDavid Howells #include <linux/fsnotify.h>
1379ddbfa5SDavid Howells #include "internal.h"
1479ddbfa5SDavid Howells 
afs_silly_rename_success(struct afs_operation * op)15e49c7b2fSDavid Howells static void afs_silly_rename_success(struct afs_operation *op)
1679ddbfa5SDavid Howells {
17e49c7b2fSDavid Howells 	_enter("op=%08x", op->debug_id);
1879ddbfa5SDavid Howells 
19b6489a49SDavid Howells 	afs_check_dir_conflict(op, &op->file[0]);
20e49c7b2fSDavid Howells 	afs_vnode_commit_status(op, &op->file[0]);
2179ddbfa5SDavid Howells }
2279ddbfa5SDavid Howells 
afs_silly_rename_edit_dir(struct afs_operation * op)23e49c7b2fSDavid Howells static void afs_silly_rename_edit_dir(struct afs_operation *op)
24e49c7b2fSDavid Howells {
25e49c7b2fSDavid Howells 	struct afs_vnode_param *dvp = &op->file[0];
26e49c7b2fSDavid Howells 	struct afs_vnode *dvnode = dvp->vnode;
27e49c7b2fSDavid Howells 	struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry));
28e49c7b2fSDavid Howells 	struct dentry *old = op->dentry;
29e49c7b2fSDavid Howells 	struct dentry *new = op->dentry_2;
3079ddbfa5SDavid Howells 
3179ddbfa5SDavid Howells 	spin_lock(&old->d_lock);
3279ddbfa5SDavid Howells 	old->d_flags |= DCACHE_NFSFS_RENAMED;
3379ddbfa5SDavid Howells 	spin_unlock(&old->d_lock);
34e49c7b2fSDavid Howells 	if (dvnode->silly_key != op->key) {
3579ddbfa5SDavid Howells 		key_put(dvnode->silly_key);
36e49c7b2fSDavid Howells 		dvnode->silly_key = key_get(op->key);
3779ddbfa5SDavid Howells 	}
3879ddbfa5SDavid Howells 
392105c282SDavid Howells 	down_write(&dvnode->validate_lock);
402105c282SDavid Howells 	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
41e49c7b2fSDavid Howells 	    dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) {
4279ddbfa5SDavid Howells 		afs_edit_dir_remove(dvnode, &old->d_name,
4379ddbfa5SDavid Howells 				    afs_edit_dir_for_silly_0);
4479ddbfa5SDavid Howells 		afs_edit_dir_add(dvnode, &new->d_name,
4579ddbfa5SDavid Howells 				 &vnode->fid, afs_edit_dir_for_silly_1);
4679ddbfa5SDavid Howells 	}
472105c282SDavid Howells 	up_write(&dvnode->validate_lock);
482105c282SDavid Howells }
4979ddbfa5SDavid Howells 
50e49c7b2fSDavid Howells static const struct afs_operation_ops afs_silly_rename_operation = {
51e49c7b2fSDavid Howells 	.issue_afs_rpc	= afs_fs_rename,
52e49c7b2fSDavid Howells 	.issue_yfs_rpc	= yfs_fs_rename,
53e49c7b2fSDavid Howells 	.success	= afs_silly_rename_success,
54e49c7b2fSDavid Howells 	.edit_dir	= afs_silly_rename_edit_dir,
55e49c7b2fSDavid Howells };
56e49c7b2fSDavid Howells 
57e49c7b2fSDavid Howells /*
58e49c7b2fSDavid Howells  * Actually perform the silly rename step.
59e49c7b2fSDavid Howells  */
afs_do_silly_rename(struct afs_vnode * dvnode,struct afs_vnode * vnode,struct dentry * old,struct dentry * new,struct key * key)60e49c7b2fSDavid Howells static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
61e49c7b2fSDavid Howells 			       struct dentry *old, struct dentry *new,
62e49c7b2fSDavid Howells 			       struct key *key)
63e49c7b2fSDavid Howells {
64e49c7b2fSDavid Howells 	struct afs_operation *op;
65e49c7b2fSDavid Howells 
66e49c7b2fSDavid Howells 	_enter("%pd,%pd", old, new);
67e49c7b2fSDavid Howells 
68e49c7b2fSDavid Howells 	op = afs_alloc_operation(key, dvnode->volume);
69e49c7b2fSDavid Howells 	if (IS_ERR(op))
70e49c7b2fSDavid Howells 		return PTR_ERR(op);
71e49c7b2fSDavid Howells 
72e49c7b2fSDavid Howells 	afs_op_set_vnode(op, 0, dvnode);
73b6489a49SDavid Howells 	afs_op_set_vnode(op, 1, dvnode);
74b6489a49SDavid Howells 	op->file[0].dv_delta = 1;
75b6489a49SDavid Howells 	op->file[1].dv_delta = 1;
7622650f14SDavid Howells 	op->file[0].modification = true;
7722650f14SDavid Howells 	op->file[1].modification = true;
78b6489a49SDavid Howells 	op->file[0].update_ctime = true;
79b6489a49SDavid Howells 	op->file[1].update_ctime = true;
80e49c7b2fSDavid Howells 
81e49c7b2fSDavid Howells 	op->dentry		= old;
82e49c7b2fSDavid Howells 	op->dentry_2		= new;
83e49c7b2fSDavid Howells 	op->ops			= &afs_silly_rename_operation;
84e49c7b2fSDavid Howells 
85e49c7b2fSDavid Howells 	trace_afs_silly_rename(vnode, false);
86e49c7b2fSDavid Howells 	return afs_do_sync_operation(op);
8779ddbfa5SDavid Howells }
8879ddbfa5SDavid Howells 
89dcb442b1SDavid Howells /*
90dcb442b1SDavid Howells  * Perform silly-rename of a dentry.
9179ddbfa5SDavid Howells  *
9279ddbfa5SDavid Howells  * AFS is stateless and the server doesn't know when the client is holding a
9379ddbfa5SDavid Howells  * file open.  To prevent application problems when a file is unlinked while
9479ddbfa5SDavid Howells  * it's still open, the client performs a "silly-rename".  That is, it renames
9579ddbfa5SDavid Howells  * the file to a hidden file in the same directory, and only performs the
9679ddbfa5SDavid Howells  * unlink once the last reference to it is put.
9779ddbfa5SDavid Howells  *
9879ddbfa5SDavid Howells  * The final cleanup is done during dentry_iput.
9979ddbfa5SDavid Howells  */
afs_sillyrename(struct afs_vnode * dvnode,struct afs_vnode * vnode,struct dentry * dentry,struct key * key)10079ddbfa5SDavid Howells int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
10179ddbfa5SDavid Howells 		    struct dentry *dentry, struct key *key)
10279ddbfa5SDavid Howells {
10379ddbfa5SDavid Howells 	static unsigned int sillycounter;
10479ddbfa5SDavid Howells 	struct dentry *sdentry = NULL;
10579ddbfa5SDavid Howells 	unsigned char silly[16];
10679ddbfa5SDavid Howells 	int ret = -EBUSY;
10779ddbfa5SDavid Howells 
10879ddbfa5SDavid Howells 	_enter("");
10979ddbfa5SDavid Howells 
11079ddbfa5SDavid Howells 	/* We don't allow a dentry to be silly-renamed twice. */
11179ddbfa5SDavid Howells 	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
11279ddbfa5SDavid Howells 		return -EBUSY;
11379ddbfa5SDavid Howells 
11479ddbfa5SDavid Howells 	sdentry = NULL;
11579ddbfa5SDavid Howells 	do {
11679ddbfa5SDavid Howells 		int slen;
11779ddbfa5SDavid Howells 
11879ddbfa5SDavid Howells 		dput(sdentry);
11979ddbfa5SDavid Howells 		sillycounter++;
12079ddbfa5SDavid Howells 
12179ddbfa5SDavid Howells 		/* Create a silly name.  Note that the ".__afs" prefix is
12279ddbfa5SDavid Howells 		 * understood by the salvager and must not be changed.
12379ddbfa5SDavid Howells 		 */
12479ddbfa5SDavid Howells 		slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter);
12579ddbfa5SDavid Howells 		sdentry = lookup_one_len(silly, dentry->d_parent, slen);
12679ddbfa5SDavid Howells 
12779ddbfa5SDavid Howells 		/* N.B. Better to return EBUSY here ... it could be dangerous
12879ddbfa5SDavid Howells 		 * to delete the file while it's in use.
12979ddbfa5SDavid Howells 		 */
13079ddbfa5SDavid Howells 		if (IS_ERR(sdentry))
13179ddbfa5SDavid Howells 			goto out;
13279ddbfa5SDavid Howells 	} while (!d_is_negative(sdentry));
13379ddbfa5SDavid Howells 
134*874c8ca1SDavid Howells 	ihold(&vnode->netfs.inode);
13579ddbfa5SDavid Howells 
13679ddbfa5SDavid Howells 	ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key);
13779ddbfa5SDavid Howells 	switch (ret) {
13879ddbfa5SDavid Howells 	case 0:
13979ddbfa5SDavid Howells 		/* The rename succeeded. */
140b6489a49SDavid Howells 		set_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags);
14179ddbfa5SDavid Howells 		d_move(dentry, sdentry);
14279ddbfa5SDavid Howells 		break;
14379ddbfa5SDavid Howells 	case -ERESTARTSYS:
14479ddbfa5SDavid Howells 		/* The result of the rename is unknown. Play it safe by forcing
14579ddbfa5SDavid Howells 		 * a new lookup.
14679ddbfa5SDavid Howells 		 */
14779ddbfa5SDavid Howells 		d_drop(dentry);
14879ddbfa5SDavid Howells 		d_drop(sdentry);
14979ddbfa5SDavid Howells 	}
15079ddbfa5SDavid Howells 
151*874c8ca1SDavid Howells 	iput(&vnode->netfs.inode);
15279ddbfa5SDavid Howells 	dput(sdentry);
15379ddbfa5SDavid Howells out:
15479ddbfa5SDavid Howells 	_leave(" = %d", ret);
15579ddbfa5SDavid Howells 	return ret;
15679ddbfa5SDavid Howells }
15779ddbfa5SDavid Howells 
afs_silly_unlink_success(struct afs_operation * op)158e49c7b2fSDavid Howells static void afs_silly_unlink_success(struct afs_operation *op)
15979ddbfa5SDavid Howells {
160e49c7b2fSDavid Howells 	_enter("op=%08x", op->debug_id);
161b6489a49SDavid Howells 	afs_check_dir_conflict(op, &op->file[0]);
162e49c7b2fSDavid Howells 	afs_vnode_commit_status(op, &op->file[0]);
163e49c7b2fSDavid Howells 	afs_vnode_commit_status(op, &op->file[1]);
164e49c7b2fSDavid Howells 	afs_update_dentry_version(op, &op->file[0], op->dentry);
16579ddbfa5SDavid Howells }
166e49c7b2fSDavid Howells 
afs_silly_unlink_edit_dir(struct afs_operation * op)167e49c7b2fSDavid Howells static void afs_silly_unlink_edit_dir(struct afs_operation *op)
168e49c7b2fSDavid Howells {
169e49c7b2fSDavid Howells 	struct afs_vnode_param *dvp = &op->file[0];
170e49c7b2fSDavid Howells 	struct afs_vnode *dvnode = dvp->vnode;
171e49c7b2fSDavid Howells 
172e49c7b2fSDavid Howells 	_enter("op=%08x", op->debug_id);
1732105c282SDavid Howells 	down_write(&dvnode->validate_lock);
1742105c282SDavid Howells 	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
175e49c7b2fSDavid Howells 	    dvnode->status.data_version == dvp->dv_before + dvp->dv_delta)
176e49c7b2fSDavid Howells 		afs_edit_dir_remove(dvnode, &op->dentry->d_name,
17779ddbfa5SDavid Howells 				    afs_edit_dir_for_unlink);
1782105c282SDavid Howells 	up_write(&dvnode->validate_lock);
1792105c282SDavid Howells }
18079ddbfa5SDavid Howells 
181e49c7b2fSDavid Howells static const struct afs_operation_ops afs_silly_unlink_operation = {
182e49c7b2fSDavid Howells 	.issue_afs_rpc	= afs_fs_remove_file,
183e49c7b2fSDavid Howells 	.issue_yfs_rpc	= yfs_fs_remove_file,
184e49c7b2fSDavid Howells 	.success	= afs_silly_unlink_success,
185728279a5SDavid Howells 	.aborted	= afs_check_for_remote_deletion,
186e49c7b2fSDavid Howells 	.edit_dir	= afs_silly_unlink_edit_dir,
187e49c7b2fSDavid Howells };
188e49c7b2fSDavid Howells 
189e49c7b2fSDavid Howells /*
190e49c7b2fSDavid Howells  * Tell the server to remove a sillyrename file.
191e49c7b2fSDavid Howells  */
afs_do_silly_unlink(struct afs_vnode * dvnode,struct afs_vnode * vnode,struct dentry * dentry,struct key * key)192e49c7b2fSDavid Howells static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode,
193e49c7b2fSDavid Howells 			       struct dentry *dentry, struct key *key)
194e49c7b2fSDavid Howells {
195e49c7b2fSDavid Howells 	struct afs_operation *op;
196e49c7b2fSDavid Howells 
197e49c7b2fSDavid Howells 	_enter("");
198e49c7b2fSDavid Howells 
199e49c7b2fSDavid Howells 	op = afs_alloc_operation(NULL, dvnode->volume);
200e49c7b2fSDavid Howells 	if (IS_ERR(op))
201e49c7b2fSDavid Howells 		return PTR_ERR(op);
202e49c7b2fSDavid Howells 
203e49c7b2fSDavid Howells 	afs_op_set_vnode(op, 0, dvnode);
204e49c7b2fSDavid Howells 	afs_op_set_vnode(op, 1, vnode);
205b6489a49SDavid Howells 	op->file[0].dv_delta = 1;
20622650f14SDavid Howells 	op->file[0].modification = true;
207b6489a49SDavid Howells 	op->file[0].update_ctime = true;
208b6489a49SDavid Howells 	op->file[1].op_unlinked = true;
209b6489a49SDavid Howells 	op->file[1].update_ctime = true;
210e49c7b2fSDavid Howells 
211e49c7b2fSDavid Howells 	op->dentry	= dentry;
212e49c7b2fSDavid Howells 	op->ops		= &afs_silly_unlink_operation;
213e49c7b2fSDavid Howells 
214e49c7b2fSDavid Howells 	trace_afs_silly_rename(vnode, true);
215b6489a49SDavid Howells 	afs_begin_vnode_operation(op);
216b6489a49SDavid Howells 	afs_wait_for_operation(op);
217b6489a49SDavid Howells 
218b6489a49SDavid Howells 	/* If there was a conflict with a third party, check the status of the
219b6489a49SDavid Howells 	 * unlinked vnode.
220b6489a49SDavid Howells 	 */
221b6489a49SDavid Howells 	if (op->error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) {
222b6489a49SDavid Howells 		op->file[1].update_ctime = false;
223b6489a49SDavid Howells 		op->fetch_status.which = 1;
224b6489a49SDavid Howells 		op->ops = &afs_fetch_status_operation;
225b6489a49SDavid Howells 		afs_begin_vnode_operation(op);
226b6489a49SDavid Howells 		afs_wait_for_operation(op);
227b6489a49SDavid Howells 	}
228b6489a49SDavid Howells 
229b6489a49SDavid Howells 	return afs_put_operation(op);
23079ddbfa5SDavid Howells }
23179ddbfa5SDavid Howells 
23279ddbfa5SDavid Howells /*
23379ddbfa5SDavid Howells  * Remove sillyrename file on iput.
23479ddbfa5SDavid Howells  */
afs_silly_iput(struct dentry * dentry,struct inode * inode)23579ddbfa5SDavid Howells int afs_silly_iput(struct dentry *dentry, struct inode *inode)
23679ddbfa5SDavid Howells {
23779ddbfa5SDavid Howells 	struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent));
23879ddbfa5SDavid Howells 	struct afs_vnode *vnode = AFS_FS_I(inode);
23979ddbfa5SDavid Howells 	struct dentry *alias;
24079ddbfa5SDavid Howells 	int ret;
24179ddbfa5SDavid Howells 
24279ddbfa5SDavid Howells 	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
24379ddbfa5SDavid Howells 
24479ddbfa5SDavid Howells 	_enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
24579ddbfa5SDavid Howells 
24679ddbfa5SDavid Howells 	down_read(&dvnode->rmdir_lock);
24779ddbfa5SDavid Howells 
24879ddbfa5SDavid Howells 	alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq);
24979ddbfa5SDavid Howells 	if (IS_ERR(alias)) {
25079ddbfa5SDavid Howells 		up_read(&dvnode->rmdir_lock);
25179ddbfa5SDavid Howells 		return 0;
25279ddbfa5SDavid Howells 	}
25379ddbfa5SDavid Howells 
25479ddbfa5SDavid Howells 	if (!d_in_lookup(alias)) {
25579ddbfa5SDavid Howells 		/* We raced with lookup...  See if we need to transfer the
25679ddbfa5SDavid Howells 		 * sillyrename information to the aliased dentry.
25779ddbfa5SDavid Howells 		 */
25879ddbfa5SDavid Howells 		ret = 0;
25979ddbfa5SDavid Howells 		spin_lock(&alias->d_lock);
26079ddbfa5SDavid Howells 		if (d_really_is_positive(alias) &&
26179ddbfa5SDavid Howells 		    !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
26279ddbfa5SDavid Howells 			alias->d_flags |= DCACHE_NFSFS_RENAMED;
26379ddbfa5SDavid Howells 			ret = 1;
26479ddbfa5SDavid Howells 		}
26579ddbfa5SDavid Howells 		spin_unlock(&alias->d_lock);
26679ddbfa5SDavid Howells 		up_read(&dvnode->rmdir_lock);
26779ddbfa5SDavid Howells 		dput(alias);
26879ddbfa5SDavid Howells 		return ret;
26979ddbfa5SDavid Howells 	}
27079ddbfa5SDavid Howells 
27179ddbfa5SDavid Howells 	/* Stop lock-release from complaining. */
27279ddbfa5SDavid Howells 	spin_lock(&vnode->lock);
27379ddbfa5SDavid Howells 	vnode->lock_state = AFS_VNODE_LOCK_DELETED;
27479ddbfa5SDavid Howells 	trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0);
27579ddbfa5SDavid Howells 	spin_unlock(&vnode->lock);
27679ddbfa5SDavid Howells 
27779ddbfa5SDavid Howells 	afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key);
27879ddbfa5SDavid Howells 	up_read(&dvnode->rmdir_lock);
27979ddbfa5SDavid Howells 	d_lookup_done(alias);
28079ddbfa5SDavid Howells 	dput(alias);
28179ddbfa5SDavid Howells 	return 1;
28279ddbfa5SDavid Howells }
283