diff options
Diffstat (limited to 'getfattr/getfattr.c')
-rw-r--r-- | getfattr/getfattr.c | 633 |
1 files changed, 349 insertions, 284 deletions
diff --git a/getfattr/getfattr.c b/getfattr/getfattr.c index 390a8c9..74c2d65 100644 --- a/getfattr/getfattr.c +++ b/getfattr/getfattr.c @@ -1,185 +1,87 @@ /* - * Original getfattr.c: - * Copyright (C) 2001 by Andreas Gruenbacher <a.gruenbacher@computer.org> - * Changes to use revised EA syscall interface: - * Copyright (C) 2001 by SGI XFS development <linux-xfs@oss.sgi.com> - */ + File: getfattr.c + (Linux Extended Attributes) + + Copyright (C) 2001-2002 Andreas Gruenbacher <a.gruenbacher@computer.org> + Copyright (C) 2001-2002 SGI XFS development <linux-xfs@oss.sgi.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <errno.h> #include <ctype.h> #include <getopt.h> #include <regex.h> #include <attr/xattr.h> -#include "walk_tree.h" +#include <ftw.h> #include <locale.h> #include <libintl.h> #define _(String) gettext (String) -#define CMD_LINE_OPTIONS "ade:hln:r:vR5LPV" -#define CMD_LINE_SPEC "[-n name|-d] [-ahvR5LPV] [-e en] [-r regex] path..." - -int opt_dump; -int opt_symlink; -char *opt_encoding; -char opt_value_only; -int opt_strip_leading_slash = 1; +#define CMD_LINE_OPTIONS "ade:hm:n:v:HRLP" +#define CMD_LINE_SPEC "[-ahvRLP] [-n name|-d] [-e en] [-m pattern] path..." + +struct option long_options[] = { + { "name", 1, 0, 'n' }, + { "dump", 0, 0, 'd' }, + { "encoding", 1, 0, 'e' }, + { "match", 1, 0, 'm' }, + { "only-values", 0, 0, 'v' }, + { "no-dereference", 0, 0, 'h' }, + { "absolute-names", 0, 0, 'a' }, + { "recursive", 0, 0, 'R' }, + { "logical", 0, 0, 'L' }, + { "physical", 0, 0, 'P' }, + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'H' }, + { NULL, 0, 0, 0 } +}; + +int opt_recursive; /* recurse into sub-directories? */ +int opt_walk_logical; /* always follow symbolic links */ +int opt_walk_physical; /* never follow symbolic links */ +int opt_dump; /* dump attribute values (or only list the names) */ +int opt_deref = 1; /* dereference symbolic links */ +char *opt_name; /* dump named attributes */ +char *opt_name_pattern = "^user\\."; /* include only matching names */ +char *opt_encoding; /* encode values automatically (NULL), or as "text", + "hex", or "base64" */ +char opt_value_only; /* dump the value only, without any decoration */ +int opt_strip_leading_slash = 1; /* strip leading '/' from path names */ const char *progname; int absolute_warning; -int header_printed; int had_errors; -regex_t re; - -int do_get_all(const char *, struct stat *, void *); -int do_get_one(const char *, struct stat *, void *); -int get_one(const char *, const char *); -const char *encode(const char *value, size_t *size); - - -void help(void) -{ - printf(_("%s %s -- get extended attributes\n"), - progname, VERSION); - printf(_("Usage: %s %s\n"), - progname, CMD_LINE_SPEC); - printf(_("\t-n name\tdump value of extended attribute `name'\n" - "\t-a\tabsolute pathnames - leading '/' not stripped\n" - "\t-d\tdump all extended attribute values\n" - "\t-e en\tencode values (en = text|hex|base64)\n" - "\t-l\tdump extended attribute values of a symlink\n" - "\t-s\tdump extended system and user attribute values\n" - "\t-v\tprint the attribute value(s) only\n" - "\t-R\trecurse into subdirectories (-5 = post-order)\n" - "\t-L\tlogical walk, follow symbolic links\n" - "\t-P\tphysical walk, do not follow symbolic links\n" - "\t-V\tprint version and exit\n" - "\t-h\tthis help text\n")); -} - -int main(int argc, char *argv[]) -{ - char *pattern = "^user\\."; - char *name = NULL; - - progname = basename(argv[0]); - setlocale(LC_ALL, ""); - - while ((optopt = getopt(argc, argv, CMD_LINE_OPTIONS)) != -1) { - switch(optopt) { - case 'a': /* absolute names */ - opt_strip_leading_slash = 0; - break; - - case 'd': /* dump attribute values */ - opt_dump = 1; - break; - - case 'e': /* encoding */ - if (strcmp(optarg, "text") != 0 && - strcmp(optarg, "hex") != 0 && - strcmp(optarg, "base64") != 0) - goto synopsis; - opt_encoding = optarg; - break; - - case 'h': - help(); - return 0; - - case 'l': /* dump attribute(s) of symlink itself */ - opt_symlink = 1; - break; - - case 'n': /* get named attribute */ - opt_dump = 1; - name = optarg; - break; - - case 'r': /* regular expression for filtering names */ - if (!strcmp(optarg, "-")) - pattern = ""; - else - pattern = optarg; - break; - - case 'v': /* get attribute values only */ - opt_value_only = 1; - break; - - case 'H': - walk_symlinks = WALK_HALF_LOGICAL; - break; - - case 'L': - walk_symlinks = WALK_FULL_LOGICAL; - break; - - case 'P': - walk_symlinks = WALK_PHYSICAL; - break; - - case 'R': - walk_recurse = 1; - walk_postorder = 0; - break; - - case '5': - walk_recurse = 1; - walk_postorder = 1; - break; - - case 'V': - printf("%s " VERSION "\n", progname); - return 0; - - default: - goto synopsis; - } - } - if (optind >= argc) - goto synopsis; - - if (regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB) != 0) { - fprintf(stderr, _("%s: invalid regular expression \"%s\"\n"), - progname, pattern); - return 1; - } +regex_t name_regex; - while (optind < argc) { - if (name) - had_errors += walk_tree(argv[optind], do_get_one, name); - else - had_errors += walk_tree(argv[optind], do_get_all, NULL); - optind++; - } - - return (had_errors ? 1 : 0); - -synopsis: - fprintf(stderr, _("Usage: %s %s\n" - "Try `%s -h' for more information.\n"), - progname, CMD_LINE_SPEC, progname); - return 2; -} int do_getxattr(const char *path, const char *name, void *value, size_t size) { - if (opt_symlink) - return lgetxattr(path, name, value, size); - return getxattr(path, name, value, size); + return (opt_deref ? getxattr : lgetxattr)(path, name, value, size); } int do_listxattr(const char *path, char *list, size_t size) { - if (opt_symlink) - return llistxattr(path, list, size); - return listxattr(path, list, size); + return (opt_deref ? listxattr : llistxattr)(path, list, size); } -int high_water_alloc(void **buf, int *bufsize, int newsize) +int high_water_alloc(void **buf, size_t *bufsize, size_t newsize) { #define CHUNK_SIZE 256 /* @@ -188,149 +90,32 @@ int high_water_alloc(void **buf, int *bufsize, int newsize) * Size is increased in fixed size chunks (CHUNK_SIZE). */ if (*bufsize < newsize) { + void *newbuf; + newsize = (newsize + CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); - *buf = realloc(*buf, newsize); - if (!*buf) { + newbuf = realloc(*buf, newsize); + if (!newbuf) { perror(progname); had_errors++; - *bufsize = 0; return 1; } + + *buf = newbuf; *bufsize = newsize; } return 0; } -int pstrcmp(const char **a, const char **b) -{ - return strcmp(*a, *b); -} - -int do_get_all(const char *path, struct stat *stat, void *dummy) -{ - static char *list; - static ssize_t bufsize; - static char **names; - static int ncount; - - char *v; - ssize_t listsize; - int n, count = 0; - - listsize = do_listxattr(path, NULL, 0); - if (listsize < 0) { - perror(path); - had_errors++; - return 1; - } else if (listsize > 0) { - if (high_water_alloc((void **)&list, &bufsize, listsize)) - return 1; - - listsize = do_listxattr(path, list, bufsize); - if (listsize < 0) { - perror(path); - had_errors++; - return 1; - } - - for (v = list; (v - list) < listsize; v += strlen(v)+1) - if (regexec(&re, v, (size_t) 0, NULL, 0) == 0) - count++; - if (count) { - n = count * sizeof(char *); - if (high_water_alloc((void **)&names, &ncount, n)) - return 1; - n = 0; - for (v = list; (v - list) < listsize; v += strlen(v)+1) - if (regexec(&re, v, (size_t) 0, NULL, 0) == 0) - names[n++] = v; - qsort(names, count, sizeof(char *), - (int (*)(const void *,const void *))pstrcmp); - } - } - if (names) { - header_printed = 0; - for (n = 0; n < count; n++) - get_one(path, names[n]); - if (header_printed) - puts(""); - } - return 0; -} - -int do_get_one(const char *path, struct stat *stat, void *name) +const char *strerror_ea(int err) { - int error; - - header_printed = 0; - error = get_one(path, (const char *)name); - if (header_printed) - puts(""); - return error; + if (err == ENOATTR) + return _("No such attribute"); + return strerror(err); } -int get_one(const char *path, const char *name) +int pstrcmp(const void *a, const void *b) { - static char *value; - static size_t vsize; - - int error, lsize = 0; - - if (opt_dump || opt_value_only) { - error = do_getxattr(path, name, NULL, 0); - if (error < 0) { - const char *strerr; -syscall_failed: - if (!strncmp("system.", name, 7) && errno == ENOATTR) - return 0; /* expected */ - else if (errno == ENOATTR) - strerr = _("No such attribute"); - else - strerr = strerror(errno); - fprintf(stderr, "%s: %s: %s\n", path, name, strerr); - return 1; - } - if (high_water_alloc((void **)&value, &vsize, error + 1)) - return 1; - error = do_getxattr(path, name, value, vsize); - if (error < 0) - goto syscall_failed; - lsize = error; - value[lsize] = '\0'; - } - - if (opt_strip_leading_slash) { - if (*path == '/') { - if (!absolute_warning) { - fprintf(stderr, _("%s: Removing leading '/' " - "from absolute path names\n"), - progname); - absolute_warning = 1; - } - while (*path == '/') - path++; - } else if (*path == '.' && *(path+1) == '/') - while (*++path == '/') - /* nothing */ ; - if (*path == '\0') - path = "."; - } - - if (!header_printed && !opt_value_only) { - printf("# file: %s\n", path); - header_printed = 1; - } - if (opt_value_only) - puts(value); - else if (lsize) { - const char *e = encode(value, &lsize); - - if (e) - printf("%s=%s\n", name, e); - } else - puts(name); - - return 0; + return strcmp(*(const char **)a, *(const char **)b); } int well_enough_printable(const char *value, size_t size) @@ -341,7 +126,7 @@ int well_enough_printable(const char *value, size_t size) if (!isprint(*value++)) nonpr++; - return (size >= nonpr*8); /* no more than 1/8 non-printable */ + return (size >= nonpr*8); /* no more than 1/8 non-printable chars */ } const char *encode(const char *value, size_t *size) @@ -449,3 +234,283 @@ const char *encode(const char *value, size_t *size) } return encoded; } + +int print_attribute(const char *path, const char *name, int *header_printed) +{ + static char *value; + static size_t value_size; + ssize_t length = 0; + + if (opt_dump || opt_value_only) { + length = do_getxattr(path, name, NULL, 0); + if (length < 0) { + if (errno == ENOATTR) { + /* + * Occasionally there are attribute + * names in the name list that are not + * accessible, or don't really exist. + * Silently ignore this case. + */ + return 0; + } + fprintf(stderr, "%s: %s: %s\n", path, name, + strerror_ea(errno)); + return 1; + } + if (high_water_alloc((void **)&value, &value_size, length)) + return 1; + length = do_getxattr(path, name, value, value_size); + if (length < 0) { + fprintf(stderr, "%s: %s: %s\n", path, name, + strerror_ea(errno)); + return 1; + } + } + + if (opt_strip_leading_slash) { + if (*path == '/') { + if (!absolute_warning) { + fprintf(stderr, _("%s: Removing leading '/' " + "from absolute path names\n"), + progname); + absolute_warning = 1; + } + while (*path == '/') + path++; + } else if (*path == '.' && *(path+1) == '/') + while (*++path == '/') + /* nothing */ ; + if (*path == '\0') + path = "."; + } + + if (!*header_printed && !opt_value_only) { + printf("# file: %s\n", path); + *header_printed = 1; + } + + if (opt_value_only) + fwrite(value, length, 1, stdout); + else if (length) { + const char *enc = encode(value, &length); + + if (enc) + printf("%s=%s\n", name, enc); + } else + puts(name); + + return 0; +} + +int list_attributes(const char *path, int *header_printed) +{ + static char *list; + static size_t list_size; + static char **names; + static size_t names_size; + int num_names = 0; + ssize_t length; + char *l; + + length = do_listxattr(path, NULL, 0); + if (length < 0) { + if (errno != ENOTSUP || errno != ENOSYS) { + fprintf(stderr, "%s: %s: %s\n", + progname, path, strerror_ea(errno)); + had_errors++; + return 1; + } + } else if (length == 0) + return 0; + + if (high_water_alloc((void **)&list, &list_size, length)) + return 1; + + length = do_listxattr(path, list, list_size); + if (length < 0) { + perror(path); + had_errors++; + return 1; + } + + for (l = list; l != list + length; l = strchr(l, '\0')+1) { + if (regexec(&name_regex, l, 0, NULL, 0) != 0) + continue; + + if (names_size < (num_names+1) * sizeof(*names)) { + if (high_water_alloc((void **)&names, &names_size, + (num_names+1) * sizeof(*names))) + return 1; + } + + names[num_names++] = l; + } + + qsort(names, num_names, sizeof(*names), pstrcmp); + + if (num_names) { + int n; + + for (n = 0; n < num_names; n++) + print_attribute(path, names[n], header_printed); + } + return 0; +} + +int do_print(const char *path, const struct stat *stat, + int flag, struct FTW *ftw) +{ + int header_printed = 0; + + /* + * Process the target of a symbolic link, and traverse the + * link, only if doing a logical walk, or if the symbolic link + * was specified on the command line. Always skip symbolic + * links if doing a physical walk. + */ + + if (S_ISLNK(stat->st_mode) && + (opt_walk_physical || (ftw->level > 0 && !opt_walk_logical))) + return 0; + + if (opt_name) + print_attribute(path, opt_name, &header_printed); + else + list_attributes(path, &header_printed); + + if (header_printed) + puts(""); + + /* + * We also get here in non-recursive mode. In that case, + * return something != 0 to abort nftw. + */ + + if (!opt_recursive) + return 1; + return 0; +} + +void help(void) +{ + printf(_("%s %s -- get extended attributes\n"), + progname, VERSION); + printf(_("Usage: %s %s\n"), + progname, _(CMD_LINE_SPEC)); + printf(_( +" -n, --name=name get the named extended attribute value\n" +" -d, --dump get all extended attribute values\n" +" -e, --encoding=... encode values (as 'text', 'hex' or 'base64')\n" +" --match=pattern only get attributes with names matching pattern\n" +" --only-values print the bare values only\n" +" -h, --no-dereference do not dereference symbolic links\n" +" --absolute-names don't strip leading '/' in pathnames\n" +" -R, --recursive recurse into subdirectories\n" +" -L, --logical logical walk, follow symbolic links\n" +" -P --physical physical walk, do not follow symbolic links\n" +" --version print version and exit\n" +" --help this help text\n")); +} + + +int main(int argc, char *argv[]) +{ + int opt; + + progname = basename(argv[0]); + setlocale(LC_ALL, ""); + + while ((opt = getopt_long(argc, argv, CMD_LINE_OPTIONS, + long_options, NULL)) != -1) { + switch(opt) { + case 'a': /* absolute names */ + opt_strip_leading_slash = 0; + break; + + case 'd': /* dump attribute values */ + opt_dump = 1; + break; + + case 'e': /* encoding */ + if (strcmp(optarg, "text") != 0 && + strcmp(optarg, "hex") != 0 && + strcmp(optarg, "base64") != 0) + goto synopsis; + opt_encoding = optarg; + break; + + case 'H': + help(); + return 0; + + case 'h': /* do not dereference symlinks */ + opt_deref = 0; + break; + + case 'n': /* get named attribute */ + opt_dump = 1; + opt_name = optarg; + break; + + case 'm': /* regular expression for filtering names */ + opt_name_pattern = optarg; + if (strcmp(opt_name_pattern, "-") == 0) + opt_name_pattern = ""; + break; + + case 'v': /* get attribute values only */ + opt_value_only = 1; + break; + + case 'L': + opt_walk_logical = 1; + opt_walk_physical = 0; + break; + + case 'P': + opt_walk_logical = 0; + opt_walk_physical = 1; + break; + + case 'R': + opt_recursive = 1; + break; + + case 'V': + printf("%s " VERSION "\n", progname); + return 0; + + case ':': /* option missing */ + case '?': /* unknown option */ + default: + goto synopsis; + } + } + if (optind >= argc) + goto synopsis; + + if (regcomp(&name_regex, opt_name_pattern, + REG_EXTENDED | REG_NOSUB) != 0) { + fprintf(stderr, _("%s: invalid regular expression \"%s\"\n"), + progname, opt_name_pattern); + return 1; + } + + while (optind < argc) { + if (nftw(argv[optind], do_print, 0, + opt_walk_physical * FTW_PHYS) < 0) { + fprintf(stderr, "%s: %s\n", progname, + strerror_ea(errno)); + } + optind++; + } + + return (had_errors ? 1 : 0); + +synopsis: + fprintf(stderr, _("Usage: %s %s\n" + "Try `%s --help' for more information.\n"), + progname, CMD_LINE_SPEC, progname); + return 2; +} + |