fuse: decrement nlink on overwriting rename
authorMiklos Szeredi <mszeredi@redhat.com>
Fri, 22 Oct 2021 15:03:02 +0000 (17:03 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 22 Oct 2021 15:03:02 +0000 (17:03 +0200)
Rename didn't decrement/clear nlink on overwritten target inode.

Create a common helper fuse_entry_unlinked() that handles this for unlink,
rmdir and rename.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dir.c

index 1b345ab..dd83b5d 100644 (file)
@@ -755,6 +755,29 @@ void fuse_update_ctime(struct inode *inode)
        }
 }
 
+static void fuse_entry_unlinked(struct dentry *entry)
+{
+       struct inode *inode = d_inode(entry);
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_inode *fi = get_fuse_inode(inode);
+
+       spin_lock(&fi->lock);
+       fi->attr_version = atomic64_inc_return(&fc->attr_version);
+       /*
+        * If i_nlink == 0 then unlink doesn't make sense, yet this can
+        * happen if userspace filesystem is careless.  It would be
+        * difficult to enforce correct nlink usage so just ignore this
+        * condition here
+        */
+       if (S_ISDIR(inode->i_mode))
+               clear_nlink(inode);
+       else if (inode->i_nlink > 0)
+               drop_nlink(inode);
+       spin_unlock(&fi->lock);
+       fuse_invalidate_entry_cache(entry);
+       fuse_update_ctime(inode);
+}
+
 static int fuse_unlink(struct inode *dir, struct dentry *entry)
 {
        int err;
@@ -771,23 +794,8 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
        args.in_args[0].value = entry->d_name.name;
        err = fuse_simple_request(fm, &args);
        if (!err) {
-               struct inode *inode = d_inode(entry);
-               struct fuse_inode *fi = get_fuse_inode(inode);
-
-               spin_lock(&fi->lock);
-               fi->attr_version = atomic64_inc_return(&fm->fc->attr_version);
-               /*
-                * If i_nlink == 0 then unlink doesn't make sense, yet this can
-                * happen if userspace filesystem is careless.  It would be
-                * difficult to enforce correct nlink usage so just ignore this
-                * condition here
-                */
-               if (inode->i_nlink > 0)
-                       drop_nlink(inode);
-               spin_unlock(&fi->lock);
                fuse_dir_changed(dir);
-               fuse_invalidate_entry_cache(entry);
-               fuse_update_ctime(inode);
+               fuse_entry_unlinked(entry);
        } else if (err == -EINTR)
                fuse_invalidate_entry(entry);
        return err;
@@ -809,9 +817,8 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
        args.in_args[0].value = entry->d_name.name;
        err = fuse_simple_request(fm, &args);
        if (!err) {
-               clear_nlink(d_inode(entry));
                fuse_dir_changed(dir);
-               fuse_invalidate_entry_cache(entry);
+               fuse_entry_unlinked(entry);
        } else if (err == -EINTR)
                fuse_invalidate_entry(entry);
        return err;
@@ -851,10 +858,8 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
                        fuse_dir_changed(newdir);
 
                /* newent will end up negative */
-               if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) {
-                       fuse_invalidate_entry_cache(newent);
-                       fuse_update_ctime(d_inode(newent));
-               }
+               if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent))
+                       fuse_entry_unlinked(newent);
        } else if (err == -EINTR) {
                /* If request was interrupted, DEITY only knows if the
                   rename actually took place.  If the invalidation