@@ -17,20 +17,43 @@
#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)
{
@@ -45,10 +68,15 @@ 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"));
}
@@ -59,6 +87,7 @@ attrset_init(void)
if (!expert_mode)
return;
+ add_command(&attr_get_cmd);
add_command(&attr_set_cmd);
add_command(&attr_remove_cmd);
}
@@ -106,6 +135,58 @@ get_buf_from_file(
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,
@@ -119,7 +200,9 @@ attr_set_f(
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;
@@ -132,9 +215,12 @@ attr_set_f(
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;
@@ -213,9 +299,10 @@ attr_set_f(
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;
}
@@ -250,6 +337,25 @@ attr_set_f(
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)) {
@@ -269,6 +375,9 @@ attr_set_f(
goto out;
}
+ if (fsprop)
+ print_fsprop(&args);
+
/* refresh with updated inode contents */
set_cur_inode(iocur_top->ino);
@@ -277,7 +386,7 @@ attr_set_f(
libxfs_irele(args.dp);
if (args.value)
free(args.value);
- if (name_from_file)
+ if (free_name)
free((void *)args.name);
return 0;
}
@@ -293,6 +402,8 @@ attr_remove_f(
.op_flags = XFS_DA_OP_OKNOENT,
};
char *name_from_file = NULL;
+ bool free_name = false;
+ bool fsprop = false;
int c;
int error;
@@ -305,9 +416,12 @@ attr_remove_f(
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;
@@ -354,6 +468,7 @@ attr_remove_f(
if (!args.name)
return 0;
+ free_name = true;
args.namelen = namelen;
} else {
if (optind != argc - 1) {
@@ -374,6 +489,9 @@ attr_remove_f(
}
}
+ 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);
@@ -398,7 +516,137 @@ attr_remove_f(
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;
}
@@ -184,7 +184,35 @@ Displays the length, free block count, per-AG reservation size, and per-AG
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
@@ -204,6 +232,10 @@ Only one namespace option can be specified.
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
@@ -211,7 +243,7 @@ Read the name from this file.
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
@@ -231,6 +263,10 @@ Only one namespace option can be specified.
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