Viewing: liblustreapi_pool.c

// SPDX-License-Identifier: LGPL-2.1+
/*
 * This file is part of Lustre, http://www.lustre.org/
 *
 * Pool pinning helpers for liblustreapi.
 */

#include <sys/types.h>
#include <sys/xattr.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

#include <lnetconfig/cyaml.h>
#include <linux/lustre/lustre_user.h>

#include "lustreapi_internal.h"

#define PIN_YAML_POOL_STR	"pool"

static int pool_pin_ensure_entry(struct cYAML *yaml, const char *pool_name,
				 char *buff, size_t bufflen)
{
	struct cYAML *node;

	/* Search for an existing entry. */
	for (node = yaml->cy_child; node != NULL; node = node->cy_next) {
		if (strcmp(node->cy_string, PIN_YAML_POOL_STR) == 0 &&
		    node->cy_type == CYAML_TYPE_STRING &&
		    node->cy_valuestring != NULL &&
		    strcmp(node->cy_valuestring, pool_name) == 0)
			break;
	}

	/* If not found, create a new entry. */
	if (node == NULL) {
		node = cYAML_create_string(yaml, (char *)PIN_YAML_POOL_STR,
					   (char *)pool_name);
		if (node == NULL)
			return -errno ? -errno : -ENOMEM;
	}

	/* Serialise the (possibly updated) YAML object into buff. */
	errno = -dump_pin_object(yaml, buff, bufflen);
	if (errno)
		return -errno;

	/* buff now contains the new serialised lustre.pin value. */
	return 0;
}

static int pool_unpin_update_entry(struct cYAML *yaml, const char *pool_name,
				   char *buff, size_t bufflen)
{
	struct cYAML *node;

	/* We have a valid pin object, search for the entry to be deleted. */
	for (node = yaml->cy_child; node != NULL; node = node->cy_next) {
		if (strcmp(node->cy_string, PIN_YAML_POOL_STR) == 0 &&
		    node->cy_type == CYAML_TYPE_STRING &&
		    node->cy_valuestring != NULL &&
		    strcmp(node->cy_valuestring, pool_name) == 0)
			break;
	}
	if (node == NULL) {
		/* No matching pool entry in lustre.pin. */
		errno = ENOENT;
		return -ENOENT;
	}

	/* Remove the node from the YAML tree. */
	if (node == yaml->cy_child) {
		/* the first child */
		if (node->cy_next)
			node->cy_next->cy_prev = NULL;
		yaml->cy_child = node->cy_next;
	} else {
		/* not the first child */
		node->cy_prev->cy_next = node->cy_next;
		if (node->cy_next)
			node->cy_next->cy_prev = node->cy_prev;
	}
	node->cy_prev = node->cy_next = NULL;
	cYAML_free_tree(node);

	errno = -dump_pin_object(yaml, buff, bufflen);
	if (errno)
		return -errno;

	return 0;
}

static int pool_is_pinned_from_yaml(struct cYAML *yaml, const char *pool_name)
{
	struct cYAML *node;
	int pinned = 0;

	for (node = yaml->cy_child; node != NULL; node = node->cy_next) {
		if (strcmp(node->cy_string, PIN_YAML_POOL_STR) == 0 &&
		    node->cy_type == CYAML_TYPE_STRING &&
		    node->cy_valuestring != NULL &&
		    strcmp(node->cy_valuestring, pool_name) == 0) {
			pinned = 1;
			break;
		}
	}

	cYAML_free_tree(yaml);
	return pinned;
}

/**
 * llapi_pool_pin_fd() - Pin a file for a given pool name using an open fd.
 * @fd: Open file descriptor for a Lustre file.
 * @pool_name: Name of the pool to pin for (optionally fsname-qualified).
 *
 * Ensure that a "pool: pool_name" entry exists in the lustre.pin xattr of the
 * file referenced by @fd. If such an entry already exists, this function
 * succeeds without modifying the xattr.
 *
 * Return: 0 on success; on failure, a negative errno value is returned and
 *         errno is set to the corresponding error code.
 */
