Commit cefd1b83 authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

fuse: decrement nlink on overwriting rename



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: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 84840efc
Loading
Loading
Loading
Loading
+27 −22
Original line number Diff line number Diff line
@@ -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