Merge tag 'for-linus-5.7-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rw...
[linux-2.6-microblaze.git] / fs / afs / dir_silly.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AFS silly rename handling
3  *
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  */
8
9 #include <linux/kernel.h>
10 #include <linux/fs.h>
11 #include <linux/namei.h>
12 #include <linux/fsnotify.h>
13 #include "internal.h"
14
15 /*
16  * Actually perform the silly rename step.
17  */
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)
21 {
22         struct afs_fs_cursor fc;
23         struct afs_status_cb *scb;
24         afs_dataversion_t dir_data_version;
25         int ret = -ERESTARTSYS;
26
27         _enter("%pd,%pd", old, new);
28
29         scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
30         if (!scb)
31                 return -ENOMEM;
32
33         trace_afs_silly_rename(vnode, false);
34         if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
35                 dir_data_version = dvnode->status.data_version + 1;
36
37                 while (afs_select_fileserver(&fc)) {
38                         fc.cb_break = afs_calc_vnode_cb_break(dvnode);
39                         afs_fs_rename(&fc, old->d_name.name,
40                                       dvnode, new->d_name.name,
41                                       scb, scb);
42                 }
43
44                 afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
45                                         &dir_data_version, scb);
46                 ret = afs_end_vnode_operation(&fc);
47         }
48
49         if (ret == 0) {
50                 spin_lock(&old->d_lock);
51                 old->d_flags |= DCACHE_NFSFS_RENAMED;
52                 spin_unlock(&old->d_lock);
53                 if (dvnode->silly_key != key) {
54                         key_put(dvnode->silly_key);
55                         dvnode->silly_key = key_get(key);
56                 }
57
58                 down_write(&dvnode->validate_lock);
59                 if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
60                     dvnode->status.data_version == dir_data_version) {
61                         afs_edit_dir_remove(dvnode, &old->d_name,
62                                             afs_edit_dir_for_silly_0);
63                         afs_edit_dir_add(dvnode, &new->d_name,
64                                          &vnode->fid, afs_edit_dir_for_silly_1);
65                 }
66                 up_write(&dvnode->validate_lock);
67         }
68
69         kfree(scb);
70         _leave(" = %d", ret);
71         return ret;
72 }
73
74 /**
75  * afs_sillyrename - Perform a silly-rename of a dentry
76  *
77  * AFS is stateless and the server doesn't know when the client is holding a
78  * file open.  To prevent application problems when a file is unlinked while
79  * it's still open, the client performs a "silly-rename".  That is, it renames
80  * the file to a hidden file in the same directory, and only performs the
81  * unlink once the last reference to it is put.
82  *
83  * The final cleanup is done during dentry_iput.
84  */
85 int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
86                     struct dentry *dentry, struct key *key)
87 {
88         static unsigned int sillycounter;
89         struct dentry *sdentry = NULL;
90         unsigned char silly[16];
91         int ret = -EBUSY;
92
93         _enter("");
94
95         /* We don't allow a dentry to be silly-renamed twice. */
96         if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
97                 return -EBUSY;
98
99         sdentry = NULL;
100         do {
101                 int slen;
102
103                 dput(sdentry);
104                 sillycounter++;
105
106                 /* Create a silly name.  Note that the ".__afs" prefix is
107                  * understood by the salvager and must not be changed.
108                  */
109                 slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter);
110                 sdentry = lookup_one_len(silly, dentry->d_parent, slen);
111
112                 /* N.B. Better to return EBUSY here ... it could be dangerous
113                  * to delete the file while it's in use.
114                  */
115                 if (IS_ERR(sdentry))
116                         goto out;
117         } while (!d_is_negative(sdentry));
118
119         ihold(&vnode->vfs_inode);
120
121         ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key);
122         switch (ret) {
123         case 0:
124                 /* The rename succeeded. */
125                 d_move(dentry, sdentry);
126                 break;
127         case -ERESTARTSYS:
128                 /* The result of the rename is unknown. Play it safe by forcing
129                  * a new lookup.
130                  */
131                 d_drop(dentry);
132                 d_drop(sdentry);
133         }
134
135         iput(&vnode->vfs_inode);
136         dput(sdentry);
137 out:
138         _leave(" = %d", ret);
139         return ret;
140 }
141
142 /*
143  * Tell the server to remove a sillyrename file.
144  */
145 static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode,
146                                struct dentry *dentry, struct key *key)
147 {
148         struct afs_fs_cursor fc;
149         struct afs_status_cb *scb;
150         int ret = -ERESTARTSYS;
151
152         _enter("");
153
154         scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
155         if (!scb)
156                 return -ENOMEM;
157
158         trace_afs_silly_rename(vnode, true);
159         if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
160                 afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
161
162                 while (afs_select_fileserver(&fc)) {
163                         fc.cb_break = afs_calc_vnode_cb_break(dvnode);
164
165                         if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
166                             !test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
167                                 yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
168                                                     &scb[0], &scb[1]);
169                                 if (fc.ac.error != -ECONNABORTED ||
170                                     fc.ac.abort_code != RXGEN_OPCODE)
171                                         continue;
172                                 set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
173                         }
174
175                         afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
176                 }
177
178                 afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
179                                         &dir_data_version, &scb[0]);
180                 ret = afs_end_vnode_operation(&fc);
181                 if (ret == 0) {
182                         drop_nlink(&vnode->vfs_inode);
183                         if (vnode->vfs_inode.i_nlink == 0) {
184                                 set_bit(AFS_VNODE_DELETED, &vnode->flags);
185                                 clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
186                         }
187                 }
188                 if (ret == 0) {
189                         down_write(&dvnode->validate_lock);
190                         if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
191                             dvnode->status.data_version == dir_data_version)
192                                 afs_edit_dir_remove(dvnode, &dentry->d_name,
193                                                     afs_edit_dir_for_unlink);
194                         up_write(&dvnode->validate_lock);
195                 }
196         }
197
198         kfree(scb);
199         _leave(" = %d", ret);
200         return ret;
201 }
202
203 /*
204  * Remove sillyrename file on iput.
205  */
206 int afs_silly_iput(struct dentry *dentry, struct inode *inode)
207 {
208         struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent));
209         struct afs_vnode *vnode = AFS_FS_I(inode);
210         struct dentry *alias;
211         int ret;
212
213         DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
214
215         _enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
216
217         down_read(&dvnode->rmdir_lock);
218
219         alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq);
220         if (IS_ERR(alias)) {
221                 up_read(&dvnode->rmdir_lock);
222                 return 0;
223         }
224
225         if (!d_in_lookup(alias)) {
226                 /* We raced with lookup...  See if we need to transfer the
227                  * sillyrename information to the aliased dentry.
228                  */
229                 ret = 0;
230                 spin_lock(&alias->d_lock);
231                 if (d_really_is_positive(alias) &&
232                     !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
233                         alias->d_flags |= DCACHE_NFSFS_RENAMED;
234                         ret = 1;
235                 }
236                 spin_unlock(&alias->d_lock);
237                 up_read(&dvnode->rmdir_lock);
238                 dput(alias);
239                 return ret;
240         }
241
242         /* Stop lock-release from complaining. */
243         spin_lock(&vnode->lock);
244         vnode->lock_state = AFS_VNODE_LOCK_DELETED;
245         trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0);
246         spin_unlock(&vnode->lock);
247
248         afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key);
249         up_read(&dvnode->rmdir_lock);
250         d_lookup_done(alias);
251         dput(alias);
252         return 1;
253 }