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