changeset 780:d1c336241209

objstore: ignore all deleted dirent targets during lookup Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sun, 29 Mar 2020 17:37:14 -0400
parents 4767ca9d2c3e
children ca83139c8103
files src/objstore/obj_dir.c
diffstat 1 files changed, 93 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/objstore/obj_dir.c	Mon Mar 30 23:03:34 2020 -0400
+++ b/src/objstore/obj_dir.c	Sun Mar 29 17:37:14 2020 -0400
@@ -150,6 +150,7 @@
 	struct ndirent_mem ent;
 	struct ndirent_tgt tgt;
 	struct buffer tgtbuf;
+	size_t i;
 	int ret;
 
 	ret = dir_lookup_entry(dirver, name, raw, true, &ent, NULL, NULL);
@@ -159,9 +160,23 @@
 	buffer_init_static(&tgtbuf, &raw[ent.tgtoff],
 			   DIR_BLOCK_SIZE - ent.tgtoff, false);
 
-	ret = unpack_dirent_tgt(&tgtbuf, &tgt);
-	if (ret)
-		return ret;
+	/*
+	 * There is exactly one non-deleted target, and zero or more deleted
+	 * ones.  Find the right one and return it.
+	 */
+	ASSERT(!ent.conflicts);
+	ASSERT(!ent.all_deleted);
+
+	for (i = 0; i < ent.ntgts; i++) {
+		ret = unpack_dirent_tgt(&tgtbuf, &tgt);
+		if (ret)
+			return ret;
+
+		if (!tgt.deleted)
+			break;
+	}
+
+	VERIFY3U(i, <, ent.ntgts);
 
 	if (tgt.type == NDIRENT_TYPE_GRAFT)
 		noid_set(child, &tgt.graft, 0, 0);
@@ -177,6 +192,31 @@
 		return 0;
 }
 
+static int __dir_lookup_all_realloc(size_t ntgts, struct noid **oids,
+				    uint8_t **types, bool first)
+{
+	void *tmp;
+
+	if (first) {
+		*oids = NULL;
+		*types = NULL;
+	}
+
+	tmp = mem_reallocarray(*oids, ntgts, sizeof(struct noid));
+	if (!tmp)
+		return -ENOMEM;
+
+	*oids = tmp;
+
+	tmp = mem_reallocarray(*types, ntgts, sizeof(uint8_t));
+	if (!tmp)
+		return -ENOMEM;
+
+	*types = tmp;
+
+	return 0;
+}
+
 ssize_t dir_lookup_all(struct objver *dirver, const char *name,
 		       struct noid **child, uint8_t **type)
 {
@@ -185,44 +225,66 @@
 	struct buffer tgtbuf;
 	struct noid *oids;
 	uint8_t *types;
-	size_t i;
+	size_t ntgts;
+	size_t i, j;
 	int ret;
 
 	ret = dir_lookup_entry(dirver, name, raw, false, &ent, NULL, NULL);
 	if (ret)
 		return ret;
 
-	oids = malloc(sizeof(struct noid) * ent.ntgts);
-	types = malloc(sizeof(uint8_t) * ent.ntgts);
+	/*
+	 * There is one or more non-deleted target and zero or more deleted
+	 * ones.  Find all the non-deleted ones and return them.
+	 */
+	ASSERT(!ent.all_deleted);
 
-	if (!oids || !types) {
-		ret = -ENOMEM;
+	/* start with 2 if there are conflicts; 1 is enough otherwise */
+	ntgts = ent.conflicts ? 2 : 1;
+
+	ret = __dir_lookup_all_realloc(ntgts, &oids, &types, true);
+	if (ret)
 		goto err;
-	}
 
 	buffer_init_static(&tgtbuf, &raw[ent.tgtoff],
 			   DIR_BLOCK_SIZE - ent.tgtoff, false);
 
-	for (i = 0; i < ent.ntgts; i++) {
+	for (i = 0, j = 0; i < ent.ntgts; i++) {
 		struct ndirent_tgt tgt;
 
 		ret = unpack_dirent_tgt(&tgtbuf, &tgt);
 		if (ret)
 			goto err;
 
+		if (tgt.deleted)
+			continue; /* deleted - skip */
+
+		/* grow the arrays if needed */
+		if (j == ntgts) {
+			ntgts++;
+
+			ret = __dir_lookup_all_realloc(ntgts, &oids, &types,
+						       false);
+			if (ret)
+				goto err;
+		}
+
 		if (tgt.type == NDIRENT_TYPE_GRAFT)
-			noid_set(&oids[i], &tgt.graft, 0, 0);
+			noid_set(&oids[j], &tgt.graft, 0, 0);
 		else
-			noid_set(&oids[i], &dirver->obj->clone->uuid, tgt.host,
+			noid_set(&oids[j], &dirver->obj->clone->uuid, tgt.host,
 				 tgt.uniq);
 
-		types[i] = tgt.type;
+		types[j] = tgt.type;
+		j++;
 	}
 
+	ASSERT3U(j, ==, ntgts);
+
 	*child = oids;
 	*type = types;
 
-	return ent.ntgts;
+	return ntgts;
 
 err:
 	free(oids);
@@ -289,14 +351,28 @@
 		if (!dirent.conflicts) {
 			struct ndirent_tgt tgt;
 			struct buffer tgtbuf;
+			size_t i;
 
 			buffer_init_static(&tgtbuf, &raw[dirent.tgtoff],
 					   DIR_BLOCK_SIZE - dirent.tgtoff,
 					   false);
 
-			ret = unpack_dirent_tgt(&tgtbuf, &tgt);
-			if (ret)
-				goto err;
+			/*
+			 * There is exactly one non-deleted target, and zero
+			 * or more deleted ones.  Find the right one and
+			 * return it.
+			 */
+
+			for (i = 0; i < dirent.ntgts; i++) {
+				ret = unpack_dirent_tgt(&tgtbuf, &tgt);
+				if (ret)
+					goto err;
+
+				if (!tgt.deleted)
+					break;
+			}
+
+			VERIFY3U(i, <, dirent.ntgts);
 
 			child->type = tgt.type;