i2c: tegra: Add Dmitry as a reviewer
[linux-2.6/linux-2.6-arm.git] / fs / afs / dir_silly.c
blob057b8d3224228d2f7bb6a7ab93987692546bee3f
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AFS silly rename handling
4 * Copyright (C) 2019 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
6 * - Derived from NFS's sillyrename.
7 */
9 #include <linux/kernel.h>
10 #include <linux/fs.h>
11 #include <linux/namei.h>
12 #include <linux/fsnotify.h>
13 #include "internal.h"
16 * Actually perform the silly rename step.
18 static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
19 struct dentry *old, struct dentry *new,
20 struct key *key)
22 struct afs_fs_cursor fc;
23 struct afs_status_cb *scb;
24 int ret = -ERESTARTSYS;
26 _enter("%pd,%pd", old, new);
28 scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
29 if (!scb)
30 return -ENOMEM;
32 trace_afs_silly_rename(vnode, false);
33 if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
34 afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
36 while (afs_select_fileserver(&fc)) {
37 fc.cb_break = afs_calc_vnode_cb_break(dvnode);
38 afs_fs_rename(&fc, old->d_name.name,
39 dvnode, new->d_name.name,
40 scb, scb);
43 afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
44 &dir_data_version, scb);
45 ret = afs_end_vnode_operation(&fc);
48 if (ret == 0) {
49 spin_lock(&old->d_lock);
50 old->d_flags |= DCACHE_NFSFS_RENAMED;
51 spin_unlock(&old->d_lock);
52 if (dvnode->silly_key != key) {
53 key_put(dvnode->silly_key);
54 dvnode->silly_key = key_get(key);
57 if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
58 afs_edit_dir_remove(dvnode, &old->d_name,
59 afs_edit_dir_for_silly_0);
60 if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
61 afs_edit_dir_add(dvnode, &new->d_name,
62 &vnode->fid, afs_edit_dir_for_silly_1);
64 /* vfs_unlink and the like do not issue this when a file is
65 * sillyrenamed, so do it here.
67 fsnotify_nameremove(old, 0);
70 kfree(scb);
71 _leave(" = %d", ret);
72 return ret;
75 /**
76 * afs_sillyrename - Perform a silly-rename of a dentry
78 * AFS is stateless and the server doesn't know when the client is holding a
79 * file open. To prevent application problems when a file is unlinked while
80 * it's still open, the client performs a "silly-rename". That is, it renames
81 * the file to a hidden file in the same directory, and only performs the
82 * unlink once the last reference to it is put.
84 * The final cleanup is done during dentry_iput.
86 int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
87 struct dentry *dentry, struct key *key)
89 static unsigned int sillycounter;
90 struct dentry *sdentry = NULL;
91 unsigned char silly[16];
92 int ret = -EBUSY;
94 _enter("");
96 /* We don't allow a dentry to be silly-renamed twice. */
97 if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
98 return -EBUSY;
100 sdentry = NULL;
101 do {
102 int slen;
104 dput(sdentry);
105 sillycounter++;
107 /* Create a silly name. Note that the ".__afs" prefix is
108 * understood by the salvager and must not be changed.
110 slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter);
111 sdentry = lookup_one_len(silly, dentry->d_parent, slen);
113 /* N.B. Better to return EBUSY here ... it could be dangerous
114 * to delete the file while it's in use.
116 if (IS_ERR(sdentry))
117 goto out;
118 } while (!d_is_negative(sdentry));
120 ihold(&vnode->vfs_inode);
122 ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key);
123 switch (ret) {
124 case 0:
125 /* The rename succeeded. */
126 d_move(dentry, sdentry);
127 break;
128 case -ERESTARTSYS:
129 /* The result of the rename is unknown. Play it safe by forcing
130 * a new lookup.
132 d_drop(dentry);
133 d_drop(sdentry);
136 iput(&vnode->vfs_inode);
137 dput(sdentry);
138 out:
139 _leave(" = %d", ret);
140 return ret;
144 * Tell the server to remove a sillyrename file.
146 static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode,
147 struct dentry *dentry, struct key *key)
149 struct afs_fs_cursor fc;
150 struct afs_status_cb *scb;
151 int ret = -ERESTARTSYS;
153 _enter("");
155 scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
156 if (!scb)
157 return -ENOMEM;
159 trace_afs_silly_rename(vnode, true);
160 if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
161 afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
163 while (afs_select_fileserver(&fc)) {
164 fc.cb_break = afs_calc_vnode_cb_break(dvnode);
166 if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
167 !test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
168 yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
169 &scb[0], &scb[1]);
170 if (fc.ac.error != -ECONNABORTED ||
171 fc.ac.abort_code != RXGEN_OPCODE)
172 continue;
173 set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
176 afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
179 afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
180 &dir_data_version, &scb[0]);
181 ret = afs_end_vnode_operation(&fc);
182 if (ret == 0) {
183 drop_nlink(&vnode->vfs_inode);
184 if (vnode->vfs_inode.i_nlink == 0) {
185 set_bit(AFS_VNODE_DELETED, &vnode->flags);
186 clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
189 if (ret == 0 &&
190 test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
191 afs_edit_dir_remove(dvnode, &dentry->d_name,
192 afs_edit_dir_for_unlink);
195 kfree(scb);
196 _leave(" = %d", ret);
197 return ret;
201 * Remove sillyrename file on iput.
203 int afs_silly_iput(struct dentry *dentry, struct inode *inode)
205 struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent));
206 struct afs_vnode *vnode = AFS_FS_I(inode);
207 struct dentry *alias;
208 int ret;
210 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
212 _enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
214 down_read(&dvnode->rmdir_lock);
216 alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq);
217 if (IS_ERR(alias)) {
218 up_read(&dvnode->rmdir_lock);
219 return 0;
222 if (!d_in_lookup(alias)) {
223 /* We raced with lookup... See if we need to transfer the
224 * sillyrename information to the aliased dentry.
226 ret = 0;
227 spin_lock(&alias->d_lock);
228 if (d_really_is_positive(alias) &&
229 !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
230 alias->d_flags |= DCACHE_NFSFS_RENAMED;
231 ret = 1;
233 spin_unlock(&alias->d_lock);
234 up_read(&dvnode->rmdir_lock);
235 dput(alias);
236 return ret;
239 /* Stop lock-release from complaining. */
240 spin_lock(&vnode->lock);
241 vnode->lock_state = AFS_VNODE_LOCK_DELETED;
242 trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0);
243 spin_unlock(&vnode->lock);
245 afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key);
246 up_read(&dvnode->rmdir_lock);
247 d_lookup_done(alias);
248 dput(alias);
249 return 1;