]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/attrset.c
xfsprogs: Release v6.10.1
[thirdparty/xfsprogs-dev.git] / db / attrset.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
57c9fccb 2/*
da23017d
NS
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
57c9fccb
NS
5 */
6
6b803e5a 7#include "libxfs.h"
57c9fccb
NS
8#include "command.h"
9#include "attrset.h"
10#include "io.h"
11#include "output.h"
12#include "type.h"
13#include "init.h"
501031ab
CH
14#include "fprint.h"
15#include "faddr.h"
16#include "field.h"
57c9fccb
NS
17#include "inode.h"
18#include "malloc.h"
a392fd5a 19#include <sys/xattr.h>
9b4c9c60 20#include "libfrog/fsproperties.h"
dfcdf16c 21#include "libxfs/listxattr.h"
57c9fccb 22
dfcdf16c 23static int attr_list_f(int argc, char **argv);
9b4c9c60 24static int attr_get_f(int argc, char **argv);
57c9fccb
NS
25static int attr_set_f(int argc, char **argv);
26static int attr_remove_f(int argc, char **argv);
dfcdf16c
DW
27
28static void attrlist_help(void);
9b4c9c60 29static void attrget_help(void);
57c9fccb
NS
30static void attrset_help(void);
31
dfcdf16c
DW
32static const cmdinfo_t attr_list_cmd =
33 { "attr_list", "alist", attr_list_f, 0, -1, 0,
34 N_("[-r|-s|-u|-p|-Z] [-v]"),
35 N_("list attributes on the current inode"), attrlist_help };
9b4c9c60
DW
36static const cmdinfo_t attr_get_cmd =
37 { "attr_get", "aget", attr_get_f, 1, -1, 0,
38 N_("[-r|-s|-u|-p|-Z] name"),
39 N_("get the named attribute on the current inode"), attrget_help };
57c9fccb
NS
40static const cmdinfo_t attr_set_cmd =
41 { "attr_set", "aset", attr_set_f, 1, -1, 0,
9b4c9c60 42 N_("[-r|-s|-u|-p|-Z] [-n] [-R|-C] [-v n] name"),
9ee7055c 43 N_("set the named attribute on the current inode"), attrset_help };
57c9fccb
NS
44static const cmdinfo_t attr_remove_cmd =
45 { "attr_remove", "aremove", attr_remove_f, 1, -1, 0,
9b4c9c60 46 N_("[-r|-s|-u|-p|-Z] [-n] name"),
9ee7055c 47 N_("remove the named attribute from the current inode"), attrset_help };
57c9fccb 48
dfcdf16c
DW
49static void
50attrlist_help(void)
51{
52 dbprintf(_(
53"\n"
54" The attr_list command provide interfaces for listing all extended attributes\n"
55" attached to an inode.\n"
56" There are 4 namespace flags:\n"
57" -r -- 'root'\n"
58" -u -- 'user' (default)\n"
59" -s -- 'secure'\n"
60" -p -- 'parent'\n"
61" -Z -- fs property\n"
62"\n"
63" -v -- print the value of the attributes\n"
64"\n"));
65}
66
9b4c9c60
DW
67static void
68attrget_help(void)
69{
70 dbprintf(_(
71"\n"
72" The attr_get command provide interfaces for retrieving the values of extended\n"
73" attributes of a file. This command requires attribute names to be specified.\n"
74" There are 4 namespace flags:\n"
75" -r -- 'root'\n"
76" -u -- 'user' (default)\n"
77" -s -- 'secure'\n"
78" -p -- 'parent'\n"
79" -Z -- fs property\n"
80"\n"));
81}
82
57c9fccb
NS
83static void
84attrset_help(void)
85{
9ee7055c 86 dbprintf(_(
57c9fccb
NS
87"\n"
88" The 'attr_set' and 'attr_remove' commands provide interfaces for debugging\n"
89" the extended attribute allocation and removal code.\n"
90" Both commands require an attribute name to be specified, and the attr_set\n"
91" command allows an optional value length (-v) to be provided as well.\n"
92" There are 4 namespace flags:\n"
93" -r -- 'root'\n"
94" -u -- 'user' (default)\n"
95" -s -- 'secure'\n"
e65a67fb 96" -p -- 'parent'\n"
9b4c9c60 97" -Z -- fs property\n"
57c9fccb 98"\n"
6239071d 99" For attr_set, these options further define the type of set operation:\n"
57c9fccb
NS
100" -C -- 'create' - create attribute, fail if it already exists\n"
101" -R -- 'replace' - replace attribute, fail if it does not exist\n"
9b4c9c60
DW
102"\n"
103" If the attribute value is a string, it can be specified after the\n"
104" attribute name.\n"
105"\n"
6239071d 106" The backward compatibility mode 'noattr2' can be emulated (-n) also.\n"
9ee7055c 107"\n"));
57c9fccb
NS
108}
109
110void
111attrset_init(void)
112{
113 if (!expert_mode)
114 return;
115
dfcdf16c 116 add_command(&attr_list_cmd);
9b4c9c60 117 add_command(&attr_get_cmd);
57c9fccb
NS
118 add_command(&attr_set_cmd);
119 add_command(&attr_remove_cmd);
120}
121
e65a67fb
DW
122static unsigned char *
123get_buf_from_file(
124 const char *fname,
125 size_t bufsize,
126 int *namelen)
127{
128 FILE *fp;
129 unsigned char *buf;
130 size_t sz;
131
132 buf = malloc(bufsize + 1);
133 if (!buf) {
134 perror("malloc");
135 return NULL;
136 }
137
138 fp = fopen(fname, "r");
139 if (!fp) {
140 perror(fname);
141 goto out_free;
142 }
143
144 sz = fread(buf, sizeof(char), bufsize, fp);
145 if (sz == 0) {
146 printf("%s: Could not read anything from file\n", fname);
147 goto out_fp;
148 }
149
150 fclose(fp);
151
152 *namelen = sz;
153 return buf;
154out_fp:
155 fclose(fp);
156out_free:
157 free(buf);
158 return NULL;
159}
160
161#define LIBXFS_ATTR_NS (LIBXFS_ATTR_SECURE | \
162 LIBXFS_ATTR_ROOT | \
163 LIBXFS_ATTR_PARENT)
164
9b4c9c60
DW
165static bool
166adjust_fsprop_attr_name(
167 struct xfs_da_args *args,
168 bool *free_name)
169{
170 const char *o = (const char *)args->name;
171 char *p;
172 int ret;
173
174 if ((args->attr_filter & LIBXFS_ATTR_NS) != LIBXFS_ATTR_ROOT) {
175 dbprintf(_("fs properties must be ATTR_ROOT\n"));
176 return false;
177 }
178
179 ret = fsprop_name_to_attr_name(o, &p);
180 if (ret < 0) {
181 dbprintf(_("could not allocate fs property name string\n"));
182 return false;
183 }
184 args->namelen = ret;
185 args->name = (const uint8_t *)p;
186
187 if (*free_name)
188 free((void *)o);
189 *free_name = true;
190
191 if (args->namelen > MAXNAMELEN) {
192 dbprintf(_("%s: name too long\n"), args->name);
193 return false;
194 }
195
196 if (args->valuelen > ATTR_MAX_VALUELEN) {
197 dbprintf(_("%s: value too long\n"), args->name);
198 return false;
199 }
200
201 return true;
202}
203
204static void
205print_fsprop(
206 struct xfs_da_args *args)
207{
208 const char *p =
209 attr_name_to_fsprop_name((const char *)args->name);
210
211 if (p)
212 printf("%s=%.*s\n", p, args->valuelen, (char *)args->value);
213 else
214 fprintf(stderr, _("%s: not a fs property?\n"), args->name);
215}
216
57c9fccb
NS
217static int
218attr_set_f(
b8e89b44
CH
219 int argc,
220 char **argv)
57c9fccb 221{
28488a35
DW
222 struct xfs_da_args args = {
223 .geo = mp->m_attr_geo,
224 .whichfork = XFS_ATTR_FORK,
225 .op_flags = XFS_DA_OP_OKNOENT,
226 };
8e9a788f 227 char *sp;
e65a67fb
DW
228 char *name_from_file = NULL;
229 char *value_from_file = NULL;
9b4c9c60 230 bool free_name = false;
8f07219c 231 enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERT;
9b4c9c60 232 bool fsprop = false;
8e9a788f 233 int c;
361af16f 234 int error;
57c9fccb
NS
235
236 if (cur_typ == NULL) {
9ee7055c 237 dbprintf(_("no current type\n"));
57c9fccb
NS
238 return 0;
239 }
240 if (cur_typ->typnm != TYP_INODE) {
9ee7055c 241 dbprintf(_("current type is not inode\n"));
57c9fccb
NS
242 return 0;
243 }
244
9b4c9c60 245 while ((c = getopt(argc, argv, "ruspCRnN:v:V:Z")) != EOF) {
57c9fccb
NS
246 switch (c) {
247 /* namespaces */
9b4c9c60
DW
248 case 'Z':
249 fsprop = true;
250 fallthrough;
57c9fccb 251 case 'r':
e65a67fb 252 args.attr_filter &= ~LIBXFS_ATTR_NS;
a392fd5a 253 args.attr_filter |= LIBXFS_ATTR_ROOT;
57c9fccb
NS
254 break;
255 case 'u':
e65a67fb 256 args.attr_filter &= ~LIBXFS_ATTR_NS;
57c9fccb
NS
257 break;
258 case 's':
e65a67fb 259 args.attr_filter &= ~LIBXFS_ATTR_NS;
a392fd5a 260 args.attr_filter |= LIBXFS_ATTR_SECURE;
e65a67fb
DW
261 break;
262 case 'p':
263 args.attr_filter &= ~LIBXFS_ATTR_NS;
264 args.attr_filter |= XFS_ATTR_PARENT;
57c9fccb
NS
265 break;
266
267 /* modifiers */
268 case 'C':
d55ab835 269 op = XFS_ATTRUPDATE_CREATE;
57c9fccb
NS
270 break;
271 case 'R':
d55ab835 272 op = XFS_ATTRUPDATE_REPLACE;
57c9fccb
NS
273 break;
274
e65a67fb
DW
275 case 'N':
276 name_from_file = optarg;
277 break;
278
6239071d 279 case 'n':
ed8f5980
DW
280 /*
281 * We never touch attr2 these days; leave this here to
282 * avoid breaking scripts.
283 */
6239071d
NS
284 break;
285
57c9fccb
NS
286 /* value length */
287 case 'v':
e65a67fb
DW
288 if (value_from_file) {
289 dbprintf(_("already set value file\n"));
290 return 0;
291 }
292
8e9a788f
CH
293 args.valuelen = strtol(optarg, &sp, 0);
294 if (*sp != '\0' ||
295 args.valuelen < 0 || args.valuelen > 64 * 1024) {
9ee7055c 296 dbprintf(_("bad attr_set valuelen %s\n"), optarg);
57c9fccb
NS
297 return 0;
298 }
299 break;
300
e65a67fb
DW
301 case 'V':
302 if (args.valuelen != 0) {
303 dbprintf(_("already set valuelen\n"));
304 return 0;
305 }
306
307 value_from_file = optarg;
308 break;
309
57c9fccb 310 default:
9ee7055c 311 dbprintf(_("bad option for attr_set command\n"));
57c9fccb
NS
312 return 0;
313 }
314 }
315
e65a67fb
DW
316 if (name_from_file) {
317 int namelen;
57c9fccb 318
e65a67fb
DW
319 if (optind != argc) {
320 dbprintf(_("too many options for attr_set (no name needed)\n"));
321 return 0;
322 }
38cff22d 323
e65a67fb
DW
324 args.name = get_buf_from_file(name_from_file, MAXNAMELEN,
325 &namelen);
326 if (!args.name)
327 return 0;
328
9b4c9c60 329 free_name = true;
e65a67fb
DW
330 args.namelen = namelen;
331 } else {
9b4c9c60 332 if (optind != argc - 1 && optind != argc - 2) {
e65a67fb
DW
333 dbprintf(_("too few options for attr_set (no name given)\n"));
334 return 0;
335 }
336
337 args.name = (const unsigned char *)argv[optind];
338 if (!args.name) {
339 dbprintf(_("invalid name\n"));
340 return 0;
341 }
342
343 args.namelen = strlen(argv[optind]);
344 if (args.namelen >= MAXNAMELEN) {
345 dbprintf(_("name too long\n"));
346 goto out;
347 }
38cff22d 348 }
57c9fccb 349
e65a67fb
DW
350 if (value_from_file) {
351 int valuelen;
352
353 args.value = get_buf_from_file(value_from_file,
354 XFS_XATTR_SIZE_MAX, &valuelen);
355 if (!args.value)
356 goto out;
357
358 args.valuelen = valuelen;
359 } else if (args.valuelen) {
8e9a788f
CH
360 args.value = memalign(getpagesize(), args.valuelen);
361 if (!args.value) {
362 dbprintf(_("cannot allocate buffer (%d)\n"),
363 args.valuelen);
57c9fccb
NS
364 goto out;
365 }
8e9a788f 366 memset(args.value, 'v', args.valuelen);
9b4c9c60
DW
367 } else if (optind == argc - 2) {
368 args.valuelen = strlen(argv[optind + 1]);
369 args.value = strdup(argv[optind + 1]);
370 if (!args.value) {
371 dbprintf(_("cannot allocate buffer (%d)\n"),
372 args.valuelen);
373 goto out;
374 }
375 }
376
377 if (fsprop) {
378 if (!fsprop_validate((const char *)args.name, args.value)) {
379 dbprintf(_("%s: invalid value \"%s\"\n"),
380 args.name, args.value);
381 goto out;
382 }
383
384 if (!adjust_fsprop_attr_name(&args, &free_name))
385 goto out;
57c9fccb
NS
386 }
387
1fecabf9 388 if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
57c9fccb
NS
389 dbprintf(_("failed to iget inode %llu\n"),
390 (unsigned long long)iocur_top->ino);
391 goto out;
392 }
393
28488a35
DW
394 args.owner = iocur_top->ino;
395 libxfs_attr_sethash(&args);
396
361af16f
DW
397 error = -libxfs_attr_set(&args, op, false);
398 if (error) {
399 dbprintf(_("failed to set attr %s on inode %llu: %s\n"),
400 args.name, (unsigned long long)iocur_top->ino,
401 strerror(error));
57c9fccb
NS
402 goto out;
403 }
404
9b4c9c60
DW
405 if (fsprop)
406 print_fsprop(&args);
407
57c9fccb
NS
408 /* refresh with updated inode contents */
409 set_cur_inode(iocur_top->ino);
410
411out:
8e9a788f
CH
412 if (args.dp)
413 libxfs_irele(args.dp);
414 if (args.value)
415 free(args.value);
9b4c9c60 416 if (free_name)
e65a67fb 417 free((void *)args.name);
57c9fccb
NS
418 return 0;
419}
420
421static int
422attr_remove_f(
b8e89b44
CH
423 int argc,
424 char **argv)
57c9fccb 425{
28488a35
DW
426 struct xfs_da_args args = {
427 .geo = mp->m_attr_geo,
428 .whichfork = XFS_ATTR_FORK,
429 .op_flags = XFS_DA_OP_OKNOENT,
430 };
e65a67fb 431 char *name_from_file = NULL;
9b4c9c60
DW
432 bool free_name = false;
433 bool fsprop = false;
a392fd5a 434 int c;
361af16f 435 int error;
57c9fccb
NS
436
437 if (cur_typ == NULL) {
9ee7055c 438 dbprintf(_("no current type\n"));
57c9fccb
NS
439 return 0;
440 }
441 if (cur_typ->typnm != TYP_INODE) {
9ee7055c 442 dbprintf(_("current type is not inode\n"));
57c9fccb
NS
443 return 0;
444 }
445
9b4c9c60 446 while ((c = getopt(argc, argv, "ruspnN:Z")) != EOF) {
57c9fccb
NS
447 switch (c) {
448 /* namespaces */
9b4c9c60
DW
449 case 'Z':
450 fsprop = true;
451 fallthrough;
57c9fccb 452 case 'r':
e65a67fb 453 args.attr_filter &= ~LIBXFS_ATTR_NS;
a392fd5a 454 args.attr_filter |= LIBXFS_ATTR_ROOT;
57c9fccb
NS
455 break;
456 case 'u':
e65a67fb 457 args.attr_filter &= ~LIBXFS_ATTR_NS;
57c9fccb
NS
458 break;
459 case 's':
e65a67fb 460 args.attr_filter &= ~LIBXFS_ATTR_NS;
a392fd5a 461 args.attr_filter |= LIBXFS_ATTR_SECURE;
e65a67fb
DW
462 break;
463 case 'p':
464 args.attr_filter &= ~LIBXFS_ATTR_NS;
465 args.attr_filter |= XFS_ATTR_PARENT;
466 break;
467
468 case 'N':
469 name_from_file = optarg;
57c9fccb
NS
470 break;
471
6239071d 472 case 'n':
ed8f5980
DW
473 /*
474 * We never touch attr2 these days; leave this here to
475 * avoid breaking scripts.
476 */
6239071d
NS
477 break;
478
57c9fccb 479 default:
9ee7055c 480 dbprintf(_("bad option for attr_remove command\n"));
57c9fccb
NS
481 return 0;
482 }
483 }
484
e65a67fb
DW
485 if (name_from_file) {
486 int namelen;
57c9fccb 487
e65a67fb
DW
488 if (optind != argc) {
489 dbprintf(_("too many options for attr_set (no name needed)\n"));
490 return 0;
491 }
38cff22d 492
e65a67fb
DW
493 args.name = get_buf_from_file(name_from_file, MAXNAMELEN,
494 &namelen);
495 if (!args.name)
496 return 0;
497
9b4c9c60 498 free_name = true;
e65a67fb
DW
499 args.namelen = namelen;
500 } else {
501 if (optind != argc - 1) {
502 dbprintf(_("too few options for attr_remove (no name given)\n"));
503 return 0;
504 }
505
506 args.name = (const unsigned char *)argv[optind];
507 if (!args.name) {
508 dbprintf(_("invalid name\n"));
509 return 0;
510 }
511
512 args.namelen = strlen(argv[optind]);
513 if (args.namelen >= MAXNAMELEN) {
514 dbprintf(_("name too long\n"));
515 return 0;
516 }
38cff22d 517 }
57c9fccb 518
9b4c9c60
DW
519 if (fsprop && !adjust_fsprop_attr_name(&args, &free_name))
520 goto out;
521
1fecabf9 522 if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
57c9fccb
NS
523 dbprintf(_("failed to iget inode %llu\n"),
524 (unsigned long long)iocur_top->ino);
525 goto out;
526 }
527
28488a35
DW
528 args.owner = iocur_top->ino;
529 libxfs_attr_sethash(&args);
530
361af16f
DW
531 error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false);
532 if (error) {
533 dbprintf(_("failed to remove attr %s from inode %llu: %s\n"),
8e9a788f 534 (unsigned char *)args.name,
361af16f
DW
535 (unsigned long long)iocur_top->ino,
536 strerror(error));
57c9fccb
NS
537 goto out;
538 }
539
540 /* refresh with updated inode contents */
541 set_cur_inode(iocur_top->ino);
542
543out:
8e9a788f
CH
544 if (args.dp)
545 libxfs_irele(args.dp);
9b4c9c60
DW
546 if (free_name)
547 free((void *)args.name);
548 return 0;
549}
550
551static int
552attr_get_f(
553 int argc,
554 char **argv)
555{
556 struct xfs_da_args args = {
557 .geo = mp->m_attr_geo,
558 .whichfork = XFS_ATTR_FORK,
559 .op_flags = XFS_DA_OP_OKNOENT,
560 };
561 char *name_from_file = NULL;
562 bool free_name = false;
563 bool fsprop = false;
564 int c;
565 int error;
566
567 if (cur_typ == NULL) {
568 dbprintf(_("no current type\n"));
569 return 0;
570 }
571 if (cur_typ->typnm != TYP_INODE) {
572 dbprintf(_("current type is not inode\n"));
573 return 0;
574 }
575
576 while ((c = getopt(argc, argv, "ruspN:Z")) != EOF) {
577 switch (c) {
578 /* namespaces */
579 case 'Z':
580 fsprop = true;
581 fallthrough;
582 case 'r':
583 args.attr_filter &= ~LIBXFS_ATTR_NS;
584 args.attr_filter |= LIBXFS_ATTR_ROOT;
585 break;
586 case 'u':
587 args.attr_filter &= ~LIBXFS_ATTR_NS;
588 break;
589 case 's':
590 args.attr_filter &= ~LIBXFS_ATTR_NS;
591 args.attr_filter |= LIBXFS_ATTR_SECURE;
592 break;
593 case 'p':
594 args.attr_filter &= ~LIBXFS_ATTR_NS;
595 args.attr_filter |= XFS_ATTR_PARENT;
596 break;
597
598 case 'N':
599 name_from_file = optarg;
600 break;
601 default:
602 dbprintf(_("bad option for attr_get command\n"));
603 return 0;
604 }
605 }
606
607 if (name_from_file) {
608 int namelen;
609
610 if (optind != argc) {
611 dbprintf(_("too many options for attr_get (no name needed)\n"));
612 return 0;
613 }
614
615 args.name = get_buf_from_file(name_from_file, MAXNAMELEN,
616 &namelen);
617 if (!args.name)
618 return 0;
619
620 free_name = true;
621 args.namelen = namelen;
622 } else {
623 if (optind != argc - 1) {
624 dbprintf(_("too few options for attr_get (no name given)\n"));
625 return 0;
626 }
627
628 args.name = (const unsigned char *)argv[optind];
629 if (!args.name) {
630 dbprintf(_("invalid name\n"));
631 return 0;
632 }
633
634 args.namelen = strlen(argv[optind]);
635 if (args.namelen >= MAXNAMELEN) {
636 dbprintf(_("name too long\n"));
637 goto out;
638 }
639 }
640
641 if (libxfs_iget(mp, NULL, iocur_top->ino, 0, &args.dp)) {
642 dbprintf(_("failed to iget inode %llu\n"),
643 (unsigned long long)iocur_top->ino);
644 goto out;
645 }
646
647 if (fsprop && !adjust_fsprop_attr_name(&args, &free_name))
648 goto out;
649
650 args.owner = iocur_top->ino;
651 libxfs_attr_sethash(&args);
652
653 /*
654 * Look up attr value with a maximally long length and a null buffer
655 * to return the value and the correct length.
656 */
657 args.valuelen = XATTR_SIZE_MAX;
658 error = -libxfs_attr_get(&args);
659 if (error) {
660 dbprintf(_("failed to get attr %s on inode %llu: %s\n"),
661 args.name, (unsigned long long)iocur_top->ino,
662 strerror(error));
663 goto out;
664 }
665
666 if (fsprop)
667 print_fsprop(&args);
668 else
669 printf("%.*s\n", args.valuelen, (char *)args.value);
670
671out:
672 if (args.dp)
673 libxfs_irele(args.dp);
674 if (args.value)
675 free(args.value);
676 if (free_name)
e65a67fb 677 free((void *)args.name);
57c9fccb
NS
678 return 0;
679}
dfcdf16c
DW
680
681struct attrlist_ctx {
682 unsigned int attr_filter;
683 bool print_values;
684 bool fsprop;
685};
686
687static int
688attrlist_print(
689 struct xfs_trans *tp,
690 struct xfs_inode *ip,
691 unsigned int attr_flags,
692 const unsigned char *name,
693 unsigned int namelen,
694 const void *value,
695 unsigned int valuelen,
696 void *priv)
697{
698 struct attrlist_ctx *ctx = priv;
699 struct xfs_da_args args = {
700 .geo = mp->m_attr_geo,
701 .whichfork = XFS_ATTR_FORK,
702 .op_flags = XFS_DA_OP_OKNOENT,
703 .dp = ip,
704 .owner = ip->i_ino,
705 .trans = tp,
706 .attr_filter = attr_flags & XFS_ATTR_NSP_ONDISK_MASK,
707 .name = name,
708 .namelen = namelen,
709 };
710 char namebuf[MAXNAMELEN + 1];
711 const char *print_name = namebuf;
712 int error;
713
714 if ((attr_flags & XFS_ATTR_NSP_ONDISK_MASK) != ctx->attr_filter)
715 return 0;
716
717 /* Make sure the name is null terminated. */
718 memcpy(namebuf, name, namelen);
719 namebuf[MAXNAMELEN] = 0;
720
721 if (ctx->fsprop) {
722 const char *p = attr_name_to_fsprop_name(namebuf);
723
724 if (!p)
725 return 0;
726
727 namelen -= (p - namebuf);
728 print_name = p;
729 }
730
731 if (!ctx->print_values) {
732 printf("%.*s\n", namelen, print_name);
733 return 0;
734 }
735
736 if (value) {
737 printf("%.*s=%.*s\n", namelen, print_name, valuelen,
738 (char *)value);
739 return 0;
740 }
741
742 libxfs_attr_sethash(&args);
743
744 /*
745 * Look up attr value with a maximally long length and a null buffer
746 * to return the value and the correct length.
747 */
748 args.valuelen = XATTR_SIZE_MAX;
749 error = -libxfs_attr_get(&args);
750 if (error) {
751 dbprintf(_("failed to get attr %s on inode %llu: %s\n"),
752 args.name, (unsigned long long)iocur_top->ino,
753 strerror(error));
754 return error;
755 }
756
757 printf("%.*s=%.*s\n", namelen, print_name, args.valuelen,
758 (char *)args.value);
759 kfree(args.value);
760
761 return 0;
762}
763
764static int
765attr_list_f(
766 int argc,
767 char **argv)
768{
769 struct attrlist_ctx ctx = { };
770 struct xfs_trans *tp;
771 struct xfs_inode *ip;
772 int c;
773 int error;
774
775 if (cur_typ == NULL) {
776 dbprintf(_("no current type\n"));
777 return 0;
778 }
779 if (cur_typ->typnm != TYP_INODE) {
780 dbprintf(_("current type is not inode\n"));
781 return 0;
782 }
783
784 while ((c = getopt(argc, argv, "ruspvZ")) != EOF) {
785 switch (c) {
786 /* namespaces */
787 case 'Z':
788 ctx.fsprop = true;
789 fallthrough;
790 case 'r':
791 ctx.attr_filter &= ~LIBXFS_ATTR_NS;
792 ctx.attr_filter |= LIBXFS_ATTR_ROOT;
793 break;
794 case 'u':
795 ctx.attr_filter &= ~LIBXFS_ATTR_NS;
796 break;
797 case 's':
798 ctx.attr_filter &= ~LIBXFS_ATTR_NS;
799 ctx.attr_filter |= LIBXFS_ATTR_SECURE;
800 break;
801 case 'p':
802 ctx.attr_filter &= ~LIBXFS_ATTR_NS;
803 ctx.attr_filter |= XFS_ATTR_PARENT;
804 break;
805
806 case 'v':
807 ctx.print_values = true;
808 break;
809 default:
810 dbprintf(_("bad option for attr_list command\n"));
811 return 0;
812 }
813 }
814
815 if (ctx.fsprop &&
816 (ctx.attr_filter & LIBXFS_ATTR_NS) != LIBXFS_ATTR_ROOT) {
817 dbprintf(_("fs properties must be ATTR_ROOT\n"));
818 return false;
819 }
820
821 if (optind != argc) {
822 dbprintf(_("too many options for attr_list (no name needed)\n"));
823 return 0;
824 }
825
826 error = -libxfs_trans_alloc_empty(mp, &tp);
827 if (error) {
828 dbprintf(_("failed to allocate empty transaction\n"));
829 return 0;
830 }
831
832 error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip);
833 if (error) {
834 dbprintf(_("failed to iget inode %llu: %s\n"),
835 (unsigned long long)iocur_top->ino,
836 strerror(error));
837 goto out_trans;
838 }
839
840 error = xattr_walk(tp, ip, attrlist_print, &ctx);
841 if (error) {
842 dbprintf(_("walking inode %llu xattrs: %s\n"),
843 (unsigned long long)iocur_top->ino,
844 strerror(error));
845 goto out_inode;
846 }
847
848out_inode:
849 libxfs_irele(ip);
850out_trans:
851 libxfs_trans_cancel(tp);
852 return 0;
853}