Mercurial > nomad
view src/objstore/dirblock.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 | 68939d7b83f8 |
children |
line wrap: on
line source
/* * Copyright (c) 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" /* * Used as a poison value in dirblock->entries[i].dirent.{tgt,name}off to * catch accidental use. */ #define DIRBLOCK_DIRENT_OFFSET_POISON (0xffff) void dirblock_init(struct dirblock *block) { block->used_bytes = sizeof(struct obj_dir_header); block->name_bytes = 0; block->ndirents = 0; block->ntgts = 0; } /* parse a block */ int dirblock_parse(struct dirblock *block, uint8_t *raw, uint16_t ndirents) { size_t i, j; dirblock_init(block); /* for each dirent */ for (i = 0; i < ndirents; i++) { const int idx = block->ndirents; struct ndirent_mem *dirent = &block->entries[idx].dirent; size_t non_deleted_targets; struct buffer tgtbuf; int ret; dirent_be2cpu(dirent, (void *) &raw[sizeof(struct obj_dir_header) + sizeof(struct ndirent_phys) * i]); /* complete the fixed sized dirent */ block->entries[idx].name = &block->names[block->name_bytes]; block->entries[idx].tgts = &block->tgts[block->ntgts]; /* save the name */ memcpy(&block->names[block->name_bytes], &raw[dirent->nameoff], dirent->namelen); /* save the targets */ buffer_init_static(&tgtbuf, &raw[dirent->tgtoff], DIR_BLOCK_SIZE - dirent->tgtoff, DIR_BLOCK_SIZE - dirent->tgtoff, false); non_deleted_targets = 0; for (j = block->ntgts; j < (block->ntgts + dirent->ntgts); j++) { ret = unpack_dirent_tgt(&tgtbuf, &block->tgts[j]); if (ret) return ret; if (!block->tgts[j].deleted) non_deleted_targets++; } /* check and correct dirent flags */ if (dirent->conflicts != (non_deleted_targets > 1)) cmn_err(CE_WARN, "Parsed dirent conflicts (%s) " "mismatches targets (%zu !deleted)...fixed", dirent->conflicts ? "true" : "false", non_deleted_targets); if (dirent->all_deleted != (non_deleted_targets == 0)) cmn_err(CE_WARN, "Parsed dirent all_deleted (%s) " "mismatches targets (%zu !deleted)...fixed", dirent->all_deleted ? "true" : "false", non_deleted_targets); dirent->conflicts = (non_deleted_targets > 1); dirent->all_deleted = (non_deleted_targets == 0); /* update block state */ block->used_bytes += sizeof(struct ndirent_phys) + dirent->namelen + buffer_offset(&tgtbuf); block->name_bytes += dirent->namelen; block->ndirents++; block->ntgts += dirent->ntgts; /* poison the block's memory entries to avoid accidents */ dirent->tgtoff = DIRBLOCK_DIRENT_OFFSET_POISON; dirent->nameoff = DIRBLOCK_DIRENT_OFFSET_POISON; } return 0; } static void __dirblock_serialize_check_dirent(struct dirblock *block, size_t dirent) { size_t non_deleted_targets; size_t tgt; ASSERT3U(block->entries[dirent].dirent.namelen, <=, MAX_NAME_LEN); /* * Check that dirent's 'conflicts' and 'all_deleted' flags matche * the targets */ non_deleted_targets = 0; for (tgt = 0; tgt < block->entries[dirent].dirent.ntgts; tgt++) if (!block->entries[dirent].tgts[tgt].deleted) non_deleted_targets++; if (block->entries[dirent].dirent.conflicts) VERIFY3U(non_deleted_targets, >, 1); else VERIFY3U(non_deleted_targets, <=, 1); if (block->entries[dirent].dirent.all_deleted) VERIFY3U(non_deleted_targets, ==, 0); else VERIFY3U(non_deleted_targets, >=, 1); } int dirblock_serialize(struct dirblock *block, struct buffer *buf) { const size_t old_size = buffer_size(buf); struct obj_dir_header hdr; size_t off; size_t i, j; int ret; ASSERT3U(block->used_bytes, <=, DIR_BLOCK_SIZE); /* directory header */ memset(&hdr, 0, sizeof(struct obj_dir_header)); hdr.magic = cpu64_to_be(OBJ_DIR_MAGIC); hdr.ndirents = cpu16_to_be(block->ndirents); ret = buffer_append(buf, &hdr, sizeof(struct obj_dir_header)); if (ret) return ret; VERIFY3U(buffer_size(buf), ==, sizeof(struct obj_dir_header)); off = sizeof(struct obj_dir_header) + (block->ndirents * sizeof(struct ndirent_phys)); /* write out the fixed entries */ for (i = 0; i < block->ndirents; i++) { struct ndirent_phys dirent; struct buffer tmp; __dirblock_serialize_check_dirent(block, i); /* fill in the missing pieces */ block->entries[i].dirent.tgtoff = off + block->entries[i].dirent.namelen; block->entries[i].dirent.nameoff = off; dirent_cpu2be(&dirent, &block->entries[i].dirent); /* poison the block's memory entries to avoid accidents */ block->entries[i].dirent.tgtoff = DIRBLOCK_DIRENT_OFFSET_POISON; block->entries[i].dirent.nameoff = DIRBLOCK_DIRENT_OFFSET_POISON; ret = buffer_append(buf, &dirent, sizeof(struct ndirent_phys)); if (ret) return ret; /* determine how much space this dirent's targets will use */ buffer_init_sink(&tmp); for (j = 0; j < block->entries[i].dirent.ntgts; j++) { ret = pack_dirent_tgt(&tmp, &block->entries[i].tgts[j]); if (ret) return ret; } off += block->entries[i].dirent.namelen + buffer_size(&tmp); VERIFY3U(off, <=, DIR_BLOCK_SIZE); } /* write out the names and targets */ for (i = 0; i < block->ndirents; i++) { /* name */ ret = buffer_append(buf, block->entries[i].name, block->entries[i].dirent.namelen); if (ret) return ret; /* targets */ for (j = 0; j < block->entries[i].dirent.ntgts; j++) { ret = pack_dirent_tgt(buf, &block->entries[i].tgts[j]); if (ret) return ret; } } ASSERT3U(buffer_size(buf) - old_size, <=, DIR_BLOCK_SIZE); return buffer_truncate(buf, old_size + DIR_BLOCK_SIZE); } static ssize_t __find_dirent(struct dirblock *block, const char *name) { const size_t namelen = strlen(name); size_t i; for (i = 0; i < block->ndirents; i++) { if (namelen != block->entries[i].dirent.namelen) continue; if (strncmp(name, block->entries[i].name, namelen)) continue; return i; } return -ENOENT; } static ssize_t __find_dirent_target(struct ndirent_mem *dirent, const struct ndirent_tgt *tgts, const struct ndirent_tgt *match) { size_t i; for (i = 0; i < dirent->ntgts; i++) { if (match->type != tgts[i].type) continue; if ((match->type == NDIRENT_TYPE_GRAFT) && xuuid_compare(&match->graft, &tgts[i].graft)) continue; if ((match->type != NDIRENT_TYPE_GRAFT) && ((match->host != tgts[i].host) || (match->uniq != tgts[i].uniq))) continue; /* not comparing flags on purpose */ return i; } return -ENOENT; } int dirblock_add_dirent(struct dirblock *block, const char *name, const struct ndirent_tgt *tgt) { const size_t namelen = strlen(name); struct buffer tgtbuf; size_t space_needed; ssize_t ret; size_t idx; if (namelen > MAX_NAME_LEN) return -ENAMETOOLONG; ret = __find_dirent(block, name); if (ret >= 0) return -EEXIST; if (ret != -ENOENT) return ret; buffer_init_sink(&tgtbuf); ret = pack_dirent_tgt(&tgtbuf, tgt); if (ret) return ret; space_needed = sizeof(struct ndirent_phys); space_needed += namelen; space_needed += buffer_size(&tgtbuf); if ((block->used_bytes + space_needed) > DIR_BLOCK_SIZE) return -ENOSPC; /* the above space check should cover these */ VERIFY3U(block->ndirents + 1, <=, ARRAY_LEN(block->entries)); VERIFY3U(block->name_bytes + namelen, <, sizeof(block->names)); VERIFY3U(block->ntgts + 1, <=, ARRAY_LEN(block->tgts)); /* save the fixed sized dirent */ idx = block->ndirents; block->entries[idx].name = &block->names[block->name_bytes]; block->entries[idx].dirent.tgtoff = DIRBLOCK_DIRENT_OFFSET_POISON; block->entries[idx].dirent.ntgts = 1; block->entries[idx].dirent.nameoff = DIRBLOCK_DIRENT_OFFSET_POISON; block->entries[idx].dirent.namelen = namelen; block->entries[idx].dirent.conflicts = false; block->entries[idx].dirent.all_deleted = tgt->deleted; block->entries[idx].tgts = &block->tgts[block->ntgts]; /* save the name */ memcpy(&block->names[block->name_bytes], name, namelen); /* save the target */ block->tgts[block->ntgts] = *tgt; /* update block state */ block->used_bytes += space_needed; block->name_bytes += namelen; block->ndirents++; block->ntgts++; return 0; } int dirblock_add_dirent_target(struct dirblock *block, const char *name, const struct ndirent_tgt *tgt) { struct ndirent_mem *dirent; struct buffer tgtbuf; size_t space_needed; ssize_t idx; ssize_t tmp; size_t i; int ret; idx = __find_dirent(block, name); if (idx < 0) return idx; dirent = &block->entries[idx].dirent; tmp = __find_dirent_target(dirent, block->entries[idx].tgts, tgt); if (tmp >= 0) return -EEXIST; if (tmp != -ENOENT) return tmp; buffer_init_sink(&tgtbuf); ret = pack_dirent_tgt(&tgtbuf, tgt); if (ret) return ret; space_needed = buffer_size(&tgtbuf); if ((block->used_bytes + space_needed) > DIR_BLOCK_SIZE) return -ENOSPC; /* for each subsequent dirent */ for (i = block->ndirents - 1; i > idx; i--) { size_t size = sizeof(struct ndirent_tgt) * block->entries[idx].dirent.ntgts; /* shift the targets down one */ memmove(&block->entries[idx].tgts[1], &block->entries[idx].tgts[0], size); /* update the tgts pointer */ block->entries[idx].tgts = &block->entries[idx].tgts[1]; } /* save the target */ block->entries[idx].dirent.ntgts++; block->entries[idx].dirent.conflicts = block->entries[idx].dirent.conflicts || (!block->entries[idx].dirent.all_deleted && !tgt->deleted); block->entries[idx].dirent.all_deleted = block->entries[idx].dirent.all_deleted && tgt->deleted; block->entries[idx].tgts[block->entries[idx].dirent.ntgts - 1] = *tgt; /* update block state */ block->used_bytes += space_needed; block->ntgts++; return 0; }