NFSv4: bump/drop the nlink count on the parent dir when we mkdir/rmdir
authorTrond Myklebust <trondmy@gmail.com>
Tue, 31 Jul 2018 19:54:10 +0000 (15:54 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 8 Aug 2018 20:50:01 +0000 (16:50 -0400)
Ensure that we always bump or drop the nlink count on the parent directory
when we do a mkdir or a rmdir(). This needs to be done by hand as we don't
have pre/post op attributes.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/nfs4proc.c

index 914f52f4ab0853fa36e7ae2167498277251a9b64..6b8822192397df672872af380564697ffed2bce9 100644 (file)
@@ -1078,12 +1078,26 @@ int nfs4_call_sync(struct rpc_clnt *clnt,
        return nfs4_call_sync_sequence(clnt, server, msg, args, res);
 }
 
-static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
+static void
+nfs4_inc_nlink_locked(struct inode *inode)
+{
+       NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER;
+       inc_nlink(inode);
+}
+
+static void
+nfs4_dec_nlink_locked(struct inode *inode)
+{
+       NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER;
+       drop_nlink(inode);
+}
+
+static void
+update_changeattr_locked(struct inode *dir, struct nfs4_change_info *cinfo,
                unsigned long timestamp)
 {
        struct nfs_inode *nfsi = NFS_I(dir);
 
-       spin_lock(&dir->i_lock);
        nfsi->cache_validity |= NFS_INO_INVALID_CTIME
                | NFS_INO_INVALID_MTIME
                | NFS_INO_INVALID_DATA;
@@ -1100,6 +1114,14 @@ static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
        nfsi->read_cache_jiffies = timestamp;
        nfsi->attr_gencount = nfs_inc_attr_generation_counter();
        nfs_fscache_invalidate(dir);
+}
+
+static void
+update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
+               unsigned long timestamp)
+{
+       spin_lock(&dir->i_lock);
+       update_changeattr_locked(dir, cinfo, timestamp);
        spin_unlock(&dir->i_lock);
 }
 
@@ -4248,7 +4270,8 @@ out:
        return status;
 }
 
-static int _nfs4_proc_remove(struct inode *dir, const struct qstr *name)
+static int
+_nfs4_proc_remove(struct inode *dir, const struct qstr *name, u32 ftype)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs_removeargs args = {
@@ -4267,8 +4290,14 @@ static int _nfs4_proc_remove(struct inode *dir, const struct qstr *name)
        int status;
 
        status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
-       if (status == 0)
-               update_changeattr(dir, &res.cinfo, timestamp);
+       if (status == 0) {
+               spin_lock(&dir->i_lock);
+               update_changeattr_locked(dir, &res.cinfo, timestamp);
+               /* Removing a directory decrements nlink in the parent */
+               if (ftype == NF4DIR && dir->i_nlink > 2)
+                       nfs4_dec_nlink_locked(dir);
+               spin_unlock(&dir->i_lock);
+       }
        return status;
 }
 
@@ -4285,7 +4314,7 @@ static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)
                        nfs4_inode_make_writeable(inode);
        }
        do {
-               err = _nfs4_proc_remove(dir, &dentry->d_name);
+               err = _nfs4_proc_remove(dir, &dentry->d_name, NF4REG);
                trace_nfs4_remove(dir, &dentry->d_name, err);
                err = nfs4_handle_exception(NFS_SERVER(dir), err,
                                &exception);
@@ -4299,7 +4328,7 @@ static int nfs4_proc_rmdir(struct inode *dir, const struct qstr *name)
        int err;
 
        do {
-               err = _nfs4_proc_remove(dir, name);
+               err = _nfs4_proc_remove(dir, name, NF4DIR);
                trace_nfs4_remove(dir, name, err);
                err = nfs4_handle_exception(NFS_SERVER(dir), err,
                                &exception);
@@ -4503,8 +4532,13 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_
        int status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg,
                                    &data->arg.seq_args, &data->res.seq_res, 1);
        if (status == 0) {
-               update_changeattr(dir, &data->res.dir_cinfo,
+               spin_lock(&dir->i_lock);
+               update_changeattr_locked(dir, &data->res.dir_cinfo,
                                data->res.fattr->time_start);
+               /* Creating a directory bumps nlink in the parent */
+               if (data->arg.ftype == NF4DIR)
+                       nfs4_inc_nlink_locked(dir);
+               spin_unlock(&dir->i_lock);
                status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->res.label);
        }
        return status;