All of lore.kernel.org
 help / color / mirror / Atom feed
From: Valerie Aurora <val@versity.com>
To: rpdfs-devel@lists.linux.dev
Cc: Valerie Aurora <val@versity.com>
Subject: [PATCH 3/3] rpdfs: Add rpdfs_unlink and rpdfs_rmdir
Date: Mon,  9 Mar 2026 22:04:03 +0100	[thread overview]
Message-ID: <20260309210403.105006-4-val@versity.com> (raw)
In-Reply-To: <20260309210403.105006-1-val@versity.com>

Add unlink and rmdir, including checking for an empty directory and
splitting out error checking into a proper prepare_unlink() function.

Signed-off-by: Valerie Aurora <val@versity.com>
---
 fs/rpdfs/dir.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/fs/rpdfs/dir.c b/fs/rpdfs/dir.c
index 0de00704a603..7d51c133cba0 100644
--- a/fs/rpdfs/dir.c
+++ b/fs/rpdfs/dir.c
@@ -503,6 +503,48 @@ static int create_and_instantiate_new(struct mnt_idmap *idmap, struct inode *dir
 	return ret;
 }
 
+/*
+ * The VFS did a bunch of checks before calling our unlink
+ * implementation, but some other node could have made changes between
+ * then and now. The prepare functions only succeed if the inodes have
+ * not been freed or reused, the directory entry to be removed is still
+ * there, and there is no detected corruption (which is distinct from
+ * changes made by other nodes).
+ */
+static int prepare_unlink(struct inode *dir, struct inode *inode, struct dentry *dentry)
+{
+	int ret;
+
+	/* normal failures due to other nodes making changes */
+	if (S_ISDIR(inode->i_mode)) {
+		if (!is_dir_empty(inode)) {
+			ret = -ENOTEMPTY;
+			goto out;
+		}
+	}
+
+	/* consistency/corruption checks */
+	if (S_ISDIR(inode->i_mode)) {
+		if (inode->i_nlink != 2) {
+			pr_warn("empty dir ino %lu has bad n_link %d",
+				inode->i_ino, inode->i_nlink);
+			ret = -EUCLEAN;
+			goto out;
+		}
+	} else {
+		if (inode->i_nlink < 1) {
+			pr_warn("attempting to unlink ino %lu but n_link %d is already < 1",
+				inode->i_ino, inode->i_nlink);
+			ret = -EUCLEAN;
+			goto out;
+		}
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
 static int rpdfs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry,
 			umode_t mode, bool excl)
 {
@@ -515,6 +557,62 @@ static struct dentry *rpdfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
 	return ERR_PTR(create_and_instantiate_new(idmap, dir, dentry, S_IFDIR | mode));
 }
 
+static int rpdfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct rpdfs_fs_info *rfi = RPDFS_INODE_FS(dir);
+	struct inode *inode = d_inode(dentry);
+	struct key_dent *kd = NULL;
+	DECLARE_RPDFS_TXN(txn);
+	int ret;
+
+	kd = alloc_key_dent(dentry, inode);
+	if (!kd) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* prepare all the blocks for the txn */
+	do {
+		ret = rpdfs_inode_txn_prepare(rfi, &txn, dir, RBAF_WRITE) ?:
+		      rpdfs_inode_txn_prepare(rfi, &txn, inode, RBAF_WRITE) ?:
+		      prepare_remove_entry(rfi, &txn, dir, kd) ?:
+		      prepare_unlink(dir, inode, dentry);
+	} while (rpdfs_txn_retry(rfi, &txn, &ret));
+	if (ret < 0)
+		goto out;
+
+	/* apply changes to block structures */
+	apply_remove_entry(rfi, &txn, dir, kd);
+
+	/* update link count and metadata change time */
+	drop_nlink(inode);
+	if (S_ISDIR(inode->i_mode)) {
+		drop_nlink(dir);
+		drop_nlink(inode);
+	}
+	inode_set_ctime_current(inode);
+
+	/* update parent dir size and data/metadata times */
+	update_dir_size(dir, -(dentry->d_name.len + 1));
+	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
+
+	/* update block storage of vfs inodes */
+	rpdfs_inode_txn_update(rfi, &txn, dir);
+	rpdfs_inode_txn_update(rfi, &txn, inode);
+
+	ret = 0;
+out:
+	rpdfs_txn_reset(rfi, &txn);
+	kfree(kd);
+
+	return ret;
+}
+
+static int rpdfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	return rpdfs_unlink(dir, dentry);
+}
+
 /*
  * The vfs has verified the cached directories.  Our inode refresh will
  * fail if the inodes have been reused, so we don't have to test if
@@ -714,6 +812,8 @@ const struct inode_operations rpdfs_dir_iops = {
 	.mkdir		= rpdfs_mkdir,
 	.rename		= rpdfs_rename,
 	.setattr	= rpdfs_setattr,
+	.unlink 	= rpdfs_unlink,
+	.rmdir 		= rpdfs_rmdir,
 };
 
 const struct file_operations rpdfs_dir_fops = {
-- 
2.49.0


  parent reply	other threads:[~2026-03-09 21:04 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-09 21:04 [PATCH 0/3] Add rpdfs_unlink and rpdfs_rmdir Valerie Aurora
2026-03-09 21:04 ` [PATCH 1/3] rpdfs: Initialize dirents field in new rpdfs_inode_info Valerie Aurora
2026-03-09 21:04 ` [PATCH 2/3] rpdfs: Add directory i_size accounting and helper functions Valerie Aurora
2026-03-09 21:04 ` Valerie Aurora [this message]
2026-03-10 16:34 ` [PATCH 0/3] Add rpdfs_unlink and rpdfs_rmdir Zach Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260309210403.105006-4-val@versity.com \
    --to=val@versity.com \
    --cc=rpdfs-devel@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.