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 static void afs_silly_rename_success(struct afs_operation *op) 16 { 17 _enter("op=%08x", op->debug_id); 18 19 afs_check_dir_conflict(op, &op->file[0]); 20 afs_vnode_commit_status(op, &op->file[0]); 21 } 22 23 static void afs_silly_rename_edit_dir(struct afs_operation *op) 24 { 25 struct afs_vnode_param *dvp = &op->file[0]; 26 struct afs_vnode *dvnode = dvp->vnode; 27 struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry)); 28 struct dentry *old = op->dentry; 29 struct dentry *new = op->dentry_2; 30 31 spin_lock(&old->d_lock); 32 old->d_flags |= DCACHE_NFSFS_RENAMED; 33 spin_unlock(&old->d_lock); 34 if (dvnode->silly_key != op->key) { 35 key_put(dvnode->silly_key); 36 dvnode->silly_key = key_get(op->key); 37 } 38 39 down_write(&dvnode->validate_lock); 40 if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && 41 dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) { 42 afs_edit_dir_remove(dvnode, &old->d_name, 43 afs_edit_dir_for_silly_0); 44 afs_edit_dir_add(dvnode, &new->d_name, 45 &vnode->fid, afs_edit_dir_for_silly_1); 46 } 47 up_write(&dvnode->validate_lock); 48 } 49 50 static const struct afs_operation_ops afs_silly_rename_operation = { 51 .issue_afs_rpc = afs_fs_rename, 52 .issue_yfs_rpc = yfs_fs_rename, 53 .success = afs_silly_rename_success, 54 .edit_dir = afs_silly_rename_edit_dir, 55 }; 56 57 /* 58 * Actually perform the silly rename step. 59 */ 60 static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode, 61 struct dentry *old, struct dentry *new, 62 struct key *key) 63 { 64 struct afs_operation *op; 65 66 _enter("%pd,%pd", old, new); 67 68 op = afs_alloc_operation(key, dvnode->volume); 69 if (IS_ERR(op)) 70 return PTR_ERR(op); 71 72 afs_op_set_vnode(op, 0, dvnode); 73 afs_op_set_vnode(op, 1, dvnode); 74 op->file[0].dv_delta = 1; 75 op->file[1].dv_delta = 1; 76 op->file[0].update_ctime = true; 77 op->file[1].update_ctime = true; 78 79 op->dentry = old; 80 op->dentry_2 = new; 81 op->ops = &afs_silly_rename_operation; 82 83 trace_afs_silly_rename(vnode, false); 84 return afs_do_sync_operation(op); 85 } 86 87 /** 88 * afs_sillyrename - Perform a silly-rename of a dentry 89 * 90 * AFS is stateless and the server doesn't know when the client is holding a 91 * file open. To prevent application problems when a file is unlinked while 92 * it's still open, the client performs a "silly-rename". That is, it renames 93 * the file to a hidden file in the same directory, and only performs the 94 * unlink once the last reference to it is put. 95 * 96 * The final cleanup is done during dentry_iput. 97 */ 98 int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode, 99 struct dentry *dentry, struct key *key) 100 { 101 static unsigned int sillycounter; 102 struct dentry *sdentry = NULL; 103 unsigned char silly[16]; 104 int ret = -EBUSY; 105 106 _enter(""); 107 108 /* We don't allow a dentry to be silly-renamed twice. */ 109 if (dentry->d_flags & DCACHE_NFSFS_RENAMED) 110 return -EBUSY; 111 112 sdentry = NULL; 113 do { 114 int slen; 115 116 dput(sdentry); 117 sillycounter++; 118 119 /* Create a silly name. Note that the ".__afs" prefix is 120 * understood by the salvager and must not be changed. 121 */ 122 slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter); 123 sdentry = lookup_one_len(silly, dentry->d_parent, slen); 124 125 /* N.B. Better to return EBUSY here ... it could be dangerous 126 * to delete the file while it's in use. 127 */ 128 if (IS_ERR(sdentry)) 129 goto out; 130 } while (!d_is_negative(sdentry)); 131 132 ihold(&vnode->vfs_inode); 133 134 ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key); 135 switch (ret) { 136 case 0: 137 /* The rename succeeded. */ 138 set_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags); 139 d_move(dentry, sdentry); 140 break; 141 case -ERESTARTSYS: 142 /* The result of the rename is unknown. Play it safe by forcing 143 * a new lookup. 144 */ 145 d_drop(dentry); 146 d_drop(sdentry); 147 } 148 149 iput(&vnode->vfs_inode); 150 dput(sdentry); 151 out: 152 _leave(" = %d", ret); 153 return ret; 154 } 155 156 static void afs_silly_unlink_success(struct afs_operation *op) 157 { 158 _enter("op=%08x", op->debug_id); 159 afs_check_dir_conflict(op, &op->file[0]); 160 afs_vnode_commit_status(op, &op->file[0]); 161 afs_vnode_commit_status(op, &op->file[1]); 162 afs_update_dentry_version(op, &op->file[0], op->dentry); 163 } 164 165 static void afs_silly_unlink_edit_dir(struct afs_operation *op) 166 { 167 struct afs_vnode_param *dvp = &op->file[0]; 168 struct afs_vnode *dvnode = dvp->vnode; 169 170 _enter("op=%08x", op->debug_id); 171 down_write(&dvnode->validate_lock); 172 if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && 173 dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) 174 afs_edit_dir_remove(dvnode, &op->dentry->d_name, 175 afs_edit_dir_for_unlink); 176 up_write(&dvnode->validate_lock); 177 } 178 179 static const struct afs_operation_ops afs_silly_unlink_operation = { 180 .issue_afs_rpc = afs_fs_remove_file, 181 .issue_yfs_rpc = yfs_fs_remove_file, 182 .success = afs_silly_unlink_success, 183 .aborted = afs_check_for_remote_deletion, 184 .edit_dir = afs_silly_unlink_edit_dir, 185 }; 186 187 /* 188 * Tell the server to remove a sillyrename file. 189 */ 190 static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode, 191 struct dentry *dentry, struct key *key) 192 { 193 struct afs_operation *op; 194 195 _enter(""); 196 197 op = afs_alloc_operation(NULL, dvnode->volume); 198 if (IS_ERR(op)) 199 return PTR_ERR(op); 200 201 afs_op_set_vnode(op, 0, dvnode); 202 afs_op_set_vnode(op, 1, vnode); 203 op->file[0].dv_delta = 1; 204 op->file[0].update_ctime = true; 205 op->file[1].op_unlinked = true; 206 op->file[1].update_ctime = true; 207 208 op->dentry = dentry; 209 op->ops = &afs_silly_unlink_operation; 210 211 trace_afs_silly_rename(vnode, true); 212 afs_begin_vnode_operation(op); 213 afs_wait_for_operation(op); 214 215 /* If there was a conflict with a third party, check the status of the 216 * unlinked vnode. 217 */ 218 if (op->error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) { 219 op->file[1].update_ctime = false; 220 op->fetch_status.which = 1; 221 op->ops = &afs_fetch_status_operation; 222 afs_begin_vnode_operation(op); 223 afs_wait_for_operation(op); 224 } 225 226 return afs_put_operation(op); 227 } 228 229 /* 230 * Remove sillyrename file on iput. 231 */ 232 int afs_silly_iput(struct dentry *dentry, struct inode *inode) 233 { 234 struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent)); 235 struct afs_vnode *vnode = AFS_FS_I(inode); 236 struct dentry *alias; 237 int ret; 238 239 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); 240 241 _enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode); 242 243 down_read(&dvnode->rmdir_lock); 244 245 alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq); 246 if (IS_ERR(alias)) { 247 up_read(&dvnode->rmdir_lock); 248 return 0; 249 } 250 251 if (!d_in_lookup(alias)) { 252 /* We raced with lookup... See if we need to transfer the 253 * sillyrename information to the aliased dentry. 254 */ 255 ret = 0; 256 spin_lock(&alias->d_lock); 257 if (d_really_is_positive(alias) && 258 !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { 259 alias->d_flags |= DCACHE_NFSFS_RENAMED; 260 ret = 1; 261 } 262 spin_unlock(&alias->d_lock); 263 up_read(&dvnode->rmdir_lock); 264 dput(alias); 265 return ret; 266 } 267 268 /* Stop lock-release from complaining. */ 269 spin_lock(&vnode->lock); 270 vnode->lock_state = AFS_VNODE_LOCK_DELETED; 271 trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0); 272 spin_unlock(&vnode->lock); 273 274 afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key); 275 up_read(&dvnode->rmdir_lock); 276 d_lookup_done(alias); 277 dput(alias); 278 return 1; 279 } 280