[PATCH 2/3] rpdfs: Add directory i_size accounting and helper functions

Valerie Aurora <[email protected]> Mon, 9 Mar 2026 22:04:02 +0100
Newsgroups dev.linux.lists.rpdfs-devel
Message-ID <[email protected]>
Implement directory i_size as the total length of all directory entry
names including a null termination.

Signed-off-by: Valerie Aurora <[email protected]>
---
 fs/rpdfs/dir.c          | 48 ++++++++++++++++++++++++++++++++++-------
 fs/rpdfs/format-block.h |  8 +++++++
 fs/rpdfs/mkfs.c         |  2 +-
 3 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/fs/rpdfs/dir.c b/fs/rpdfs/dir.c
index 7ae8faf74161..0de00704a603 100644
--- a/fs/rpdfs/dir.c
+++ b/fs/rpdfs/dir.c
@@ -363,6 +363,26 @@ static struct key_dent *alloc_key_dent(struct dentry *dentry, struct inode *inod
 	return kd;
 }
 
+static void init_dir_size(struct inode *inode)
+{
+	i_size_write(inode, RPDFS_EMPTY_DIR_LEN);
+}
+
+static int is_dir_empty(struct inode *inode)
+{
+	return (i_size_read(inode) == RPDFS_EMPTY_DIR_LEN);
+}
+
+/*
+ * Helper function for readability. All consistency/corruption checks
+ * should happen in the prepare phase, so there are no checks when
+ * making changes.
+ */
+static void update_dir_size(struct inode *inode, s32 len)
+{
+	i_size_write(inode, i_size_read(inode) + len);
+}
+
 /*
  * Allocate an inode in a block transaction and return an allocated vfs
  * inode at its ino/gen position.  Like _iget, this inserts an I_NEW
@@ -429,17 +449,21 @@ static struct inode *create_new_inode(struct mnt_idmap *idmap, struct inode *dir
 
 	/* update vfs inodes */
 	inode_init_owner(idmap, inode, dir, mode);
-	if (S_ISDIR(mode))
+	if (S_ISDIR(mode)) {
 		set_nlink(inode, 2);
-	else
+		init_dir_size(inode);
+	} else {
 		set_nlink(inode, 1);
+	}
+
 	rpdfs_inode_init_ops(inode);
 
+	/* update parent vfs inode and apply changes to referenced blocks */
 	if (S_ISDIR(mode))
 		inc_nlink(dir);
 
-	/* apply changes to referenced blocks */
 	if (kd) {
+		update_dir_size(dir, kd->dent.name_len + 1);
 		kd->dent.ig = ig;
 		apply_add_entry(rfi, &txn, dir, kd);
 	}
@@ -532,7 +556,7 @@ static int rpdfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct d
 		goto out;
 	}
 
-	/* prepare all the blocks for in the txn */
+	/* prepare all the blocks for the txn */
 	do {
 		ret = rpdfs_inode_txn_prepare(rfi, &txn, old_dir, RBAF_WRITE) ?:
 		      rpdfs_inode_txn_prepare(rfi, &txn, old_inode, RBAF_WRITE) ?:
@@ -551,6 +575,11 @@ static int rpdfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct d
 	if (ret < 0)
 		goto out;
 
+	/*
+	 * TODO: walk ancestors, and if there is a directory to be
+	 * deleted, check if it is empty.
+	 */
+
 	/* apply changes to block structures */
 	apply_remove_entry(rfi, &txn, old_dir, old_kd);
 	if (new_inode)
@@ -558,11 +587,12 @@ static int rpdfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct d
 	else
 		apply_add_entry(rfi, &txn, new_dir, new_kd);
 
-	/* update vfs inodes: first dir sizes .. */
-	i_size_write(old_dir, i_size_read(old_dir) - old_dentry->d_name.len);
+	/* update dir sizes */
+	update_dir_size(old_dir, -(old_dentry->d_name.len + 1));
 	if (!new_inode)
-               i_size_write(new_dir, i_size_read(new_dir) + new_dentry->d_name.len);
-	/* .. then link counts .. */
+		update_dir_size(new_dir, new_dentry->d_name.len + 1);
+
+	/* and link counts */
 	if (new_inode) {
 		drop_nlink(new_inode);
 		if (S_ISDIR(new_inode->i_mode)) {
@@ -570,10 +600,12 @@ static int rpdfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct d
 			drop_nlink(new_inode);
 		}
 	}
+
 	if (S_ISDIR(old_inode->i_mode) && (old_dir != new_dir)) {
 		drop_nlink(old_dir);
 		inc_nlink(new_dir);
 	}
+
 	/* .. and finally times */
 	now = inode_set_ctime_current(old_dir);
 	inode_set_mtime_to_ts(old_dir, now);
diff --git a/fs/rpdfs/format-block.h b/fs/rpdfs/format-block.h
index 1ff315668d2f..d4a0fb78d670 100644
--- a/fs/rpdfs/format-block.h
+++ b/fs/rpdfs/format-block.h
@@ -210,6 +210,14 @@ struct rpdfs_dirent {
 #define RPDFS_DIRENT_DOT_DOT_HASH	1ULL
 #define RPDFS_DIRENT_MIN_HASH		2ULL
 
+/*
+ * An empty dir contains pseudo entries for "." and "..". The reported
+ * size of directory is the length of the null-terminated names of all
+ * the directory entries. (The actual size is the number of blocks
+ * necessary to store the dirents btree.)
+ */
+#define RPDFS_EMPTY_DIR_LEN	5
+
 /*
  * xattrs are currently implemented as btree items, whose keys are the
  * hash of the name combined with the xattr_create_counter value in the
diff --git a/fs/rpdfs/mkfs.c b/fs/rpdfs/mkfs.c
index f4cbf5538b3b..94798799c960 100644
--- a/fs/rpdfs/mkfs.c
+++ b/fs/rpdfs/mkfs.c
@@ -45,7 +45,7 @@ int rpdfs_mkfs(struct rpdfs_fs_info *rfi)
 
 	rinode->ig.ino = cpu_to_le64(RPDFS_ROOT_INO);
 	rinode->ig.gen = cpu_to_le64(RPDFS_ROOT_GEN);
-	rinode->size = cpu_to_le64(5); /* name lens of . and .. with null term */
+	rinode->size = cpu_to_le64(RPDFS_EMPTY_DIR_LEN);
 	rinode->version = cpu_to_le64(1);
 	rinode->nlink = cpu_to_le32(2);
 	rinode->mode = cpu_to_le32(S_IFDIR | 0755);
-- 
2.49.0