18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* AFS silly rename handling 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci * - Derived from NFS's sillyrename. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/namei.h> 128c2ecf20Sopenharmony_ci#include <linux/fsnotify.h> 138c2ecf20Sopenharmony_ci#include "internal.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic void afs_silly_rename_success(struct afs_operation *op) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci _enter("op=%08x", op->debug_id); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci afs_check_dir_conflict(op, &op->file[0]); 208c2ecf20Sopenharmony_ci afs_vnode_commit_status(op, &op->file[0]); 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void afs_silly_rename_edit_dir(struct afs_operation *op) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct afs_vnode_param *dvp = &op->file[0]; 268c2ecf20Sopenharmony_ci struct afs_vnode *dvnode = dvp->vnode; 278c2ecf20Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry)); 288c2ecf20Sopenharmony_ci struct dentry *old = op->dentry; 298c2ecf20Sopenharmony_ci struct dentry *new = op->dentry_2; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci spin_lock(&old->d_lock); 328c2ecf20Sopenharmony_ci old->d_flags |= DCACHE_NFSFS_RENAMED; 338c2ecf20Sopenharmony_ci spin_unlock(&old->d_lock); 348c2ecf20Sopenharmony_ci if (dvnode->silly_key != op->key) { 358c2ecf20Sopenharmony_ci key_put(dvnode->silly_key); 368c2ecf20Sopenharmony_ci dvnode->silly_key = key_get(op->key); 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci down_write(&dvnode->validate_lock); 408c2ecf20Sopenharmony_ci if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && 418c2ecf20Sopenharmony_ci dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) { 428c2ecf20Sopenharmony_ci afs_edit_dir_remove(dvnode, &old->d_name, 438c2ecf20Sopenharmony_ci afs_edit_dir_for_silly_0); 448c2ecf20Sopenharmony_ci afs_edit_dir_add(dvnode, &new->d_name, 458c2ecf20Sopenharmony_ci &vnode->fid, afs_edit_dir_for_silly_1); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci up_write(&dvnode->validate_lock); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const struct afs_operation_ops afs_silly_rename_operation = { 518c2ecf20Sopenharmony_ci .issue_afs_rpc = afs_fs_rename, 528c2ecf20Sopenharmony_ci .issue_yfs_rpc = yfs_fs_rename, 538c2ecf20Sopenharmony_ci .success = afs_silly_rename_success, 548c2ecf20Sopenharmony_ci .edit_dir = afs_silly_rename_edit_dir, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * Actually perform the silly rename step. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode, 618c2ecf20Sopenharmony_ci struct dentry *old, struct dentry *new, 628c2ecf20Sopenharmony_ci struct key *key) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct afs_operation *op; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci _enter("%pd,%pd", old, new); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci op = afs_alloc_operation(key, dvnode->volume); 698c2ecf20Sopenharmony_ci if (IS_ERR(op)) 708c2ecf20Sopenharmony_ci return PTR_ERR(op); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci afs_op_set_vnode(op, 0, dvnode); 738c2ecf20Sopenharmony_ci afs_op_set_vnode(op, 1, dvnode); 748c2ecf20Sopenharmony_ci op->file[0].dv_delta = 1; 758c2ecf20Sopenharmony_ci op->file[1].dv_delta = 1; 768c2ecf20Sopenharmony_ci op->file[0].modification = true; 778c2ecf20Sopenharmony_ci op->file[1].modification = true; 788c2ecf20Sopenharmony_ci op->file[0].update_ctime = true; 798c2ecf20Sopenharmony_ci op->file[1].update_ctime = true; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci op->dentry = old; 828c2ecf20Sopenharmony_ci op->dentry_2 = new; 838c2ecf20Sopenharmony_ci op->ops = &afs_silly_rename_operation; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci trace_afs_silly_rename(vnode, false); 868c2ecf20Sopenharmony_ci return afs_do_sync_operation(op); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/** 908c2ecf20Sopenharmony_ci * afs_sillyrename - Perform a silly-rename of a dentry 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * AFS is stateless and the server doesn't know when the client is holding a 938c2ecf20Sopenharmony_ci * file open. To prevent application problems when a file is unlinked while 948c2ecf20Sopenharmony_ci * it's still open, the client performs a "silly-rename". That is, it renames 958c2ecf20Sopenharmony_ci * the file to a hidden file in the same directory, and only performs the 968c2ecf20Sopenharmony_ci * unlink once the last reference to it is put. 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * The final cleanup is done during dentry_iput. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ciint afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode, 1018c2ecf20Sopenharmony_ci struct dentry *dentry, struct key *key) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci static unsigned int sillycounter; 1048c2ecf20Sopenharmony_ci struct dentry *sdentry = NULL; 1058c2ecf20Sopenharmony_ci unsigned char silly[16]; 1068c2ecf20Sopenharmony_ci int ret = -EBUSY; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci _enter(""); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* We don't allow a dentry to be silly-renamed twice. */ 1118c2ecf20Sopenharmony_ci if (dentry->d_flags & DCACHE_NFSFS_RENAMED) 1128c2ecf20Sopenharmony_ci return -EBUSY; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci sdentry = NULL; 1158c2ecf20Sopenharmony_ci do { 1168c2ecf20Sopenharmony_ci int slen; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci dput(sdentry); 1198c2ecf20Sopenharmony_ci sillycounter++; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Create a silly name. Note that the ".__afs" prefix is 1228c2ecf20Sopenharmony_ci * understood by the salvager and must not be changed. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci slen = scnprintf(silly, sizeof(silly), ".__afs%04X", sillycounter); 1258c2ecf20Sopenharmony_ci sdentry = lookup_one_len(silly, dentry->d_parent, slen); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* N.B. Better to return EBUSY here ... it could be dangerous 1288c2ecf20Sopenharmony_ci * to delete the file while it's in use. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci if (IS_ERR(sdentry)) 1318c2ecf20Sopenharmony_ci goto out; 1328c2ecf20Sopenharmony_ci } while (!d_is_negative(sdentry)); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ihold(&vnode->vfs_inode); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = afs_do_silly_rename(dvnode, vnode, dentry, sdentry, key); 1378c2ecf20Sopenharmony_ci switch (ret) { 1388c2ecf20Sopenharmony_ci case 0: 1398c2ecf20Sopenharmony_ci /* The rename succeeded. */ 1408c2ecf20Sopenharmony_ci set_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags); 1418c2ecf20Sopenharmony_ci d_move(dentry, sdentry); 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci case -ERESTARTSYS: 1448c2ecf20Sopenharmony_ci /* The result of the rename is unknown. Play it safe by forcing 1458c2ecf20Sopenharmony_ci * a new lookup. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci d_drop(dentry); 1488c2ecf20Sopenharmony_ci d_drop(sdentry); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci iput(&vnode->vfs_inode); 1528c2ecf20Sopenharmony_ci dput(sdentry); 1538c2ecf20Sopenharmony_ciout: 1548c2ecf20Sopenharmony_ci _leave(" = %d", ret); 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void afs_silly_unlink_success(struct afs_operation *op) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci _enter("op=%08x", op->debug_id); 1618c2ecf20Sopenharmony_ci afs_check_dir_conflict(op, &op->file[0]); 1628c2ecf20Sopenharmony_ci afs_vnode_commit_status(op, &op->file[0]); 1638c2ecf20Sopenharmony_ci afs_vnode_commit_status(op, &op->file[1]); 1648c2ecf20Sopenharmony_ci afs_update_dentry_version(op, &op->file[0], op->dentry); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void afs_silly_unlink_edit_dir(struct afs_operation *op) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct afs_vnode_param *dvp = &op->file[0]; 1708c2ecf20Sopenharmony_ci struct afs_vnode *dvnode = dvp->vnode; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci _enter("op=%08x", op->debug_id); 1738c2ecf20Sopenharmony_ci down_write(&dvnode->validate_lock); 1748c2ecf20Sopenharmony_ci if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) && 1758c2ecf20Sopenharmony_ci dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) 1768c2ecf20Sopenharmony_ci afs_edit_dir_remove(dvnode, &op->dentry->d_name, 1778c2ecf20Sopenharmony_ci afs_edit_dir_for_unlink); 1788c2ecf20Sopenharmony_ci up_write(&dvnode->validate_lock); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const struct afs_operation_ops afs_silly_unlink_operation = { 1828c2ecf20Sopenharmony_ci .issue_afs_rpc = afs_fs_remove_file, 1838c2ecf20Sopenharmony_ci .issue_yfs_rpc = yfs_fs_remove_file, 1848c2ecf20Sopenharmony_ci .success = afs_silly_unlink_success, 1858c2ecf20Sopenharmony_ci .aborted = afs_check_for_remote_deletion, 1868c2ecf20Sopenharmony_ci .edit_dir = afs_silly_unlink_edit_dir, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Tell the server to remove a sillyrename file. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode, 1938c2ecf20Sopenharmony_ci struct dentry *dentry, struct key *key) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct afs_operation *op; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci _enter(""); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci op = afs_alloc_operation(NULL, dvnode->volume); 2008c2ecf20Sopenharmony_ci if (IS_ERR(op)) 2018c2ecf20Sopenharmony_ci return PTR_ERR(op); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci afs_op_set_vnode(op, 0, dvnode); 2048c2ecf20Sopenharmony_ci afs_op_set_vnode(op, 1, vnode); 2058c2ecf20Sopenharmony_ci op->file[0].dv_delta = 1; 2068c2ecf20Sopenharmony_ci op->file[0].modification = true; 2078c2ecf20Sopenharmony_ci op->file[0].update_ctime = true; 2088c2ecf20Sopenharmony_ci op->file[1].op_unlinked = true; 2098c2ecf20Sopenharmony_ci op->file[1].update_ctime = true; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci op->dentry = dentry; 2128c2ecf20Sopenharmony_ci op->ops = &afs_silly_unlink_operation; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci trace_afs_silly_rename(vnode, true); 2158c2ecf20Sopenharmony_ci afs_begin_vnode_operation(op); 2168c2ecf20Sopenharmony_ci afs_wait_for_operation(op); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* If there was a conflict with a third party, check the status of the 2198c2ecf20Sopenharmony_ci * unlinked vnode. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci if (op->error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) { 2228c2ecf20Sopenharmony_ci op->file[1].update_ctime = false; 2238c2ecf20Sopenharmony_ci op->fetch_status.which = 1; 2248c2ecf20Sopenharmony_ci op->ops = &afs_fetch_status_operation; 2258c2ecf20Sopenharmony_ci afs_begin_vnode_operation(op); 2268c2ecf20Sopenharmony_ci afs_wait_for_operation(op); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return afs_put_operation(op); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * Remove sillyrename file on iput. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ciint afs_silly_iput(struct dentry *dentry, struct inode *inode) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct afs_vnode *dvnode = AFS_FS_I(d_inode(dentry->d_parent)); 2388c2ecf20Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 2398c2ecf20Sopenharmony_ci struct dentry *alias; 2408c2ecf20Sopenharmony_ci int ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci _enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci down_read(&dvnode->rmdir_lock); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq); 2498c2ecf20Sopenharmony_ci if (IS_ERR(alias)) { 2508c2ecf20Sopenharmony_ci up_read(&dvnode->rmdir_lock); 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!d_in_lookup(alias)) { 2558c2ecf20Sopenharmony_ci /* We raced with lookup... See if we need to transfer the 2568c2ecf20Sopenharmony_ci * sillyrename information to the aliased dentry. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci ret = 0; 2598c2ecf20Sopenharmony_ci spin_lock(&alias->d_lock); 2608c2ecf20Sopenharmony_ci if (d_really_is_positive(alias) && 2618c2ecf20Sopenharmony_ci !(alias->d_flags & DCACHE_NFSFS_RENAMED)) { 2628c2ecf20Sopenharmony_ci alias->d_flags |= DCACHE_NFSFS_RENAMED; 2638c2ecf20Sopenharmony_ci ret = 1; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci spin_unlock(&alias->d_lock); 2668c2ecf20Sopenharmony_ci up_read(&dvnode->rmdir_lock); 2678c2ecf20Sopenharmony_ci dput(alias); 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Stop lock-release from complaining. */ 2728c2ecf20Sopenharmony_ci spin_lock(&vnode->lock); 2738c2ecf20Sopenharmony_ci vnode->lock_state = AFS_VNODE_LOCK_DELETED; 2748c2ecf20Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_silly_delete, 0); 2758c2ecf20Sopenharmony_ci spin_unlock(&vnode->lock); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci afs_do_silly_unlink(dvnode, vnode, dentry, dvnode->silly_key); 2788c2ecf20Sopenharmony_ci up_read(&dvnode->rmdir_lock); 2798c2ecf20Sopenharmony_ci d_lookup_done(alias); 2808c2ecf20Sopenharmony_ci dput(alias); 2818c2ecf20Sopenharmony_ci return 1; 2828c2ecf20Sopenharmony_ci} 283