[PATCH 13/20] Add ngnfs_dir_mkdir() and debugfs command

Valerie Aurora val at versity.com
Thu Jun 12 13:11:05 PDT 2025


Add ngnfs_dir_mkdir() and corresponding debugfs command. Other file
systems report a non-zero i_size for empty directories, so we also
give empty directories a non-zero file size. In our case it is 5
bytes, to match our convention for the effect on directory i_size of
real dirents of name plus null terminator as applied to "." and "..".

Signed-off-by: Valerie Aurora <val at versity.com>
---
 cli/debugfs.c  | 15 +++++++++++++++
 shared/dir.c   | 50 +++++++++++++++++++++++++++++++++++++++++++++-----
 shared/dir.h   |  2 ++
 shared/inode.c | 17 ++++++++++++++++-
 4 files changed, 78 insertions(+), 6 deletions(-)

diff --git a/cli/debugfs.c b/cli/debugfs.c
index 6bf944c..82c087d 100644
--- a/cli/debugfs.c
+++ b/cli/debugfs.c
@@ -132,6 +132,20 @@ static void cmd_lookup(struct debugfs_context *ctx, int argc, char **argv)
 	       lent.ig.gen);
 }
 
+static void cmd_mkdir(struct debugfs_context *ctx, int argc, char **argv)
+{
+	int ret;
+
+	if (argc != 2) {
+		printf("must have one, and only one, directory name to create\n");
+		return;
+	}
+
+	ret = ngnfs_dir_mkdir(ctx->nfi, &ctx->cwd_ig, 0755, argv[1], strlen(argv[1]));
+	if (ret < 0)
+		print_err("mkdir", ret);
+}
+
 static void cmd_mkfs(struct debugfs_context *ctx, int argc, char **argv)
 {
 	int ret;
@@ -281,6 +295,7 @@ static struct command {
 	{ "cd", cmd_cd, },
 	{ "create", cmd_create, },
 	{ "lookup", cmd_lookup, },
+	{ "mkdir", cmd_mkdir, },
 	{ "mkfs", cmd_mkfs, },
 	{ "quit", cmd_quit, },
 	{ "readdir", cmd_readdir, },
diff --git a/shared/dir.c b/shared/dir.c
index ac4de02..3460bf4 100644
--- a/shared/dir.c
+++ b/shared/dir.c
@@ -277,11 +277,25 @@ static int insert_dirent(struct ngnfs_fs_info *nfi, struct ngnfs_transaction *tx
 				      &last, insert_dirent_wr, da);
 }
 
+/*
+ * Prevent creation of "." and "..", with appropriate error return
+ * codes.
+ */
+static int check_create_dots(u64 hash, mode_t mode)
+{
+	if (hash >= NGNFS_DIRENT_MIN_HASH)
+		return 0;
+
+	if (S_ISDIR(mode))
+		return -EEXIST;
+	else
+		return -EISDIR;
+}
 
 /*
  * Allocate a new inode and add a directory entry referencing it.
  */
-int ngnfs_dir_create(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, umode_t mode,
+static int do_create(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, umode_t mode,
 		     char *name, size_t name_len)
 {
 	struct {
@@ -291,20 +305,33 @@ int ngnfs_dir_create(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir,
 		struct ngnfs_inode_ino_gen parent_ig;
 		struct ngnfs_inode_ino_gen ig;
 		u64 nsec;
+		int nlink;
 		struct dirent_args da;
 	} *op;
 	int ret;
 
+	if (name_len > NGNFS_NAME_MAX) {
+		ret = -ENAMETOOLONG;
+		goto out;
+	}
+
 	op = kmalloc(sizeof(*op), GFP_NOFS);
 	if (!op) {
 		ret = -ENOMEM;
 		goto out;
 	}
 
+	if (S_ISDIR(mode)) {
+		op->nlink = 2;
+		op->parent_ig = *dir;
+	} else {
+		op->nlink = 1;
+		op->parent_ig.ino = 0;
+		op->parent_ig.gen = 0;
+	}
+
 	ngnfs_txn_init(&op->txn);
-	op->parent_ig.ino = 0;
-	op->parent_ig.gen = 0;
-	init_dirent_args(&op->da, name, name_len, mode | S_IFREG);
+	init_dirent_args(&op->da, name, name_len, mode);
 
 	do {
 		op->nsec = ktime_to_ns(ktime_get_real());
@@ -312,8 +339,9 @@ int ngnfs_dir_create(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir,
 
 		ret = ngnfs_inode_get(nfi, &op->txn, NBF_WRITE, dir, &op->dir)			?:
 		      check_ifmt(op->dir.ninode, S_IFDIR, -ENOTDIR)				?:
+		      check_create_dots(op->da.hash, mode)					?:
 		      ngnfs_inode_alloc(nfi, &op->txn, &op->ig, &op->inode)			?:
-		      ngnfs_inode_init(&op->inode, &op->ig, 1, mode | S_IFREG, op->nsec,
+		      ngnfs_inode_init(&op->inode, &op->ig, op->nlink, mode, op->nsec,
 				       &op->parent_ig)						?:
 		      update_dirent_args(&op->da, &op->ig)					?:
 		      insert_dirent(nfi, &op->txn, &op->dir, &op->da)				?:
@@ -327,6 +355,18 @@ out:
 	return ret;
 }
 
+int ngnfs_dir_create(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, umode_t mode,
+		     char *name, size_t name_len)
+{
+	return do_create(nfi, dir, mode | S_IFREG, name, name_len);
+}
+
+int ngnfs_dir_mkdir(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, umode_t mode,
+		    char *name, size_t name_len)
+{
+	return do_create(nfi, dir, mode | S_IFDIR, name, name_len);
+}
+
 struct readdir_args {
 	struct ngnfs_readdir_entry *ent;
 	size_t size;
diff --git a/shared/dir.h b/shared/dir.h
index dc0ae07..366d28e 100644
--- a/shared/dir.h
+++ b/shared/dir.h
@@ -7,6 +7,8 @@
 
 int ngnfs_dir_create(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, umode_t mode,
 		     char *name, size_t name_len);
+int ngnfs_dir_mkdir(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, umode_t mode,
+		    char *name, size_t name_len);
 
 /*
  * Readdir fills the buffer with entries.  The start of the buffer must
diff --git a/shared/inode.c b/shared/inode.c
index f4a66f2..1c722a5 100644
--- a/shared/inode.c
+++ b/shared/inode.c
@@ -5,6 +5,7 @@
 #include "shared/lk/errno.h"
 #include "shared/lk/kernel.h"
 #include "shared/lk/minmax.h"
+#include "shared/lk/stat.h"
 #include "shared/lk/types.h"
 
 #include "shared/block.h"
@@ -19,15 +20,29 @@
  * to pack inode data into the rest of the block.
  */
 
+/*
+ * Most (all?) file systems have a non-zero i_size for a dir containing
+ * only "." and "..", so define one that matches how we count i_size for
+ * other dentries (name_len plus null terminator). It doesn't really
+ * matter.
+ */
+#define NGNFS_DIR_SIZE	5
+
 int ngnfs_inode_init(struct ngnfs_inode_txn_ref *itref, struct ngnfs_inode_ino_gen *ig, u32 nlink,
 		     umode_t mode, u64 nsec, struct ngnfs_inode_ino_gen *parent_ig)
 {
 	struct ngnfs_txn_block *tblk = itref->tblk;
 	struct ngnfs_inode *ninode = itref->ninode;
+	u64 i_size;
+
+	if (S_ISDIR(mode))
+		i_size = NGNFS_DIR_SIZE;
+	else
+		i_size = 0;
 
 	ngnfs_tblk_assign(tblk, ninode->ig.ino, cpu_to_le64(ig->ino));
 	ngnfs_tblk_assign(tblk, ninode->ig.gen, cpu_to_le64(ig->gen));
-	ngnfs_tblk_assign(tblk, ninode->size, 0);
+	ngnfs_tblk_assign(tblk, ninode->size, cpu_to_le64(i_size));
 	ngnfs_tblk_assign(tblk, ninode->version, cpu_to_le64(1));
 	ngnfs_tblk_assign(tblk, ninode->parent_ig.ino, cpu_to_le64(parent_ig->ino));
 	ngnfs_tblk_assign(tblk, ninode->parent_ig.gen, cpu_to_le64(parent_ig->gen));
-- 
2.49.0




More information about the ngnfs-devel mailing list