#include "inode.h"
#include "malloc.h"
#include <sys/xattr.h>
+#include "libfrog/fsproperties.h"
+static int attr_get_f(int argc, char **argv);
static int attr_set_f(int argc, char **argv);
static int attr_remove_f(int argc, char **argv);
+static void attrget_help(void);
static void attrset_help(void);
+static const cmdinfo_t attr_get_cmd =
+ { "attr_get", "aget", attr_get_f, 1, -1, 0,
+ N_("[-r|-s|-u|-p|-Z] name"),
+ N_("get the named attribute on the current inode"), attrget_help };
static const cmdinfo_t attr_set_cmd =
{ "attr_set", "aset", attr_set_f, 1, -1, 0,
- N_("[-r|-s|-u|-p] [-n] [-R|-C] [-v n] name"),
+ N_("[-r|-s|-u|-p|-Z] [-n] [-R|-C] [-v n] name"),
N_("set the named attribute on the current inode"), attrset_help };
static const cmdinfo_t attr_remove_cmd =
{ "attr_remove", "aremove", attr_remove_f, 1, -1, 0,
- N_("[-r|-s|-u|-p] [-n] name"),
+ N_("[-r|-s|-u|-p|-Z] [-n] name"),
N_("remove the named attribute from the current inode"), attrset_help };
+static void
+attrget_help(void)
+{
+ dbprintf(_(
+"\n"
+" The attr_get command provide interfaces for retrieving the values of extended\n"
+" attributes of a file. This command requires attribute names to be specified.\n"
+" There are 4 namespace flags:\n"
+" -r -- 'root'\n"
+" -u -- 'user' (default)\n"
+" -s -- 'secure'\n"
+" -p -- 'parent'\n"
+" -Z -- fs property\n"
+"\n"));
+}
+
static void
attrset_help(void)
{
" -u -- 'user' (default)\n"
" -s -- 'secure'\n"
" -p -- 'parent'\n"
+" -Z -- fs property\n"
"\n"
" For attr_set, these options further define the type of set operation:\n"
" -C -- 'create' - create attribute, fail if it already exists\n"
" -R -- 'replace' - replace attribute, fail if it does not exist\n"
+"\n"
+" If the attribute value is a string, it can be specified after the\n"
+" attribute name.\n"
+"\n"
" The backward compatibility mode 'noattr2' can be emulated (-n) also.\n"
"\n"));
}
if (!expert_mode)
return;
+ add_command(&attr_get_cmd);
add_command(&attr_set_cmd);
add_command(&attr_remove_cmd);
}
LIBXFS_ATTR_ROOT | \
LIBXFS_ATTR_PARENT)
+static bool
+adjust_fsprop_attr_name(
+ struct xfs_da_args *args,
+ bool *free_name)
+{
+ const char *o = (const char *)args->name;
+ char *p;
+ int ret;
+
+ if ((args->attr_filter & LIBXFS_ATTR_NS) != LIBXFS_ATTR_ROOT) {
+ dbprintf(_("fs properties must be ATTR_ROOT\n"));
+ return false;
+ }
+
+ ret = fsprop_name_to_attr_name(o, &p);
+ if (ret < 0) {
+ dbprintf(_("could not allocate fs property name string\n"));
+ return false;
+ }
+ args->namelen = ret;
+ args->name = (const uint8_t *)p;
+
+ if (*free_name)
+ free((void *)o);
+ *free_name = true;
+
+ if (args->namelen > MAXNAMELEN) {
+ dbprintf(_("%s: name too long\n"), args->name);
+ return false;
+ }
+
+ if (args->valuelen > ATTR_MAX_VALUELEN) {
+ dbprintf(_("%s: value too long\n"), args->name);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+print_fsprop(
+ struct xfs_da_args *args)
+{
+ const char *p =
+ attr_name_to_fsprop_name((const char *)args->name);
+
+ if (p)
+ printf("%s=%.*s\n", p, args->valuelen, (char *)args->value);
+ else
+ fprintf(stderr, _("%s: not a fs property?\n"), args->name);
+}
+
static int
attr_set_f(
int argc,
char *sp;
char *name_from_file = NULL;
char *value_from_file = NULL;
+ bool free_name = false;
enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERT;
+ bool fsprop = false;
int c;
int error;
return 0;
}
- while ((c = getopt(argc, argv, "ruspCRnN:v:V:")) != EOF) {
+ while ((c = getopt(argc, argv, "ruspCRnN:v:V:Z")) != EOF) {
switch (c) {
/* namespaces */
+ case 'Z':
+ fsprop = true;
+ fallthrough;
case 'r':
args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_ROOT;
if (!args.name)
return 0;
+ free_name = true;
args.namelen = namelen;
} else {
- if (optind != argc - 1) {
+ if (optind != argc - 1 && optind != argc - 2) {
dbprintf(_("too few options for attr_set (no name given)\n"));
return 0;
}
goto out;
}
memset(args.value, 'v', args.valuelen);
+ } else if (optind == argc - 2) {
+ args.valuelen = strlen(argv[optind + 1]);
+ args.value = strdup(argv[optind + 1]);
+ if (!args.value) {
+ dbprintf(_("cannot allocate buffer (%d)\n"),
+ args.valuelen);
+ goto out;
+ }
+ }
+
+ if (fsprop) {
+ if (!fsprop_validate((const char *)args.name, args.value)) {
+ dbprintf(_("%s: invalid value \"%s\"\n"),
+ args.name, args.value);
+ goto out;
+ }
+
+ if (!adjust_fsprop_attr_name(&args, &free_name))
+ goto out;
}
if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
goto out;
}
+ if (fsprop)
+ print_fsprop(&args);
+
/* refresh with updated inode contents */
set_cur_inode(iocur_top->ino);
libxfs_irele(args.dp);
if (args.value)
free(args.value);
- if (name_from_file)
+ if (free_name)
free((void *)args.name);
return 0;
}
.op_flags = XFS_DA_OP_OKNOENT,
};
char *name_from_file = NULL;
+ bool free_name = false;
+ bool fsprop = false;
int c;
int error;
return 0;
}
- while ((c = getopt(argc, argv, "ruspnN:")) != EOF) {
+ while ((c = getopt(argc, argv, "ruspnN:Z")) != EOF) {
switch (c) {
/* namespaces */
+ case 'Z':
+ fsprop = true;
+ fallthrough;
case 'r':
args.attr_filter &= ~LIBXFS_ATTR_NS;
args.attr_filter |= LIBXFS_ATTR_ROOT;
if (!args.name)
return 0;
+ free_name = true;
args.namelen = namelen;
} else {
if (optind != argc - 1) {
}
}
+ if (fsprop && !adjust_fsprop_attr_name(&args, &free_name))
+ goto out;
+
if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
dbprintf(_("failed to iget inode %llu\n"),
(unsigned long long)iocur_top->ino);
out:
if (args.dp)
libxfs_irele(args.dp);
- if (name_from_file)
+ if (free_name)
+ free((void *)args.name);
+ return 0;
+}
+
+static int
+attr_get_f(
+ int argc,
+ char **argv)
+{
+ struct xfs_da_args args = {
+ .geo = mp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .op_flags = XFS_DA_OP_OKNOENT,
+ };
+ char *name_from_file = NULL;
+ bool free_name = false;
+ bool fsprop = false;
+ int c;
+ int error;
+
+ if (cur_typ == NULL) {
+ dbprintf(_("no current type\n"));
+ return 0;
+ }
+ if (cur_typ->typnm != TYP_INODE) {
+ dbprintf(_("current type is not inode\n"));
+ return 0;
+ }
+
+ while ((c = getopt(argc, argv, "ruspN:Z")) != EOF) {
+ switch (c) {
+ /* namespaces */
+ case 'Z':
+ fsprop = true;
+ fallthrough;
+ case 'r':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= LIBXFS_ATTR_ROOT;
+ break;
+ case 'u':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ break;
+ case 's':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= LIBXFS_ATTR_SECURE;
+ break;
+ case 'p':
+ args.attr_filter &= ~LIBXFS_ATTR_NS;
+ args.attr_filter |= XFS_ATTR_PARENT;
+ break;
+
+ case 'N':
+ name_from_file = optarg;
+ break;
+ default:
+ dbprintf(_("bad option for attr_get command\n"));
+ return 0;
+ }
+ }
+
+ if (name_from_file) {
+ int namelen;
+
+ if (optind != argc) {
+ dbprintf(_("too many options for attr_get (no name needed)\n"));
+ return 0;
+ }
+
+ args.name = get_buf_from_file(name_from_file, MAXNAMELEN,
+ &namelen);
+ if (!args.name)
+ return 0;
+
+ free_name = true;
+ args.namelen = namelen;
+ } else {
+ if (optind != argc - 1) {
+ dbprintf(_("too few options for attr_get (no name given)\n"));
+ return 0;
+ }
+
+ args.name = (const unsigned char *)argv[optind];
+ if (!args.name) {
+ dbprintf(_("invalid name\n"));
+ return 0;
+ }
+
+ args.namelen = strlen(argv[optind]);
+ if (args.namelen >= MAXNAMELEN) {
+ dbprintf(_("name too long\n"));
+ goto out;
+ }
+ }
+
+ if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
+ dbprintf(_("failed to iget inode %llu\n"),
+ (unsigned long long)iocur_top->ino);
+ goto out;
+ }
+
+ if (fsprop && !adjust_fsprop_attr_name(&args, &free_name))
+ goto out;
+
+ args.owner = iocur_top->ino;
+ libxfs_attr_sethash(&args);
+
+ /*
+ * Look up attr value with a maximally long length and a null buffer
+ * to return the value and the correct length.
+ */
+ args.valuelen = XATTR_SIZE_MAX;
+ error = -libxfs_attr_get(&args);
+ if (error) {
+ dbprintf(_("failed to get attr %s on inode %llu: %s\n"),
+ args.name, (unsigned long long)iocur_top->ino,
+ strerror(error));
+ goto out;
+ }
+
+ if (fsprop)
+ print_fsprop(&args);
+ else
+ printf("%.*s\n", args.valuelen, (char *)args.value);
+
+out:
+ if (args.dp)
+ libxfs_irele(args.dp);
+ if (args.value)
+ free(args.value);
+ if (free_name)
free((void *)args.name);
return 0;
}
reservation usage for a given AG.
If no argument is given, display information for all AGs.
.TP
-.BI "attr_remove [\-p|\-r|\-u|\-s] [\-n] [\-N " namefile "|" name "] "
+.BI "attr_get [\-p|\-r|\-u|\-s|\-Z] [\-N " namefile "|" name "] "
+Print the value of the specified extended attribute from the current file.
+.RS 1.0i
+.TP 0.4i
+.B \-p
+Sets the attribute in the parent namespace.
+Only one namespace option can be specified.
+.TP
+.B \-r
+Sets the attribute in the root namespace.
+Only one namespace option can be specified.
+.TP
+.B \-u
+Sets the attribute in the user namespace.
+Only one namespace option can be specified.
+.TP
+.B \-s
+Sets the attribute in the secure namespace.
+Only one namespace option can be specified.
+.TP
+.B \-Z
+Sets a filesystem property in the root namespace.
+Only one namespace option can be specified.
+.TP
+.B \-N
+Read the name from this file.
+.RE
+.TP
+.BI "attr_remove [\-p|\-r|\-u|\-s|\-Z] [\-n] [\-N " namefile "|" name "] "
Remove the specified extended attribute from the current file.
.RS 1.0i
.TP 0.4i
Sets the attribute in the secure namespace.
Only one namespace option can be specified.
.TP
+.B \-Z
+Sets a filesystem property in the root namespace.
+Only one namespace option can be specified.
+.TP
.B \-N
Read the name from this file.
.TP
Do not enable 'noattr2' mode on V4 filesystems.
.RE
.TP
-.BI "attr_set [\-p\-r|\-u|\-s] [\-n] [\-R|\-C] [\-v " valuelen "|\-V " valuefile "] [\-N " namefile "|" name "] "
+.BI "attr_set [\-p\-r|\-u|\-s|\-Z] [\-n] [\-R|\-C] [\-v " valuelen "|\-V " valuefile "] [\-N " namefile "|" name "] [" value "]"
Sets an extended attribute on the current file with the given name.
.RS 1.0i
.TP 0.4i
Sets the attribute in the secure namespace.
Only one namespace option can be specified.
.TP
+.B \-Z
+Sets a filesystem property in the root namespace.
+Only one namespace option can be specified.
+.TP
.B \-N
Read the name from this file.
.TP