changeset 788:1eef9314f649

objstore: extend existing dirents on create if necessary When creating a new file, we may already have a dirent for that name but with all its targets deleted. In this case, we must add a new target to the existing dirent instead of allocating a new one. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Tue, 31 Mar 2020 11:05:51 -0400
parents f53967574f3e
children 7966b4a0bcbc
files src/objstore/obj_dir_create.c
diffstat 1 files changed, 121 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/objstore/obj_dir_create.c	Wed Mar 25 12:33:44 2020 -0400
+++ b/src/objstore/obj_dir_create.c	Tue Mar 31 11:05:51 2020 -0400
@@ -118,6 +118,105 @@
 	return ret;
 }
 
+static int __dir_create_update_dirent_real(struct dirblock *block,
+					   const char *name, uint16_t mode,
+					   const struct noid *child)
+{
+	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->host != noid_get_allocator(child)) ||
+		    (tgt->uniq != noid_get_uniq(child)))
+			continue;
+	}
+
+	/* update the target */
+	if (j < block->entries[i].dirent.ntgts) {
+		/* found a match - just reset the flag */
+		block->entries[i].tgts[j].deleted = false;
+	} else {
+		/* not found - allocate a new target */
+		struct ndirent_tgt tgt;
+		int ret;
+
+		tgt.type = mode >> NATTR_TSHIFT;
+		tgt.deleted = false;
+		tgt.host = noid_get_allocator(child);
+		tgt.uniq = noid_get_uniq(child);
+
+		ret = dirblock_add_dirent_target(block, name, &tgt);
+		if (ret)
+			return ret;
+	}
+
+	non_deleted_targets = 0;
+	for (j = 0; j < block->entries[i].dirent.ntgts; j++)
+		if (!block->entries[i].tgts[j].deleted)
+			non_deleted_targets++;
+
+	VERIFY3U(non_deleted_targets, ==, 1);
+
+	block->entries[i].dirent.conflicts = false;
+	block->entries[i].dirent.all_deleted = false;
+
+	return 0;
+}
+
+static int __dir_create_update_dirent(struct txn *txn, struct objver *dirver,
+				      uint8_t *raw, uint64_t diroff,
+				      uint16_t ndirents, const char *name,
+				      uint16_t mode, const struct noid *child)
+{
+	struct dirblock block;
+	struct buffer buffer;
+	int ret;
+
+	/* parse the block */
+	ret = dirblock_parse(&block, raw, ndirents);
+	if (ret)
+		return ret;
+
+	/* update the dirent target */
+	ret = __dir_create_update_dirent_real(&block, name, mode, child);
+	if (ret)
+		return ret;
+
+	/* serialize the block & write it out */
+	ret = buffer_init_heap(&buffer, DIR_BLOCK_SIZE);
+	if (ret)
+		return ret;
+
+	VERIFY0(buffer_truncate(&buffer, 0));
+
+	ret = dirblock_serialize(&block, &buffer);
+	if (ret) {
+		buffer_free(&buffer);
+		return ret;
+	}
+
+	obj_write(txn, dirver, &buffer, diroff);
+
+	return 0;
+}
+
 static void __dir_create_parent_link(struct txn *txn, struct objver *dirver)
 {
 	struct nattr attrs = {
@@ -132,17 +231,26 @@
 {
 	uint8_t raw[DIR_BLOCK_SIZE];
 	struct ndirent_mem ent;
+	bool update_existing;
+	uint16_t ndirents;
+	uint64_t diroff;
 	int ret;
 
-	ret = dir_lookup_entry(dirver, name, raw, &ent, NULL, NULL);
+	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;
 	}
 
 	/* make new oid for the child */
@@ -150,6 +258,8 @@
 	if (ret)
 		return ret;
 
+	ASSERT(!noid_is_null(child));
+
 	/* write out empty child dir contents */
 	if (NATTR_ISDIR(mode)) {
 		ret = __dir_create_child_contents(txn, &dirver->obj->oid, child);
@@ -158,9 +268,16 @@
 	}
 
 	/* add dirent to parent dir */
-	ret = __dir_create_add_dirent(txn, dirver, name, mode, child);
-	if (ret)
-		return ret;
+	if (!update_existing) {
+		ret = __dir_create_add_dirent(txn, dirver, name, mode, child);
+		if (ret)
+			return ret;
+	} else {
+		ret = __dir_create_update_dirent(txn, dirver, raw, diroff,
+						 ndirents, name, mode, child);
+		if (ret)
+			return ret;
+	}
 
 	/* increment parent dir's link count */
 	if (NATTR_ISDIR(mode))