Mercurial > nomad
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))