int llapi_pool_pin_fd(int fd, const char *pool_name)
{
	int rc = 0;
	struct cYAML *yaml = NULL;
	char buff[XATTR_SIZE_MAX];

	if (!llapi_pool_name_is_valid(&pool_name)) {
		errno = EINVAL;
		return -errno;
	}

	yaml = read_pin_xattr_object_fd(fd);

	if (yaml == NULL && errno == ENODATA) {
		snprintf(buff, sizeof(buff), "[%s: %s]", PIN_YAML_POOL_STR,
			 pool_name);
	} else if (yaml == NULL) {
		return -errno;
	} else {
		rc = pool_pin_ensure_entry(yaml, pool_name, buff, sizeof(buff));
		if (rc < 0)
			goto out;
	}

	if (fsetxattr(fd, XATTR_LUSTRE_PIN, buff, strlen(buff), 0) < 0)
		rc = -errno;
	else
		rc = 0;

out:
	if (yaml)
		cYAML_free_tree(yaml);
	return rc;
}

/**
 * llapi_pool_pin_file() - Pin a file for a given pool name using a path.
 * @path: Path to a Lustre file.
 * @pool_name: Name of the pool to pin for (optionally fsname-qualified).
 *
 * Ensure that a "pool: pool_name" entry exists in the lustre.pin xattr of the
 * file at @path. If such an entry already exists, this function succeeds
 * without modifying the xattr.
 *
 * Return: 0 on success; on failure, a negative errno value is returned and
 *         errno is set to the corresponding error code.
 */
int llapi_pool_pin_file(const char *path, const char *pool_name)
{
	int rc = 0;
	struct cYAML *yaml = NULL;
	char buff[XATTR_SIZE_MAX];

	if (!llapi_pool_name_is_valid(&pool_name)) {
		errno = EINVAL;
		return -errno;
	}

	yaml = read_pin_xattr_object(path);

	if (yaml == NULL && errno == ENODATA) {
		snprintf(buff, sizeof(buff), "[%s: %s]", PIN_YAML_POOL_STR,
			 pool_name);
	} else if (yaml == NULL) {
		llapi_error(LLAPI_MSG_ERROR, errno,
			    "cannot read or parse pin xattr of file '%s'.",
			    path);
		rc = -errno;
		goto out;
	} else {
		rc = pool_pin_ensure_entry(yaml, pool_name, buff, sizeof(buff));
		if (rc < 0)
			goto out;
	}

	if (setxattr(path, XATTR_LUSTRE_PIN, buff, strlen(buff), 0) < 0)
		rc = -errno;
	else
		rc = 0;

out:
	if (yaml)
		cYAML_free_tree(yaml);
	return rc;
}

/**
 * llapi_pool_pin_fid() - Pin a file for a given pool name using its FID.
 * @lustre_dir: Path within the target Lustre filesystem (usually the mount).
 * @fid:        FID of the file to be pinned.
 * @pool_name:  Name of the pool to pin for (optionally fsname-qualified).
 *
 * Open the object referenced by @fid in @lustre_dir, then ensure a
 * corresponding "pool: pool_name" entry exists in its lustre.pin xattr.
 *
 * Return: 0 on success; on failure, a negative errno value is returned and
 *         errno is set to the corresponding error code.
 */
int llapi_pool_pin_fid(const char *lustre_dir, const struct lu_fid *fid,
		       const char *pool_name)
{
	int fd;
	int rc;

	fd = llapi_open_by_fid(lustre_dir, fid, O_RDONLY | O_NONBLOCK);
	if (fd < 0)
		return fd; /* negative errno, errno set by helper */

	rc = llapi_pool_pin_fd(fd, pool_name);
	if (rc < 0) {
		int saved_errno = errno;

		close(fd);
		errno = saved_errno;
		return rc;
	}

	close(fd);
	return 0;
}

