Mercurial > nomad
view src/objstore/obj_dir.c @ 800:f679541c8142
switch to new buffer_init_static libjeffpc API
The function gained a second size argument removing the need for some
truncate calls.
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Fri, 03 Apr 2020 15:54:51 -0400 |
parents | f53967574f3e |
children | a525d07c3fda |
line wrap: on
line source
/* * Copyright (c) 2015-2020 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 __read_dir_block(struct objver *dirver, uint8_t *raw, size_t rawsize, uint64_t off, uint16_t *ndirents) { struct obj_dir_header hdr; ssize_t ret; ret = dirver->obj->ops->read(dirver, raw, rawsize, off); if (ret < 0) return ret; if (ret != DIR_BLOCK_SIZE) return -EIO; /* we require the whole dir block */ memcpy(&hdr, raw, sizeof(struct obj_dir_header)); hdr.magic = be64_to_cpu(hdr.magic); hdr.ndirents = be16_to_cpu(hdr.ndirents); if (hdr.magic != OBJ_DIR_MAGIC) return -EIO; /* magic mismatch */ /* ndirents must not be more than the max number of dirents per block */ if (hdr.ndirents > (rawsize / sizeof(struct ndirent_phys))) return -EIO; *ndirents = hdr.ndirents; return 0; } int obj_make_dir_buffer(uint64_t this_host, uint64_t this_uniq, uint64_t parent_host, uint64_t parent_uniq, struct buffer *buf) { struct ndirent_tgt tgt; struct dirblock block; int ret; dirblock_init(&block); ret = buffer_truncate(buf, 0); if (ret) return ret; /* '.' dirent */ tgt.type = NDIRENT_TYPE_DIR; tgt.deleted = false; tgt.host = this_host; tgt.uniq = this_uniq; ret = dirblock_add_dirent(&block, ".", &tgt); if (ret) return ret; /* '..' dirent */ tgt.type = NDIRENT_TYPE_DIR; tgt.deleted = false; tgt.host = parent_host; tgt.uniq = parent_uniq; ret = dirblock_add_dirent(&block, "..", &tgt); if (ret) return ret; /* serialize */ return dirblock_serialize(&block, buf); } /* find an entry - no matter what its state is (conflicts, all_deleted, etc.) */ int dir_lookup_entry(struct objver *dirver, const char *name, uint8_t *raw, struct ndirent_mem *ent, uint64_t *_off, uint16_t *_ndirents) { const size_t namelen = strlen(name); uint16_t ndirents; uint64_t off; uint16_t i; int ret; off = 0; /* for each directory block */ do { uint8_t *cur; ret = __read_dir_block(dirver, raw, DIR_BLOCK_SIZE, off, &ndirents); if (ret) return ret; /* for each dirent */ cur = &raw[sizeof(struct obj_dir_header)]; for (i = 0; i < ndirents; i++) { dirent_be2cpu(ent, (void *) &raw[sizeof(struct obj_dir_header) + sizeof(struct ndirent_phys) * i]); /* check the name */ if (ent->namelen != namelen) continue; if (memcmp(&raw[ent->nameoff], name, namelen)) continue; if (_off) *_off = off; if (_ndirents) *_ndirents = ndirents; return 0; } off += DIR_BLOCK_SIZE; } while (off < dirver->attrs.size); return -ENOENT; } int dir_lookup_one(struct objver *dirver, const char *name, const struct noid *desired, struct noid *child, uint8_t *type) { uint8_t raw[DIR_BLOCK_SIZE]; struct ndirent_mem ent; struct ndirent_tgt tgt; struct buffer tgtbuf; size_t i; int ret; ret = dir_lookup_entry(dirver, name, raw, &ent, NULL, NULL); if (ret) return ret; if (ent.all_deleted) return -ENOENT; if (ent.conflicts) return -ENOTUNIQ; buffer_init_static(&tgtbuf, &raw[ent.tgtoff], DIR_BLOCK_SIZE - ent.tgtoff, DIR_BLOCK_SIZE - ent.tgtoff, false); /* * There is exactly one non-deleted target, and zero or more deleted * ones. Find the right one and return it. */ 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); else noid_set(child, &dirver->obj->clone->uuid, tgt.host, tgt.uniq); /* * is it the one we desire? */ if (!noid_is_null(desired)) return noid_cmp(desired, child) ? -ENOENT : 0; else 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) { uint8_t raw[DIR_BLOCK_SIZE]; struct ndirent_mem ent; struct buffer tgtbuf; struct noid *oids; uint8_t *types; size_t ntgts; size_t i, j; int ret; ret = dir_lookup_entry(dirver, name, raw, &ent, NULL, NULL); if (ret) return ret; if (ent.all_deleted) return -ENOENT; /* * There is one or more non-deleted target and zero or more deleted * ones. Find all the non-deleted ones and return them. */ /* 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, DIR_BLOCK_SIZE - ent.tgtoff, false); 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[j], &tgt.graft, 0, 0); else noid_set(&oids[j], &dirver->obj->clone->uuid, tgt.host, tgt.uniq); types[j] = tgt.type; j++; } ASSERT3U(j, ==, ntgts); *child = oids; *type = types; return ntgts; err: free(oids); free(types); return ret; } int dir_getdent(struct objver *dirver, const uint64_t offset, struct ndirent *child) { uint8_t raw[DIR_BLOCK_SIZE]; struct ndirent_mem dirent; uint16_t ndirents; uint64_t blkno, blkidx; char *name; int ret; blkno = offset / DIR_BLOCK_SIZE; blkidx = offset % DIR_BLOCK_SIZE; restart: /* find the next entry >= supplied index */ while ((blkno * DIR_BLOCK_SIZE) < dirver->attrs.size) { ret = __read_dir_block(dirver, raw, DIR_BLOCK_SIZE, blkno * DIR_BLOCK_SIZE, &ndirents); if (ret) return ret; if (blkidx < ndirents) break; blkno++; blkidx = 0; } /* found EOF */ if ((blkno * DIR_BLOCK_SIZE) >= dirver->attrs.size) return -ENOENT; dirent_be2cpu(&dirent, (void *) &raw[sizeof(struct obj_dir_header) + sizeof(struct ndirent_phys) * blkidx]); /* all targets deleted - go to next dirent */ if (dirent.all_deleted) { blkidx++; if (blkidx >= ndirents) { blkno++; blkidx = 0; } goto restart; } /* fill out `child` */ name = malloc(dirent.namelen + 1); if (name) { child->dir_offset = (blkno * DIR_BLOCK_SIZE) + blkidx; child->name = name; child->namelen = dirent.namelen; 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, DIR_BLOCK_SIZE - dirent.tgtoff, false); /* * 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; if (tgt.type == NDIRENT_TYPE_GRAFT) noid_set(&child->oid, &tgt.graft, 0, 0); else noid_set(&child->oid, &dirver->obj->clone->uuid, be64_to_cpu(tgt.host), be64_to_cpu(tgt.uniq)); } else { child->type = NDIRENT_TYPE_MULTI; } memcpy(name, &raw[dirent.nameoff], dirent.namelen); name[dirent.namelen] = '\0'; ret = 0; } else { ret = -ENOMEM; } return ret; err: free(name); return ret; }