@@ -16,10 +16,12 @@
#include "field.h"
#include "inode.h"
#include "malloc.h"
+#include "write.h"
#include <sys/xattr.h>
static int attr_set_f(int argc, char **argv);
static int attr_remove_f(int argc, char **argv);
+static int attr_modify_f(int argc, char **argv);
static void attrset_help(void);
static const cmdinfo_t attr_set_cmd =
@@ -30,6 +32,11 @@ static const cmdinfo_t attr_remove_cmd =
{ "attr_remove", "aremove", attr_remove_f, 1, -1, 0,
N_("[-r|-s|-u|-p] [-n] name"),
N_("remove the named attribute from the current inode"), attrset_help };
+static const cmdinfo_t attr_modify_cmd =
+ { "attr_modify", "amodify", attr_modify_f, 1, -1, 0,
+ N_("[-r|-s|-u] [-o n] [-v n] [-m n] name value"),
+ N_("modify value of the named attribute of the current inode"),
+ attrset_help };
static void
attrset_help(void)
@@ -38,8 +45,9 @@ attrset_help(void)
"\n"
" The 'attr_set' and 'attr_remove' commands provide interfaces for debugging\n"
" the extended attribute allocation and removal code.\n"
-" Both commands require an attribute name to be specified, and the attr_set\n"
-" command allows an optional value length (-v) to be provided as well.\n"
+" Both commands together with 'attr_modify' require an attribute name to be\n"
+" specified. The attr_set and attr_modify commands allow an optional value\n"
+" length (-v) to be provided as well.\n"
" There are 4 namespace flags:\n"
" -r -- 'root'\n"
" -u -- 'user' (default)\n"
@@ -49,6 +57,9 @@ attrset_help(void)
" 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"
+" attr_modify command provides more of the following options:\n"
+" -m -- 'name length' - specify length of the name (handy with binary names)\n"
+" -o -- 'value offset' - offset new value within old attr's value\n"
" The backward compatibility mode 'noattr2' can be emulated (-n) also.\n"
"\n"));
}
@@ -61,6 +72,7 @@ attrset_init(void)
add_command(&attr_set_cmd);
add_command(&attr_remove_cmd);
+ add_command(&attr_modify_cmd);
}
static unsigned char *
@@ -402,3 +414,197 @@ attr_remove_f(
free((void *)args.name);
return 0;
}
+
+static int
+attr_modify_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,
+ };
+ int c;
+ int offset = 0;
+ char *sp;
+ char *converted;
+ uint8_t *name;
+ int namelen = 0;
+ uint8_t *value;
+ int valuelen = 0;
+ 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, "rusnv:o:m:")) != EOF) {
+ switch (c) {
+ /* namespaces */
+ case 'r':
+ args.attr_filter |= LIBXFS_ATTR_ROOT;
+ args.attr_filter &= ~LIBXFS_ATTR_SECURE;
+ break;
+ case 'u':
+ args.attr_filter &= ~(LIBXFS_ATTR_ROOT |
+ LIBXFS_ATTR_SECURE);
+ break;
+ case 's':
+ args.attr_filter |= LIBXFS_ATTR_SECURE;
+ args.attr_filter &= ~LIBXFS_ATTR_ROOT;
+ break;
+
+ case 'n':
+ /*
+ * We never touch attr2 these days; leave this here to
+ * avoid breaking scripts.
+ */
+ break;
+
+ case 'o':
+ offset = strtol(optarg, &sp, 0);
+ if (*sp != '\0' || offset < 0 || offset > XFS_XATTR_SIZE_MAX) {
+ dbprintf(_("bad attr_modify offset %s\n"),
+ optarg);
+ return 0;
+ }
+ break;
+
+ case 'v':
+ valuelen = strtol(optarg, &sp, 0);
+ if (*sp != '\0' || offset < 0 || valuelen > XFS_XATTR_SIZE_MAX) {
+ dbprintf(_("bad attr_modify value len %s\n"),
+ optarg);
+ return 0;
+ }
+ break;
+
+ case 'm':
+ namelen = strtol(optarg, &sp, 0);
+ if (*sp != '\0' || offset < 0 || namelen > MAXNAMELEN) {
+ dbprintf(_("bad attr_modify name len %s\n"),
+ optarg);
+ return 0;
+ }
+ break;
+
+ default:
+ dbprintf(_("bad option for attr_modify command\n"));
+ return 0;
+ }
+ }
+
+ if (optind != argc - 2) {
+ dbprintf(_("too few options for attr_modify\n"));
+ return 0;
+ }
+
+ if (namelen >= MAXNAMELEN) {
+ dbprintf(_("name too long\n"));
+ return 0;
+ }
+
+ if (!namelen) {
+ if (argv[optind][0] == '#')
+ namelen = strlen(argv[optind])/2;
+ if (argv[optind][0] == '"')
+ namelen = strlen(argv[optind]) - 2;
+ }
+
+ name = xcalloc(namelen, sizeof(uint8_t));
+ converted = convert_arg(argv[optind], (int)(namelen*8));
+ if (!converted) {
+ dbprintf(_("invalid name\n"));
+ goto out_free_name;
+ }
+
+ memcpy(name, converted, namelen);
+ args.name = (const uint8_t *)name;
+ args.namelen = namelen;
+
+ optind++;
+
+ if (valuelen > XFS_XATTR_SIZE_MAX) {
+ dbprintf(_("value too long\n"));
+ goto out_free_name;
+ }
+
+ if (!valuelen) {
+ if (argv[optind][0] == '#')
+ valuelen = strlen(argv[optind])/2;
+ if (argv[optind][0] == '"')
+ valuelen = strlen(argv[optind]) - 2;
+ }
+
+ if ((valuelen + offset) > XFS_XATTR_SIZE_MAX) {
+ dbprintf(_("offsetted value too long\n"));
+ goto out_free_name;
+ }
+
+ value = xcalloc(valuelen, sizeof(uint8_t));
+ converted = convert_arg(argv[optind], (int)(valuelen*8));
+ if (!converted) {
+ dbprintf(_("invalid value\n"));
+ goto out_free_value;
+ }
+ memcpy(value, converted, valuelen);
+
+ 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;
+ }
+
+ 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' from inode %llu: %s\n"),
+ args.name, (unsigned long long)iocur_top->ino,
+ strerror(error));
+ goto out;
+ }
+
+ if (valuelen + offset > args.valuelen) {
+ dbprintf(_("new value too long\n"));
+ goto out;
+ }
+
+ /* modify value */
+ memcpy((uint8_t *)args.value + offset, value, valuelen);
+
+ error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_REPLACE, false);
+ if (error) {
+ dbprintf(_("failed to set attr '%s' from inode %llu: %s\n"),
+ (unsigned char *)args.name,
+ (unsigned long long)iocur_top->ino,
+ strerror(error));
+ goto out;
+ }
+
+ /* refresh with updated inode contents */
+ set_cur_inode(iocur_top->ino);
+
+out:
+ if (args.dp)
+ libxfs_irele(args.dp);
+ xfree(args.value);
+out_free_value:
+ xfree(value);
+out_free_name:
+ xfree(name);
+ return 0;
+}
@@ -511,7 +511,7 @@ convert_oct(
* are adjusted in the buffer so that the first input bit is to be be written to
* the first bit in the output.
*/
-static char *
+char *
convert_arg(
char *arg,
int bit_length)
@@ -6,6 +6,7 @@
struct field;
+extern char *convert_arg(char *arg, int bit_length);
extern void write_init(void);
extern void write_block(const field_t *fields, int argc, char **argv);
extern void write_struct(const field_t *fields, int argc, char **argv);