/**
 * llapi_pool_unpin_fd() - Remove a pool pin from a file (by fd).
 * @fd: Open file descriptor for a Lustre file.
 * @pool_name: Name of the pool to unpin (optionally fsname-qualified).
 *
 * Remove the "pool: pool_name" entry from the lustre.pin xattr of the file
 * referenced by @fd. If no matching entry exists (or the attribute is
 * missing), this is treated as an error and ENOENT is reported. When the last
 * entry is removed, the lustre.pin xattr itself is deleted.
 *
 * Return: 0 on success; on failure, a negative errno value is returned and
 *         errno is set to the corresponding error code.
 */
int llapi_pool_unpin_fd(int fd, const char *pool_name)
{
	int rc = 0;
	struct cYAML *yaml = NULL;
	char buff[XATTR_SIZE_MAX];

	if (!llapi_pool_name_is_valid(&pool_name)) {
		errno = EINVAL;
		return -errno;
	}

	yaml = read_pin_xattr_object_fd(fd);

	if (yaml == NULL && errno == ENODATA) {
		errno = ENOENT;
		return -errno;
	}
	if (yaml == NULL)
		return -errno;

	rc = pool_unpin_update_entry(yaml, pool_name, buff, sizeof(buff));
	if (rc < 0)
		goto out;

	if (strlen(buff) == 0) {
		if (fremovexattr(fd, XATTR_LUSTRE_PIN) < 0)
			rc = -errno;
		else
			rc = 0;
	} else {
		if (fsetxattr(fd, XATTR_LUSTRE_PIN, buff, strlen(buff), 0) < 0)
			rc = -errno;
		else
			rc = 0;
	}

out:
	if (yaml)
		cYAML_free_tree(yaml);
	return rc;
}

/**
 * llapi_pool_unpin_file() - Remove a pool pin from a file.
 * @path: Path to a Lustre file.
 * @pool_name: Name of the pool to unpin (optionally fsname-qualified).
 *
 * Remove the "pool: pool_name" entry from the lustre.pin xattr of @path. If
 * no matching entry exists (or the attribute is missing), this is treated as
 * an error and ENOENT is reported. When the last entry is removed, the
 * lustre.pin xattr itself is deleted.
 *
 * Return: 0 on success; on failure, a negative errno value is returned and
 *         errno is set to the corresponding error code.
 */
int llapi_pool_unpin_file(const char *path, const char *pool_name)
{
	int rc = 0;
	struct cYAML *yaml = NULL;
	char buff[XATTR_SIZE_MAX];

	if (!llapi_pool_name_is_valid(&pool_name)) {
		errno = EINVAL;
		return -errno;
	}

	yaml = read_pin_xattr_object(path);

	if (yaml == NULL && errno == ENODATA) {
		errno = ENOENT;
		rc = -errno;
		goto out;
	}
	if (yaml == NULL) {
		llapi_error(LLAPI_MSG_ERROR, errno,
			    "cannot read or parse pin xattr of file '%s'.",
			    path);
		rc = -errno;
		goto out;
	}

	rc = pool_unpin_update_entry(yaml, pool_name, buff, sizeof(buff));
	if (rc < 0)
		goto out;

	if (strlen(buff) == 0) {
		if (removexattr(path, XATTR_LUSTRE_PIN) < 0)
			rc = -errno;
		else
			rc = 0;
	} else {
		if (setxattr(path, XATTR_LUSTRE_PIN, buff, strlen(buff), 0) < 0)
			rc = -errno;
		else
			rc = 0;
	}

out:
	if (yaml)
		cYAML_free_tree(yaml);
	return rc;
}

/**
 * llapi_pool_unpin_fid() - Remove a pool pin from a FID-referenced file.
 * @lustre_dir: Path within the target Lustre filesystem (usually the mount).
 * @fid:        FID of the file to be unpinned.
 * @pool_name:  Name of the pool to unpin (optionally fsname-qualified).
 *
 * Open the object referenced by @fid in @lustre_dir and remove the
 * corresponding "pool: pool_name" entry from its lustre.pin xattr.
 *
 * Return: 0 on success; on failure, a negative errno value is returned and
 *         errno is set to the corresponding error code.
 */
