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