Linux-libre 5.3.12-gnu
[librecmc/linux-libre.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
65         kfree(scb);
66         _leave(" = %d", ret);
67         return ret;
68 }
69
70 /**
71  * afs_sillyrename - Perform a silly-rename of a dentry
72  *
73  * AFS is stateless and the server doesn't know when the client is holding a
74  * file open.  To prevent application problems when a file is unlinked while
75  * it's still open, the client performs a "silly-rename".  That is, it renames
76  * the file to a hidden file in the same directory, and only performs the
77  * unlink once the last reference to it is put.
78  *
79  * The final cleanup is done during dentry_iput.
80  */
81 int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
82                     struct dentry *dentry, struct key *key)
83 {
84         static unsigned int sillycounter;
85         struct dentry *sdentry = NULL;
86         unsigned char silly[16];
87         int ret = -EBUSY;
88
89         _enter("");
90
91         /* We don't allow a dentry to be silly-renamed twice. */
92         if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
93                 return -EBUSY;
94
95         sdentry = NULL;
96         do {
97                 int slen;
98
99                 dput(sdentry);
100                 sillycounter++;
101
102                 /* Create a silly name.  Note that the ".__afs" prefix is
103                  * understood by the salvager and must not be changed.
104                  */
105                 slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter);
106                 sdentry = lookup_one_len(silly, dentry->d_parent, slen);
107
108                 /* N.B. Better to return EBUSY here ... it could be dangerous
109                  * to delete the file while it's in use.
110                  */
111                 if (IS_ERR(sdentry))
112                         goto out;
113         } while (!d_is_negative(sdentry));
114
115         ihold(&vnode->vfs_inode);
116
117         ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key);
118         switch (ret) {
119         case 0:
120                 /* The rename succeeded. */
121                 d_move(dentry, sdentry);
122                 break;
123         case -ERESTARTSYS:
124                 /* The result of the rename is unknown. Play it safe by forcing
125                  * a new lookup.
126                  */
127                 d_drop(dentry);
128                 d_drop(sdentry);
129         }
130
131         iput(&vnode->vfs_inode);
132         dput(sdentry);
133 out:
134         _leave(" = %d", ret);
135         return ret;
136 }
137
138 /*
139  * Tell the server to remove a sillyrename file.
140  */
141 static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode,
142                                struct dentry *dentry, struct key *key)
143 {
144         struct afs_fs_cursor fc;
145         struct afs_status_cb *scb;
146         int ret = -ERESTARTSYS;
147
148         _enter("");
149
150         scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
151         if (!scb)
152                 return -ENOMEM;
153
154         trace_afs_silly_rename(vnode, true);
155         if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
156                 afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
157
158                 while (afs_select_fileserver(&fc)) {
159                         fc.cb_break = afs_calc_vnode_cb_break(dvnode);
160
161                         if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
162                             !test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
163                                 yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
164                                                     &scb[0], &scb[1]);
165                                 if (fc.ac.error != -ECONNABORTED ||
166                                     fc.ac.abort_code != RXGEN_OPCODE)
167                                         continue;
168                                 set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
169                         }
170
171                         afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
172                 }
173
174                 afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
175                                         &dir_data_version, &scb[0]);
176                 ret = afs_end_vnode_operation(&fc);
177                 if (ret == 0) {
178                         drop_nlink(&vnode->vfs_inode);
179                         if (vnode->vfs_inode.i_nlink == 0) {
180                                 set_bit(AFS_VNODE_DELETED, &vnode->flags);
181                                 clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
182                         }
183                 }
184                 if (ret == 0 &&
185                     test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
186                         afs_edit_dir_remove(dvnode, &dentry->d_name,
187                                             afs_edit_dir_for_unlink);
188         }
189
190         kfree(scb);
191         _leave(" = %d", ret);
192         return ret;
193 }
194
195 /*
196  * Remove sillyrename file on iput.
197  */
198 int afs_silly_iput(struct dentry *dentry, struct inode *inode)
199 {
200         struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent));
201         struct afs_vnode *vnode = AFS_FS_I(inode);
202         struct dentry *alias;
203         int ret;
204
205         DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
206
207         _enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
208
209         down_read(&dvnode->rmdir_lock);
210
211         alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq);
212         if (IS_ERR(alias)) {
213                 up_read(&dvnode->rmdir_lock);
214                 return 0;
215         }
216
217         if (!d_in_lookup(alias)) {
218                 /* We raced with lookup...  See if we need to transfer the
219                  * sillyrename information to the aliased dentry.
220                  */
221                 ret = 0;
222                 spin_lock(&alias->d_lock);
223                 if (d_really_is_positive(alias) &&
224                     !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
225                         alias->d_flags |= DCACHE_NFSFS_RENAMED;
226                         ret = 1;
227                 }
228                 spin_unlock(&alias->d_lock);
229                 up_read(&dvnode->rmdir_lock);
230                 dput(alias);
231                 return ret;
232         }
233
234         /* Stop lock-release from complaining. */
235         spin_lock(&vnode->lock);
236         vnode->lock_state = AFS_VNODE_LOCK_DELETED;
237         trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0);
238         spin_unlock(&vnode->lock);
239
240         afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key);
241         up_read(&dvnode->rmdir_lock);
242         d_lookup_done(alias);
243         dput(alias);
244         return 1;
245 }