int llapi_pool_unpin_fid(const char *lustre_dir, const struct lu_fid *fid,
			 const char *pool_name)
{
	int fd;
	int rc;

	fd = llapi_open_by_fid(lustre_dir, fid, O_RDONLY | O_NONBLOCK);
	if (fd < 0)
		return fd;

	rc = llapi_pool_unpin_fd(fd, pool_name);
	if (rc < 0) {
		int saved_errno = errno;

		close(fd);
		errno = saved_errno;
		return rc;
	}

	close(fd);
	return 0;
}

/**
 * llapi_pool_is_pinned_fd() - Test if a file is pinned for a pool name (fd).
 * @fd: Open file descriptor for a Lustre file.
 * @pool_name: Pool name to test (optionally fsname-qualified).
 *
 * Check whether the lustre.pin xattr of the file referenced by @fd contains
 * a "pool: pool_name" entry.
 *
 * Return: 1 if such an entry exists, 0 if not. On error, a negative errno
 *         value is returned.
 */
int llapi_pool_is_pinned_fd(int fd, const char *pool_name)
{
	struct cYAML *yaml;

	if (!llapi_pool_name_is_valid(&pool_name)) {
		errno = EINVAL;
		return -errno;
	}

	yaml = read_pin_xattr_object_fd(fd);
	if (yaml == NULL) {
		/*
		 * No lustre.pin attribute: treat as "not pinned".
		 * Any other error is propagated as -errno.
		 */
		if (errno == ENODATA)
			return 0;
		return -errno;
	}

	return pool_is_pinned_from_yaml(yaml, pool_name);
}

/**
 * llapi_pool_is_pinned_file() - Test if a file is pinned for a pool name.
 * @path: Path to a Lustre file.
 * @pool_name: Pool name to test (optionally fsname-qualified).
 *
 * Check whether the lustre.pin xattr of the file at @path contains a
 * "pool: pool_name" entry.
 *
 * Return: 1 if such an entry exists, 0 if not. On error, a negative errno
 *         value is returned.
 */
int llapi_pool_is_pinned_file(const char *path, const char *pool_name)
{
	struct cYAML *yaml;

	if (!llapi_pool_name_is_valid(&pool_name)) {
		errno = EINVAL;
		return -errno;
	}

	yaml = read_pin_xattr_object(path);
	if (yaml == NULL) {
		if (errno == ENODATA)
			return 0;
		return -errno;
	}

	return pool_is_pinned_from_yaml(yaml, pool_name);
}

/**
 * llapi_pool_is_pinned_fid() - Test if a FID-referenced file is pool-pinned.
 * @lustre_dir: Path within the target Lustre filesystem (usually the mount).
 * @fid:        FID of the file to be checked.
 * @pool_name:  Pool name to test (optionally fsname-qualified).
 *
 * Open the object referenced by @fid in @lustre_dir and check whether its
 * lustre.pin xattr contains a "pool: pool_name" entry.
 *
 * Return: 1 if such an entry exists, 0 if not. On error, a negative errno
 *         value is returned, including failures from llapi_open_by_fid().
 */
int llapi_pool_is_pinned_fid(const char *lustre_dir, const struct lu_fid *fid,
			     const char *pool_name)
{
	int fd;
	int pinned;

	fd = llapi_open_by_fid(lustre_dir, fid, O_RDONLY | O_NONBLOCK);
	if (fd < 0)
		return fd;

	pinned = llapi_pool_is_pinned_fd(fd, pool_name);
	if (pinned < 0) {
		int saved_errno = errno;

		close(fd);
		errno = saved_errno;
		return pinned;
	}

	close(fd);
	return pinned;
}