changeset 892:9b1ce98807c0

objstore: drop redundant _dir from a handful of file names Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Mon, 19 Dec 2022 19:49:35 -0500
parents 77a35abc6a24
children 7d90b5dc4fe3
files src/objstore/CMakeLists.txt src/objstore/obj_create.c src/objstore/obj_dir_create.c src/objstore/obj_dir_link.c src/objstore/obj_dir_unlink.c src/objstore/obj_link.c src/objstore/obj_unlink.c
diffstat 7 files changed, 376 insertions(+), 376 deletions(-) [+]
line wrap: on
line diff
--- a/src/objstore/CMakeLists.txt	Mon Dec 19 19:44:45 2022 -0500
+++ b/src/objstore/CMakeLists.txt	Mon Dec 19 19:49:35 2022 -0500
@@ -28,12 +28,12 @@
 	dirent_target_packing.c
 	obj.c
 	obj_attr.c
+	obj_create.c
 	obj_dir.c
-	obj_dir_create.c
-	obj_dir_link.c
-	obj_dir_unlink.c
+	obj_link.c
 	obj_ops.c
 	obj_rw.c
+	obj_unlink.c
 	objstore.c
 	txn.c
 	vdev.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/objstore/obj_create.c	Mon Dec 19 19:49:35 2022 -0500
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015-2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "dir.h"
+
+#include <jeffpc/time.h>
+
+int dir_create(struct txn *txn, struct objver *dirver, const char *name,
+	       uint32_t owner, uint32_t group, uint16_t mode,
+	       struct noid *_child)
+{
+	const uint64_t now = gettime();
+	struct nattr attrs = {
+		.mode = mode,
+		.nlink = 1, /* even new dirs start with link count of 1 */
+		.size = NATTR_ISDIR(mode) ? DIR_BLOCK_SIZE : 0,
+		.atime = now,
+		.btime = now,
+		.ctime = now,
+		.mtime = now,
+		.owner = owner,
+		.group = group,
+	};
+	uint8_t raw[DIR_BLOCK_SIZE];
+	struct objver *child;
+	struct ndirent_mem ent;
+	bool update_existing;
+	uint16_t ndirents;
+	uint64_t diroff;
+	int ret;
+
+	ASSERT(!NATTR_ISGRAFT(mode));
+
+	ret = dir_lookup_entry(dirver, name, raw, &ent, &diroff, &ndirents);
+	if (ret) {
+		/* error or didn't find anything */
+		if (ret != -ENOENT)
+			return ret;
+
+		update_existing = false;
+	} else {
+		/* found something */
+		if (!ent.all_deleted)
+			return -EEXIST;
+
+		update_existing = true;
+	}
+
+	/* create the child */
+	child = obj_create(txn, &attrs, &dirver->obj->oid);
+	if (IS_ERR(child))
+		return PTR_ERR(child);
+
+	/* if the child is a directory, write out empty directory contents */
+	if (NATTR_ISDIR(child->attrs.mode)) {
+		uint8_t raw[DIR_BLOCK_SIZE];
+		struct buffer contents;
+		ssize_t ret2;
+
+		buffer_init_static(&contents, raw, sizeof(raw), sizeof(raw),
+				   true);
+
+		ret = dir_make_buffer(noid_get_allocator(&child->obj->oid),
+				      noid_get_uniq(&child->obj->oid),
+				      noid_get_allocator(&dirver->obj->oid),
+				      noid_get_uniq(&dirver->obj->oid),
+				      &contents);
+		if (ret)
+			goto err;
+
+		ret2 = obj_write(txn, child, raw, sizeof(raw), 0);
+		if (ret2 < 0) {
+			ret = ret2;
+			goto err;
+		}
+
+		if (ret2 != sizeof(raw)) {
+			ret = -EIO;
+			goto err;
+		}
+	}
+
+	/* add dirent to parent dir */
+	if (!update_existing) {
+		ret = dir_add_dirent(txn, dirver, name, mode, &child->obj->oid);
+		if (ret)
+			goto err;
+	} else {
+		ret = dir_update_dirent(txn, dirver, raw, diroff, ndirents,
+					name, mode, &child->obj->oid);
+		if (ret)
+			goto err;
+	}
+
+	/*
+	 * We don't track nlinks on directories (it is always 1), but if we
+	 * did we'd increment the parent dir's link count here.
+	 */
+
+	*_child = child->obj->oid;
+
+	ret = 0;
+
+err:
+	obj_putref(child->obj);
+
+	return ret;
+}
--- a/src/objstore/obj_dir_create.c	Mon Dec 19 19:44:45 2022 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2015-2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include "dir.h"
-
-#include <jeffpc/time.h>
-
-int dir_create(struct txn *txn, struct objver *dirver, const char *name,
-	       uint32_t owner, uint32_t group, uint16_t mode,
-	       struct noid *_child)
-{
-	const uint64_t now = gettime();
-	struct nattr attrs = {
-		.mode = mode,
-		.nlink = 1, /* even new dirs start with link count of 1 */
-		.size = NATTR_ISDIR(mode) ? DIR_BLOCK_SIZE : 0,
-		.atime = now,
-		.btime = now,
-		.ctime = now,
-		.mtime = now,
-		.owner = owner,
-		.group = group,
-	};
-	uint8_t raw[DIR_BLOCK_SIZE];
-	struct objver *child;
-	struct ndirent_mem ent;
-	bool update_existing;
-	uint16_t ndirents;
-	uint64_t diroff;
-	int ret;
-
-	ASSERT(!NATTR_ISGRAFT(mode));
-
-	ret = dir_lookup_entry(dirver, name, raw, &ent, &diroff, &ndirents);
-	if (ret) {
-		/* error or didn't find anything */
-		if (ret != -ENOENT)
-			return ret;
-
-		update_existing = false;
-	} else {
-		/* found something */
-		if (!ent.all_deleted)
-			return -EEXIST;
-
-		update_existing = true;
-	}
-
-	/* create the child */
-	child = obj_create(txn, &attrs, &dirver->obj->oid);
-	if (IS_ERR(child))
-		return PTR_ERR(child);
-
-	/* if the child is a directory, write out empty directory contents */
-	if (NATTR_ISDIR(child->attrs.mode)) {
-		uint8_t raw[DIR_BLOCK_SIZE];
-		struct buffer contents;
-		ssize_t ret2;
-
-		buffer_init_static(&contents, raw, sizeof(raw), sizeof(raw),
-				   true);
-
-		ret = dir_make_buffer(noid_get_allocator(&child->obj->oid),
-				      noid_get_uniq(&child->obj->oid),
-				      noid_get_allocator(&dirver->obj->oid),
-				      noid_get_uniq(&dirver->obj->oid),
-				      &contents);
-		if (ret)
-			goto err;
-
-		ret2 = obj_write(txn, child, raw, sizeof(raw), 0);
-		if (ret2 < 0) {
-			ret = ret2;
-			goto err;
-		}
-
-		if (ret2 != sizeof(raw)) {
-			ret = -EIO;
-			goto err;
-		}
-	}
-
-	/* add dirent to parent dir */
-	if (!update_existing) {
-		ret = dir_add_dirent(txn, dirver, name, mode, &child->obj->oid);
-		if (ret)
-			goto err;
-	} else {
-		ret = dir_update_dirent(txn, dirver, raw, diroff, ndirents,
-					name, mode, &child->obj->oid);
-		if (ret)
-			goto err;
-	}
-
-	/*
-	 * We don't track nlinks on directories (it is always 1), but if we
-	 * did we'd increment the parent dir's link count here.
-	 */
-
-	*_child = child->obj->oid;
-
-	ret = 0;
-
-err:
-	obj_putref(child->obj);
-
-	return ret;
-}
--- a/src/objstore/obj_dir_link.c	Mon Dec 19 19:44:45 2022 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include "dir.h"
-
-#include <jeffpc/time.h>
-
-int dir_symlink(struct txn *txn, struct objver *dirver, const char *name,
-		uint32_t owner, uint32_t group, uint16_t mode,
-		const char *target, struct noid *_child)
-{
-	const uint64_t now = gettime();
-	struct nattr attrs = {
-		.mode = mode,
-		.nlink = 1,
-		.size = strlen(target),
-		.atime = now,
-		.btime = now,
-		.ctime = now,
-		.mtime = now,
-		.owner = owner,
-		.group = group,
-	};
-	uint8_t raw[DIR_BLOCK_SIZE];
-	struct ndirent_mem ent;
-	bool update_existing;
-	struct objver *child;
-	uint16_t ndirents;
-	uint64_t diroff;
-	ssize_t ret;
-
-	ASSERT(NATTR_ISLNK(mode));
-
-	ret = dir_lookup_entry(dirver, name, raw, &ent, &diroff, &ndirents);
-	if (ret) {
-		/* error or didn't find anything */
-		if (ret != -ENOENT)
-			return ret;
-
-		update_existing = false;
-	} else {
-		/* found something */
-		if (!ent.all_deleted)
-			return -EEXIST;
-
-		update_existing = true;
-	}
-
-	/* make new oid for the child */
-	child = obj_create(txn, &attrs, &dirver->obj->oid);
-	if (IS_ERR(child))
-		return PTR_ERR(child);
-
-	/* write out symlink target */
-	ret = obj_write(txn, child, target, strlen(target), 0);
-	if (ret < 0)
-		goto err;
-
-	if (ret != strlen(target)) {
-		ret = -EIO;
-		goto err;
-	}
-
-	/* add dirent to parent dir */
-	if (!update_existing) {
-		ret = dir_add_dirent(txn, dirver, name, mode, &child->obj->oid);
-		if (ret)
-			return ret;
-	} else {
-		ret = dir_update_dirent(txn, dirver, raw, diroff, ndirents,
-					name, mode, &child->obj->oid);
-		if (ret)
-			return ret;
-	}
-
-	*_child = child->obj->oid;
-
-	ret = 0;
-
-err:
-	obj_putref(child->obj);
-
-	return ret;
-}
--- a/src/objstore/obj_dir_unlink.c	Mon Dec 19 19:44:45 2022 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2015-2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include "dir.h"
-
-static int __dir_unlink_update(struct dirblock *block, const char *name,
-			       const struct noid *desired, bool rmdir)
-{
-	const size_t namelen = strlen(name);
-	size_t non_deleted_targets;
-	size_t i, j;
-
-	/* find the dirent */
-	for (i = 0; i < block->ndirents; i++) {
-		if (namelen != block->entries[i].dirent.namelen)
-			continue;
-
-		if (!strncmp(name, block->entries[i].name, namelen))
-			break;
-	}
-
-	VERIFY3U(i, <, block->ndirents);
-
-	VERIFY(!block->entries[i].dirent.all_deleted);
-
-	/* find the target */
-	for (j = 0; j < block->entries[i].dirent.ntgts; j++) {
-		struct ndirent_tgt *tgt = &block->entries[i].tgts[j];
-
-		if (tgt->deleted)
-			continue;
-
-		if (noid_is_null(desired))
-			break; /* found it */
-
-		/* looking for a specific target */
-
-		if (tgt->type == NDIRENT_TYPE_GRAFT) {
-			if (xuuid_compare(&tgt->graft, noid_get_vol(desired)))
-				continue;
-		} else {
-			if ((tgt->host != noid_get_allocator(desired)) ||
-			    (tgt->uniq != noid_get_uniq(desired)))
-				continue;
-		}
-	}
-
-	VERIFY3U(j, <, block->entries[i].dirent.ntgts);
-	VERIFY(!block->entries[i].tgts[j].deleted);
-
-	if (rmdir && (block->entries[i].tgts[j].type != NDIRENT_TYPE_DIR))
-		return -ENOTDIR;
-	if (!rmdir && (block->entries[i].tgts[j].type == NDIRENT_TYPE_DIR))
-		return -EISDIR;
-
-	if (!rmdir)
-		FIXME("need to decrement the target's link count");
-
-	/* update the target */
-	block->entries[i].tgts[j].deleted = true;
-
-	non_deleted_targets = 0;
-	for (j = 0; j < block->entries[i].dirent.ntgts; j++)
-		if (!block->entries[i].tgts[j].deleted)
-			non_deleted_targets++;
-
-	if (!block->entries[i].dirent.conflicts)
-		VERIFY3U(non_deleted_targets, ==, 0);
-
-	block->entries[i].dirent.conflicts = (non_deleted_targets > 1);
-	block->entries[i].dirent.all_deleted = (non_deleted_targets == 0);
-
-	return 0;
-}
-
-int dir_unlink(struct txn *txn, struct objver *dirver, const char *name,
-	       const struct noid *desired, bool rmdir)
-{
-	uint8_t raw[DIR_BLOCK_SIZE];
-	struct ndirent_mem ent;
-	struct dirblock block;
-	struct buffer buffer;
-	uint64_t diroff;
-	uint16_t ndirents;
-	ssize_t ret;
-
-	ret = dir_lookup_entry(dirver, name, raw, &ent, &diroff,
-			       &ndirents);
-	if (ret)
-		return ret;
-
-	if (ent.all_deleted)
-		return -ENOENT;
-
-	if (ent.conflicts && noid_is_null(desired))
-		return -ENOTUNIQ;
-
-	/* parse the block */
-	ret = dirblock_parse(&block, raw, ndirents);
-	if (ret)
-		return ret;
-
-	/* update the dirent target */
-	ret = __dir_unlink_update(&block, name, desired, rmdir);
-	if (ret)
-		return ret;
-
-	/* serialize the block & write it out */
-	buffer_init_static(&buffer, raw, 0, sizeof(raw), true);
-
-	ret = dirblock_serialize(&block, &buffer);
-	if (ret)
-		return ret;
-
-	ret = obj_write(txn, dirver, buffer_data(&buffer), buffer_size(&buffer),
-			diroff);
-	if (ret < 0)
-		return ret;
-
-	if (ret != buffer_size(&buffer))
-		panic("dir block partial write!");
-
-	return 0;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/objstore/obj_link.c	Mon Dec 19 19:49:35 2022 -0500
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "dir.h"
+
+#include <jeffpc/time.h>
+
+int dir_symlink(struct txn *txn, struct objver *dirver, const char *name,
+		uint32_t owner, uint32_t group, uint16_t mode,
+		const char *target, struct noid *_child)
+{
+	const uint64_t now = gettime();
+	struct nattr attrs = {
+		.mode = mode,
+		.nlink = 1,
+		.size = strlen(target),
+		.atime = now,
+		.btime = now,
+		.ctime = now,
+		.mtime = now,
+		.owner = owner,
+		.group = group,
+	};
+	uint8_t raw[DIR_BLOCK_SIZE];
+	struct ndirent_mem ent;
+	bool update_existing;
+	struct objver *child;
+	uint16_t ndirents;
+	uint64_t diroff;
+	ssize_t ret;
+
+	ASSERT(NATTR_ISLNK(mode));
+
+	ret = dir_lookup_entry(dirver, name, raw, &ent, &diroff, &ndirents);
+	if (ret) {
+		/* error or didn't find anything */
+		if (ret != -ENOENT)
+			return ret;
+
+		update_existing = false;
+	} else {
+		/* found something */
+		if (!ent.all_deleted)
+			return -EEXIST;
+
+		update_existing = true;
+	}
+
+	/* make new oid for the child */
+	child = obj_create(txn, &attrs, &dirver->obj->oid);
+	if (IS_ERR(child))
+		return PTR_ERR(child);
+
+	/* write out symlink target */
+	ret = obj_write(txn, child, target, strlen(target), 0);
+	if (ret < 0)
+		goto err;
+
+	if (ret != strlen(target)) {
+		ret = -EIO;
+		goto err;
+	}
+
+	/* add dirent to parent dir */
+	if (!update_existing) {
+		ret = dir_add_dirent(txn, dirver, name, mode, &child->obj->oid);
+		if (ret)
+			return ret;
+	} else {
+		ret = dir_update_dirent(txn, dirver, raw, diroff, ndirents,
+					name, mode, &child->obj->oid);
+		if (ret)
+			return ret;
+	}
+
+	*_child = child->obj->oid;
+
+	ret = 0;
+
+err:
+	obj_putref(child->obj);
+
+	return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/objstore/obj_unlink.c	Mon Dec 19 19:49:35 2022 -0500
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2015-2020,2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "dir.h"
+
+static int __dir_unlink_update(struct dirblock *block, const char *name,
+			       const struct noid *desired, bool rmdir)
+{
+	const size_t namelen = strlen(name);
+	size_t non_deleted_targets;
+	size_t i, j;
+
+	/* find the dirent */
+	for (i = 0; i < block->ndirents; i++) {
+		if (namelen != block->entries[i].dirent.namelen)
+			continue;
+
+		if (!strncmp(name, block->entries[i].name, namelen))
+			break;
+	}
+
+	VERIFY3U(i, <, block->ndirents);
+
+	VERIFY(!block->entries[i].dirent.all_deleted);
+
+	/* find the target */
+	for (j = 0; j < block->entries[i].dirent.ntgts; j++) {
+		struct ndirent_tgt *tgt = &block->entries[i].tgts[j];
+
+		if (tgt->deleted)
+			continue;
+
+		if (noid_is_null(desired))
+			break; /* found it */
+
+		/* looking for a specific target */
+
+		if (tgt->type == NDIRENT_TYPE_GRAFT) {
+			if (xuuid_compare(&tgt->graft, noid_get_vol(desired)))
+				continue;
+		} else {
+			if ((tgt->host != noid_get_allocator(desired)) ||
+			    (tgt->uniq != noid_get_uniq(desired)))
+				continue;
+		}
+	}
+
+	VERIFY3U(j, <, block->entries[i].dirent.ntgts);
+	VERIFY(!block->entries[i].tgts[j].deleted);
+
+	if (rmdir && (block->entries[i].tgts[j].type != NDIRENT_TYPE_DIR))
+		return -ENOTDIR;
+	if (!rmdir && (block->entries[i].tgts[j].type == NDIRENT_TYPE_DIR))
+		return -EISDIR;
+
+	if (!rmdir)
+		FIXME("need to decrement the target's link count");
+
+	/* update the target */
+	block->entries[i].tgts[j].deleted = true;
+
+	non_deleted_targets = 0;
+	for (j = 0; j < block->entries[i].dirent.ntgts; j++)
+		if (!block->entries[i].tgts[j].deleted)
+			non_deleted_targets++;
+
+	if (!block->entries[i].dirent.conflicts)
+		VERIFY3U(non_deleted_targets, ==, 0);
+
+	block->entries[i].dirent.conflicts = (non_deleted_targets > 1);
+	block->entries[i].dirent.all_deleted = (non_deleted_targets == 0);
+
+	return 0;
+}
+
+int dir_unlink(struct txn *txn, struct objver *dirver, const char *name,
+	       const struct noid *desired, bool rmdir)
+{
+	uint8_t raw[DIR_BLOCK_SIZE];
+	struct ndirent_mem ent;
+	struct dirblock block;
+	struct buffer buffer;
+	uint64_t diroff;
+	uint16_t ndirents;
+	ssize_t ret;
+
+	ret = dir_lookup_entry(dirver, name, raw, &ent, &diroff,
+			       &ndirents);
+	if (ret)
+		return ret;
+
+	if (ent.all_deleted)
+		return -ENOENT;
+
+	if (ent.conflicts && noid_is_null(desired))
+		return -ENOTUNIQ;
+
+	/* parse the block */
+	ret = dirblock_parse(&block, raw, ndirents);
+	if (ret)
+		return ret;
+
+	/* update the dirent target */
+	ret = __dir_unlink_update(&block, name, desired, rmdir);
+	if (ret)
+		return ret;
+
+	/* serialize the block & write it out */
+	buffer_init_static(&buffer, raw, 0, sizeof(raw), true);
+
+	ret = dirblock_serialize(&block, &buffer);
+	if (ret)
+		return ret;
+
+	ret = obj_write(txn, dirver, buffer_data(&buffer), buffer_size(&buffer),
+			diroff);
+	if (ret < 0)
+		return ret;
+
+	if (ret != buffer_size(&buffer))
+		panic("dir block partial write!");
+
+	return 0;
+}