]> asedeno.scripts.mit.edu Git - linux.git/blob - fs/afs/dir_silly.c
Merge tag 'v5.2' into next
[linux.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         int ret = -ERESTARTSYS;
25
26         _enter("%pd,%pd", old, new);
27
28         scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
29         if (!scb)
30                 return -ENOMEM;
31
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;
35
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);
41                 }
42
43                 afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
44                                         &dir_data_version, scb);
45                 ret = afs_end_vnode_operation(&fc);
46         }
47
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);
55                 }
56
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);
63
64                 /* vfs_unlink and the like do not issue this when a file is
65                  * sillyrenamed, so do it here.
66                  */
67                 fsnotify_nameremove(old, 0);
68         }
69
70         kfree(scb);
71         _leave(" = %d", ret);
72         return ret;
73 }
74
75 /**
76  * afs_sillyrename - Perform a silly-rename of a dentry
77  *
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.
83  *
84  * The final cleanup is done during dentry_iput.
85  */
86 int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
87                     struct dentry *dentry, struct key *key)
88 {
89         static unsigned int sillycounter;
90         struct dentry *sdentry = NULL;
91         unsigned char silly[16];
92         int ret = -EBUSY;
93
94         _enter("");
95
96         /* We don't allow a dentry to be silly-renamed twice. */
97         if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
98                 return -EBUSY;
99
100         sdentry = NULL;
101         do {
102                 int slen;
103
104                 dput(sdentry);
105                 sillycounter++;
106
107                 /* Create a silly name.  Note that the ".__afs" prefix is
108                  * understood by the salvager and must not be changed.
109                  */
110                 slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter);
111                 sdentry = lookup_one_len(silly, dentry->d_parent, slen);
112
113                 /* N.B. Better to return EBUSY here ... it could be dangerous
114                  * to delete the file while it's in use.
115                  */
116                 if (IS_ERR(sdentry))
117                         goto out;
118         } while (!d_is_negative(sdentry));
119
120         ihold(&vnode->vfs_inode);
121
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.
131                  */
132                 d_drop(dentry);
133                 d_drop(sdentry);
134         }
135
136         iput(&vnode->vfs_inode);
137         dput(sdentry);
138 out:
139         _leave(" = %d", ret);
140         return ret;
141 }
142
143 /*
144  * Tell the server to remove a sillyrename file.
145  */
146 static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode,
147                                struct dentry *dentry, struct key *key)
148 {
149         struct afs_fs_cursor fc;
150         struct afs_status_cb *scb;
151         int ret = -ERESTARTSYS;
152
153         _enter("");
154
155         scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
156         if (!scb)
157                 return -ENOMEM;
158
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;
162
163                 while (afs_select_fileserver(&fc)) {
164                         fc.cb_break = afs_calc_vnode_cb_break(dvnode);
165
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);
174                         }
175
176                         afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
177                 }
178
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);
187                         }
188                 }
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);
193         }
194
195         kfree(scb);
196         _leave(" = %d", ret);
197         return ret;
198 }
199
200 /*
201  * Remove sillyrename file on iput.
202  */
203 int afs_silly_iput(struct dentry *dentry, struct inode *inode)
204 {
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;
209
210         DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
211
212         _enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
213
214         down_read(&dvnode->rmdir_lock);
215
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;
220         }
221
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.
225                  */
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;
232                 }
233                 spin_unlock(&alias->d_lock);
234                 up_read(&dvnode->rmdir_lock);
235                 dput(alias);
236                 return ret;
237         }
238
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);
244
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;
250 }