root/fs/afs/dir_silly.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. afs_do_silly_rename
  2. afs_sillyrename
  3. afs_do_silly_unlink
  4. afs_silly_iput

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

/* [<][>][^][v][top][bottom][index][help] */