view src/objstore/dir.c @ 862:9a63d933a60e

objstore: expose dirent add/update helpers to the rest of objstore These are useful anytime we want to create a new directory entry - create, link, symlink, etc. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sat, 17 Dec 2022 17:02:07 -0500
parents
children 77a35abc6a24
line wrap: on
line source

/*
 * Copyright (c) 2015-2020,2022 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"

int dir_add_dirent(struct txn *txn, struct objver *dirver, const char *name,
		   uint16_t mode, const struct noid *child)
{
	uint8_t rawbuf[DIR_BLOCK_SIZE];
	struct nattr attrs = {
		.size = dirver->attrs.size + DIR_BLOCK_SIZE,
	};
	struct ndirent_tgt tgt;
	struct dirblock block;
	struct buffer buf;
	ssize_t ret;

	/* TODO: try to put it into an existing block */

	buffer_init_static(&buf, rawbuf, 0, sizeof(rawbuf), true);

	dirblock_init(&block);

	tgt.type = mode >> NATTR_TSHIFT;
	tgt.deleted = false;
	tgt.host = noid_get_allocator(child);
	tgt.uniq = noid_get_uniq(child);

	ret = dirblock_add_dirent(&block, name, &tgt);
	if (ret)
		return ret;

	ret = dirblock_serialize(&block, &buf);
	if (ret)
		return ret;

	/* grow the directory by a block */
	obj_setattr(txn, dirver, &attrs, OBJ_ATTR_SIZE);

	/* fill in the fresh block */
	ret = obj_write(txn, dirver, buffer_data(&buf), buffer_size(&buf),
			dirver->attrs.size - DIR_BLOCK_SIZE);
	if (ret < 0)
		return ret;

	if (ret != buffer_size(&buf))
		panic("dir block partial write!");

	return 0;
}

static int __dir_update_dirent(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;
}

int dir_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)
{
	uint8_t rawbuf[DIR_BLOCK_SIZE];
	struct dirblock block;
	struct buffer buffer;
	ssize_t ret;

	/* parse the block */
	ret = dirblock_parse(&block, raw, ndirents);
	if (ret)
		return ret;

	/* update the dirent target */
	ret = __dir_update_dirent(&block, name, mode, child);
	if (ret)
		return ret;

	/* serialize the block & write it out */
	buffer_init_static(&buffer, rawbuf, 0, sizeof(rawbuf), true);

	ret = dirblock_serialize(&block, &buffer);
	if (ret)
		return ret;

	ret = obj_write(txn, dirver, buffer_data(&buffer), buffer_size(&buffer),
			diroff);
	if (ret < 0)
		return ret;

	if (ret != buffer_size(&buffer))
		panic("dir block partial write!");

	return 0;
}