view src/objstore/obj_dir_unlink.c @ 871:7f43822eef34

objstore: add a FIXME about decrementing the unlink target's nlinks Not doing this is currently fine because: 1. nothing will have a link count >1 as we don't support hardlinks yet 2. everything we unlink will cease to exist, so not writing a 0 nlinks is ok Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sat, 17 Dec 2022 20:19:31 -0500
parents 75e654e85cab
children
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"

static int __dir_unlink_update(struct dirblock *block, const char *name,
			       const struct noid *desired, bool rmdir)
{
	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->deleted)
			continue;

		if (noid_is_null(desired))
			break; /* found it */

		/* looking for a specific target */

		if (tgt->type == NDIRENT_TYPE_GRAFT) {
			if (xuuid_compare(&tgt->graft, noid_get_vol(desired)))
				continue;
		} else {
			if ((tgt->host != noid_get_allocator(desired)) ||
			    (tgt->uniq != noid_get_uniq(desired)))
				continue;
		}
	}

	VERIFY3U(j, <, block->entries[i].dirent.ntgts);
	VERIFY(!block->entries[i].tgts[j].deleted);

	if (rmdir && (block->entries[i].tgts[j].type != NDIRENT_TYPE_DIR))
		return -ENOTDIR;
	if (!rmdir && (block->entries[i].tgts[j].type == NDIRENT_TYPE_DIR))
		return -EISDIR;

	if (!rmdir)
		FIXME("need to decrement the target's link count");

	/* update the target */
	block->entries[i].tgts[j].deleted = true;

	non_deleted_targets = 0;
	for (j = 0; j < block->entries[i].dirent.ntgts; j++)
		if (!block->entries[i].tgts[j].deleted)
			non_deleted_targets++;

	if (!block->entries[i].dirent.conflicts)
		VERIFY3U(non_deleted_targets, ==, 0);

	block->entries[i].dirent.conflicts = (non_deleted_targets > 1);
	block->entries[i].dirent.all_deleted = (non_deleted_targets == 0);

	return 0;
}

int dir_unlink(struct txn *txn, struct objver *dirver, const char *name,
	       const struct noid *desired, bool rmdir)
{
	uint8_t raw[DIR_BLOCK_SIZE];
	struct ndirent_mem ent;
	struct dirblock block;
	struct buffer buffer;
	uint64_t diroff;
	uint16_t ndirents;
	ssize_t ret;

	ret = dir_lookup_entry(dirver, name, raw, &ent, &diroff,
			       &ndirents);
	if (ret)
		return ret;

	if (ent.all_deleted)
		return -ENOENT;

	if (ent.conflicts && noid_is_null(desired))
		return -ENOTUNIQ;

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

	/* update the dirent target */
	ret = __dir_unlink_update(&block, name, desired, rmdir);
	if (ret)
		return ret;

	/* serialize the block & write it out */
	buffer_init_static(&buffer, raw, 0, sizeof(raw), 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;
}