[PATCH 12/15] Add ngnfs_dir_unlink(), ngfs_dir_rmdir(), and debugfs commands
Valerie Aurora
val at versity.com
Wed Mar 12 07:33:51 PDT 2025
Allow removal of links to files and directories.
Signed-off-by: Valerie Aurora <val at versity.com>
---
cli/debugfs.c | 43 +++++++++++++
shared/dir.c | 170 +++++++++++++++++++++++++++++++++++++++++++++----
shared/dir.h | 4 ++
shared/inode.c | 20 ++++++
shared/inode.h | 1 +
5 files changed, 227 insertions(+), 11 deletions(-)
diff --git a/cli/debugfs.c b/cli/debugfs.c
index 3494070..32fc3f6 100644
--- a/cli/debugfs.c
+++ b/cli/debugfs.c
@@ -214,6 +214,27 @@ static void cmd_readdir(struct debugfs_context *ctx, int argc, char **argv)
free(buf);
}
+static void cmd_rmdir(struct debugfs_context *ctx, int argc, char **argv)
+{
+ char *name;
+ int ret;
+
+ if (argc != 2) {
+ printf("usage: rmdir <pathname>\n");
+ return;
+ }
+
+ name = argv[1];
+
+ ret = ngnfs_dir_rmdir(ctx->nfi, &ctx->cwd_ig, name, strlen(name));
+ if (ret < 0) {
+ if (ret == -ENOENT)
+ printf("rmdir: %s does not exist\n", name);
+ else
+ log("rmdir error: "ENOF"\n", ENOA(-ret));
+ }
+}
+
static void cmd_stat(struct debugfs_context *ctx, int argc, char **argv)
{
struct ngnfs_inode_ino_gen ig = ctx->cwd_ig;
@@ -280,6 +301,26 @@ static void cmd_sync(struct debugfs_context *ctx, int argc, char **argv)
printf("sync error: "ENOF"\n", ENOA(-ret));
}
+static void cmd_unlink(struct debugfs_context *ctx, int argc, char **argv)
+{
+ char *name;
+ int ret;
+
+ if (argc != 2) {
+ printf("usage: unlink <pathname>\n");
+ return;
+ }
+
+ name = argv[1];
+
+ ret = ngnfs_dir_unlink(ctx->nfi, &ctx->cwd_ig, name, strlen(name));
+ if (ret < 0) {
+ if (ret == -ENOENT)
+ printf("unlink: %s does not exist\n", name);
+ else
+ log("unlink error: "ENOF"\n", ENOA(-ret));
+ }
+}
static struct command {
char *name;
@@ -292,8 +333,10 @@ static struct command {
{ "mkfs", cmd_mkfs, },
{ "quit", cmd_quit, },
{ "readdir", cmd_readdir, },
+ { "rmdir", cmd_rmdir, },
{ "stat", cmd_stat, },
{ "sync", cmd_sync, },
+ { "unlink", cmd_unlink, },
};
static int compar_cmd_names(const void *A, const void *B)
diff --git a/shared/dir.c b/shared/dir.c
index ebeea01..a8a180a 100644
--- a/shared/dir.c
+++ b/shared/dir.c
@@ -77,6 +77,15 @@ static u64 name_hash(void *name, size_t name_len)
return hash;
}
+/*
+ * Flags to specify which tests should be done before removing a
+ * directory entry.
+ */
+enum {
+ NGNFS_DIRENT_WANT_DIR = 0x1,
+ NGNFS_DIRENT_WILL_MOVE = 0x2,
+};
+
/*
* These large dirent structs can fit a full sized name so that we can
* copy in and out any dirent as we work with them.
@@ -173,27 +182,26 @@ static inline int check_ifmt(struct ngnfs_inode *ninode, u32 ifmt, int err)
}
/*
- * Update a directory's inode to reflect creation. We can return errors
- * if the create should fail.
+ * Update a parent directory's inode to reflect creation or deletion of
+ * an entry. We can return errors if the create should fail.
*/
static int update_dir(struct ngnfs_txn_block *tblk, struct ngnfs_inode *dir,
- struct dirent_args *da, int posneg)
+ struct dirent_args *da, s32 nlink_delta)
{
- s32 delta;
+ s32 dent_bytes;
+ s32 posneg;
int ret;
if (da->dent.pers_dtype == NGNFS_DT_DIR) {
- delta = posneg * 1;
- if ((le32_to_cpu(dir->nlink) + delta >= NGNFS_LINK_MAX)) {
- ret = -EMLINK;
+ ret = ngnfs_inode_update(tblk, dir, nlink_delta);
+ if (ret < 0)
goto out;
- }
- ngnfs_tblk_assign(tblk, dir->nlink, cpu_to_le32(le32_to_cpu(dir->nlink) + delta));
}
+ posneg = nlink_delta >= 0 ? 1 : -1;
/* dir i_size includes null termed names */
- delta = posneg * ((s32)da->dent.name_len + 1);
- ngnfs_tblk_assign(tblk, dir->size, cpu_to_le64(le64_to_cpu(dir->size) + delta));
+ dent_bytes = posneg * ((s32)da->dent.name_len + 1);
+ ngnfs_tblk_assign(tblk, dir->size, cpu_to_le64(le64_to_cpu(dir->size) + dent_bytes));
ret = 0;
out:
return ret;
@@ -578,3 +586,143 @@ int ngnfs_dir_lookup(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir_
kfree(op);
return ret;
}
+
+static int remove_dirent_wr(struct ngnfs_btree_key *key, void *val, size_t size, void *arg,
+ struct ngnfs_btree_op *op)
+{
+ struct ngnfs_dirent *dent = val;
+ struct dirent_args *da = arg;
+
+ if (!dent)
+ return -ENOENT;
+
+ if (!names_equal(dent->name, dent->name_len, da->dent.name, da->dent.name_len))
+ return NGNFS_BTREE_ITER_CONTINUE;
+
+ da->ig.ino = le64_to_cpu(dent->ig.ino);
+ da->ig.gen = le64_to_cpu(dent->ig.gen);
+ memcpy(&da->dent, dent, size);
+
+ op->delete = 1;
+
+ return 0;
+}
+
+/*
+ * Remove a dirent item from a directory inode's dirent btree.
+ */
+static int remove_dirent(struct ngnfs_fs_info *nfi, struct ngnfs_transaction *txn,
+ struct ngnfs_inode_txn_ref *dir, struct dirent_args *da)
+{
+ struct ngnfs_btree_key key;
+ struct ngnfs_btree_key last;
+ int ret;
+
+ init_dirent_key(&key, da->hash);
+ init_dirent_key(&last, da->hash | NGNFS_DIRENT_COLL_BIT);
+
+ ret = ngnfs_btree_write_iter(nfi, txn, dir->tblk, &dir->ninode->dirents, &key, &last,
+ remove_dirent_wr, da);
+ if (ret < 0)
+ return ret;
+
+ if (da->dent.ig.ino == 0)
+ ret = ENOENT;
+
+ return ret;
+}
+
+static int check_empty_dir(struct ngnfs_fs_info *nfi, struct ngnfs_transaction *txn,
+ struct ngnfs_inode_ino_gen *ig)
+{
+ struct ngnfs_inode_txn_ref new;
+ int ret;
+
+ ret = ngnfs_inode_get(nfi, txn, NBF_READ, ig, &new);
+ if (ret < 0)
+ return ret;
+
+ if (le32_to_cpu(new.ninode->nlink) != 2)
+ return -ENOTEMPTY;
+
+ return 0;
+}
+
+/*
+ * Run final checks before removing a dirent, depending on whether we
+ * want a directory or if we are deleting or moving a dirent.
+ */
+static int check_remove_dirent(struct ngnfs_fs_info *nfi, struct ngnfs_transaction *txn,
+ struct ngnfs_inode_txn_ref *dir, struct ngnfs_inode_txn_ref *new,
+ struct dirent_args *da, int flags)
+{
+ if (!(flags & NGNFS_DIRENT_WANT_DIR)) {
+ if (da->dent.pers_dtype == NGNFS_DT_DIR)
+ return -EISDIR;
+ else
+ return 0;
+ }
+
+ if (da->dent.pers_dtype != NGNFS_DT_DIR)
+ return -ENOTDIR;
+
+ if (flags & NGNFS_DIRENT_WILL_MOVE)
+ return 0;
+
+ return check_empty_dir(nfi, txn, &da->ig);
+}
+
+/*
+ * Unlink a directory entry.
+ */
+static int do_unlink(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir_ig, char *name,
+ size_t name_len, int flags)
+{
+ struct {
+ struct ngnfs_transaction txn;
+ struct ngnfs_inode_txn_ref dir;
+ struct ngnfs_inode_txn_ref target;
+ struct dirent_args da;
+ u64 ino;
+ u64 nsec;
+ } *op;
+ int ret;
+
+ op = kmalloc(sizeof(*op), GFP_NOFS);
+ if (!op) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ngnfs_txn_init(&op->txn);
+ op->nsec = ktime_to_ns(ktime_get_real());
+ init_dirent_args(&op->da, name, name_len, 0);
+
+ do {
+ ret = ngnfs_inode_get(nfi, &op->txn, NBF_WRITE, dir_ig, &op->dir) ?:
+ check_ifmt(op->dir.ninode, S_IFDIR, -ENOTDIR) ?:
+ remove_dirent(nfi, &op->txn, &op->dir, &op->da) ?:
+ ngnfs_inode_get(nfi, &op->txn, NBF_WRITE, &op->da.ig, &op->target) ?:
+ check_remove_dirent(nfi, &op->txn, &op->dir, &op->target, &op->da, flags) ?:
+ ngnfs_inode_update(op->target.tblk, op->target.ninode, -1) ?:
+ update_dir(op->dir.tblk, op->dir.ninode, &op->da, -1);
+
+ } while (ngnfs_txn_retry(nfi, &op->txn, &ret));
+
+ ngnfs_txn_teardown(nfi, &op->txn);
+ kfree(op);
+out:
+ return ret;
+}
+
+int ngnfs_dir_unlink(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, char *name,
+ size_t name_len)
+{
+ return do_unlink(nfi, dir, name, name_len, 0);
+}
+
+int ngnfs_dir_rmdir(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, char *name,
+ size_t name_len)
+{
+ return do_unlink(nfi, dir, name, name_len, NGNFS_DIRENT_WANT_DIR);
+}
diff --git a/shared/dir.h b/shared/dir.h
index 5dd57dd..8aba78b 100644
--- a/shared/dir.h
+++ b/shared/dir.h
@@ -10,6 +10,10 @@ int ngnfs_dir_create(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir,
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);
+int ngnfs_dir_unlink(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, char *name,
+ size_t name_len);
+int ngnfs_dir_rmdir(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *dir, 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 2b29a1f..370cc50 100644
--- a/shared/inode.c
+++ b/shared/inode.c
@@ -57,6 +57,8 @@ int ngnfs_inode_init(struct ngnfs_inode_txn_ref *itref, struct ngnfs_inode_ino_g
int ngnfs_inode_get(struct ngnfs_fs_info *nfi, struct ngnfs_transaction *txn, nbf_t nbf,
struct ngnfs_inode_ino_gen *ig, struct ngnfs_inode_txn_ref *itref)
{
+ BUG_ON(ig->ino == 0);
+
return ngnfs_txn_get_block(nfi, txn, ig->ino, nbf, &itref->tblk, (void **)&itref->ninode);
}
@@ -106,3 +108,21 @@ int ngnfs_inode_read_copy(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen
out:
return ret;
}
+
+/*
+ * Update an inode to reflect the addition or removal of one or more links to it.
+ */
+int ngnfs_inode_update(struct ngnfs_txn_block *tblk, struct ngnfs_inode *inode, s32 delta)
+{
+ s32 nlink = le32_to_cpu(inode->nlink);
+
+ if ((delta > 0) && (nlink > NGNFS_LINK_MAX - delta))
+ return -EMLINK;
+
+ /* nlink < 0 is a bug */
+ BUG_ON((delta < 0) && (nlink + delta < 0));
+
+ ngnfs_tblk_assign(tblk, inode->nlink, cpu_to_le32(nlink + delta));
+
+ return 0;
+}
diff --git a/shared/inode.h b/shared/inode.h
index 92b5114..fe07b49 100644
--- a/shared/inode.h
+++ b/shared/inode.h
@@ -27,5 +27,6 @@ int ngnfs_inode_alloc(struct ngnfs_fs_info *nfi, struct ngnfs_transaction *txn,
struct ngnfs_inode_ino_gen *ig, struct ngnfs_inode_txn_ref *itref);
int ngnfs_inode_read_copy(struct ngnfs_fs_info *nfi, struct ngnfs_inode_ino_gen *ig,
void *buf, int size);
+int ngnfs_inode_update(struct ngnfs_txn_block *tblk, struct ngnfs_inode *inode, int posneg);
#endif
--
2.48.1
More information about the ngnfs-devel
mailing list