blob: f59fbd9c21650e31a8a71f14d636ccbaa1371596 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#include "xfs.h"
#include "handle.h"
#include "libfrog/fsgeom.h"
#include "libfrog/paths.h"
#include "libfrog/bulkstat.h"
#include "libfrog/fsprops.h"
#include "libfrog/fsproperties.h"
#include "libfrog/fakelibattr.h"
/*
* Given an xfd and a mount table path, get us the handle for the root dir so
* we can set filesystem properties.
*/
int
fsprops_open_handle(
struct xfs_fd *xfd,
struct fs_path *fs_path,
struct fsprops_handle *fph)
{
struct xfs_bulkstat bulkstat;
struct stat sb;
int ret;
/* fs properties only supported on V5 filesystems */
if (!(xfd->fsgeom.flags & XFS_FSOP_GEOM_FLAGS_V5SB)) {
errno = EOPNOTSUPP;
return -1;
}
ret = -xfrog_bulkstat_single(xfd, XFS_BULK_IREQ_SPECIAL_ROOT,
XFS_BULK_IREQ_SPECIAL, &bulkstat);
if (ret)
return ret;
ret = fstat(xfd->fd, &sb);
if (ret)
return ret;
if (sb.st_ino != bulkstat.bs_ino) {
errno = ESRMNT;
return -1;
}
return fd_to_handle(xfd->fd, &fph->hanp, &fph->hlen);
}
/* Free a fsproperties handle. */
void
fsprops_free_handle(
struct fsprops_handle *fph)
{
if (fph->hanp)
free_handle(fph->hanp, fph->hlen);
fph->hanp = NULL;
fph->hlen = 0;
}
/* Call the given callback on every fs property accessible through the handle. */
int
fsprops_walk_names(
struct fsprops_handle *fph,
fsprops_name_walk_fn walk_fn,
void *priv)
{
struct xfs_attrlist_cursor cur = { };
struct xfs_attrlist *attrlist;
int ret;
attrlist = calloc(XFS_XATTR_LIST_MAX, 1);
if (!attrlist)
return -1;
while ((ret = libfrog_attr_list_by_handle(fph->hanp, fph->hlen,
attrlist, XFS_XATTR_LIST_MAX,
XFS_IOC_ATTR_ROOT, &cur)) == 0) {
unsigned int i;
for (i = 0; i < attrlist->al_count; i++) {
struct xfs_attrlist_ent *ent =
libfrog_attr_entry(attrlist, i);
const char *p =
attr_name_to_fsprop_name(ent->a_name);
if (p) {
ret = walk_fn(fph, p, ent->a_valuelen, priv);
if (ret)
break;
}
}
if (!attrlist->al_more)
break;
}
free(attrlist);
return ret;
}
/* Retrieve the value of a specific fileystem property. */
int
fsprops_get(
struct fsprops_handle *fph,
const char *name,
void *valuebuf,
size_t *valuelen)
{
struct xfs_attr_multiop ops = {
.am_opcode = ATTR_OP_GET,
.am_attrvalue = valuebuf,
.am_length = *valuelen,
.am_flags = XFS_IOC_ATTR_ROOT,
};
int ret;
ret = fsprop_name_to_attr_name(name, (char **)&ops.am_attrname);
if (ret < 0)
return ret;
ret = attr_multi_by_handle(fph->hanp, fph->hlen, &ops, 1, 0);
if (ret < 0)
goto out_name;
if (ops.am_error) {
errno = -ops.am_error;
ret = -1;
goto out_name;
}
*valuelen = ops.am_length;
out_name:
free(ops.am_attrname);
return ret;
}
/* Set the value of a specific fileystem property. */
int
fsprops_set(
struct fsprops_handle *fph,
const char *name,
void *valuebuf,
size_t valuelen)
{
struct xfs_attr_multiop ops = {
.am_opcode = ATTR_OP_SET,
.am_attrvalue = valuebuf,
.am_length = valuelen,
.am_flags = XFS_IOC_ATTR_ROOT,
};
int ret;
ret = fsprop_name_to_attr_name(name, (char **)&ops.am_attrname);
if (ret < 0)
return ret;
ret = attr_multi_by_handle(fph->hanp, fph->hlen, &ops, 1, 0);
if (ret < 0)
goto out_name;
if (ops.am_error) {
errno = -ops.am_error;
ret = -1;
goto out_name;
}
out_name:
free(ops.am_attrname);
return ret;
}
/* Unset the value of a specific fileystem property. */
int
fsprops_remove(
struct fsprops_handle *fph,
const char *name)
{
struct xfs_attr_multiop ops = {
.am_opcode = ATTR_OP_REMOVE,
.am_flags = XFS_IOC_ATTR_ROOT,
};
int ret;
ret = fsprop_name_to_attr_name(name, (char **)&ops.am_attrname);
if (ret < 0)
return ret;
ret = attr_multi_by_handle(fph->hanp, fph->hlen, &ops, 1, 0);
if (ret < 0)
goto out_name;
if (ops.am_error) {
errno = -ops.am_error;
ret = -1;
goto out_name;
}
out_name:
free(ops.am_attrname);
return ret;
}