diff options
author | Mike Frysinger <vapier@gentoo.org> | 2014-01-09 16:30:17 -0500 |
---|---|---|
committer | Andreas Gruenbacher <agruen@linbit.com> | 2014-01-09 23:15:09 +0100 |
commit | 9ce21bd8c28792dc81aba2349ffc8a64bf5e5df0 (patch) | |
tree | 534e009f5324de72ba7356ce68de614aa263ef5b /tools/getfattr.c | |
parent | b010dbc0db987468ab50fdae098797605808e342 (diff) | |
download | attr-9ce21bd8c28792dc81aba2349ffc8a64bf5e5df0.tar.gz |
modernize build system
This deletes the hand rolled build system and replaces it entirely
with autotools. The overall diffstat shows that this is a clear
win, and it makes the package build/install like every other autotool
package out there which makes the lives of distro maintainers a lot
easier.
This should also be faster by virtue of using a non-recursive build.
Things to note:
- to generate autotools:
./autogen.sh
- to see full compile output:
make V=1
- to build specific targets:
make attr getfattr ...
- to run tests:
make check
- to create a release:
make distcheck -j
Other non-developer things:
- the xattr.conf file is now installed into sysconfdir by default
- man pages are no longer compressed as this is uncommon in the
autotools world and distros don't need it anyways (they already
handle it automatically for most pakages)
- the minor # of the shared library is now based on the package
version so it'll be much bigger ... this isn't a problem, and
is actually a bugfix (older releases didn't change when they
should have)
(Again, this is all using the standard autotool targets.)
Diffstat (limited to 'tools/getfattr.c')
-rw-r--r-- | tools/getfattr.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/tools/getfattr.c b/tools/getfattr.c new file mode 100644 index 0000000..692d2d8 --- /dev/null +++ b/tools/getfattr.c @@ -0,0 +1,497 @@ +/* + File: getfattr.c + (Linux Extended Attributes) + + Copyright (C) 2001-2002 Andreas Gruenbacher <a.gruenbacher@bestbits.at> + Copyright (C) 2001-2002 Silicon Graphics, Inc. All Rights Reserved. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <getopt.h> +#include <regex.h> +#include <locale.h> +#include <libgen.h> + +#include <attr/xattr.h> +#include "config.h" +#include "walk_tree.h" +#include "misc.h" + +#define CMD_LINE_OPTIONS "n:de:m:hRLP" +#define CMD_LINE_SPEC "[-hRLP] [-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 walk_flags = WALK_TREE_DEREFERENCE; +int opt_dump; /* dump attribute values (or only list the names) */ +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 had_errors; +regex_t name_regex; + + +static const char *xquote(const char *str, const char *quote_chars) +{ + const char *q = quote(str, quote_chars); + if (q == NULL) { + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + exit(1); + } + return q; +} + +int do_getxattr(const char *path, const char *name, void *value, size_t size) +{ + return ((walk_flags & WALK_TREE_DEREFERENCE) ? + getxattr : lgetxattr)(path, name, value, size); +} + +int do_listxattr(const char *path, char *list, size_t size) +{ + return ((walk_flags & WALK_TREE_DEREFERENCE) ? + listxattr : llistxattr)(path, list, size); +} + +const char *strerror_ea(int err) +{ +#ifdef __linux__ + /* The Linux kernel does not define ENOATTR, but maps it to ENODATA. */ + if (err == ENODATA) + return _("No such attribute"); +#endif + return strerror(err); +} + +int pstrcmp(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +int well_enough_printable(const char *value, size_t size) +{ + size_t n, nonpr = 0; + + for (n=0; n < size; n++) + if (!isprint(*value++)) + nonpr++; + + return (size >= nonpr*8); /* no more than 1/8 non-printable chars */ +} + +const char *encode(const char *value, size_t *size) +{ + static char *encoded; + static size_t encoded_size; + char *enc, *e; + + if (opt_encoding == NULL) { + if (well_enough_printable(value, *size)) + enc = "text"; + else + enc = "base64"; + } else + enc = opt_encoding; + + if (strcmp(enc, "text") == 0) { + size_t n, extra = 0; + + for (e=(char *)value; e < value + *size; e++) { + if (*e == '\0' || *e == '\n' || *e == '\r') + extra += 4; + else if (*e == '\\' || *e == '"') + extra++; + } + if (high_water_alloc((void **)&encoded, &encoded_size, + *size + extra + 3)) { + perror(progname); + had_errors++; + return NULL; + } + e = encoded; + *e++='"'; + for (n = 0; n < *size; n++, value++) { + if (*value == '\0' && n + 1 == *size) + break; + if (*value == '\0' || *value == '\n' || *value == '\r') { + *e++ = '\\'; + *e++ = '0' + ((unsigned char)*value >> 6); + *e++ = '0' + (((unsigned char)*value & 070) >> 3); + *e++ = '0' + ((unsigned char)*value & 07); + } else if (*value == '\\' || *value == '"') { + *e++ = '\\'; + *e++ = *value; + } else { + *e++ = *value; + } + } + *e++ = '"'; + *e = '\0'; + *size = (e - encoded); + } else if (strcmp(enc, "hex") == 0) { + static const char *digits = "0123456789abcdef"; + size_t n; + + if (high_water_alloc((void **)&encoded, &encoded_size, + *size * 2 + 4)) { + perror(progname); + had_errors++; + return NULL; + } + e = encoded; + *e++='0'; *e++ = 'x'; + for (n = 0; n < *size; n++, value++) { + *e++ = digits[((unsigned char)*value >> 4)]; + *e++ = digits[((unsigned char)*value & 0x0F)]; + } + *e = '\0'; + *size = (e - encoded); + } else if (strcmp(enc, "base64") == 0) { + static const char *digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" + "ghijklmnopqrstuvwxyz0123456789+/"; + size_t n; + + if (high_water_alloc((void **)&encoded, &encoded_size, + (*size + 2) / 3 * 4 + 1)) { + perror(progname); + had_errors++; + return NULL; + } + e = encoded; + *e++='0'; *e++ = 's'; + for (n=0; n + 2 < *size; n += 3) { + *e++ = digits[(unsigned char)value[0] >> 2]; + *e++ = digits[(((unsigned char)value[0] & 0x03) << 4) | + (((unsigned char)value[1] & 0xF0) >> 4)]; + *e++ = digits[(((unsigned char)value[1] & 0x0F) << 2) | + ((unsigned char)value[2] >> 6)]; + *e++ = digits[(unsigned char)value[2] & 0x3F]; + value += 3; + } + if (*size - n == 2) { + *e++ = digits[(unsigned char)value[0] >> 2]; + *e++ = digits[(((unsigned char)value[0] & 0x03) << 4) | + (((unsigned char)value[1] & 0xF0) >> 4)]; + *e++ = digits[((unsigned char)value[1] & 0x0F) << 2]; + *e++ = '='; + } else if (*size - n == 1) { + *e++ = digits[(unsigned char)value[0] >> 2]; + *e++ = digits[((unsigned char)value[0] & 0x03) << 4]; + *e++ = '='; + *e++ = '='; + } + *e = '\0'; + *size = (e - encoded); + } + return encoded; +} + +int print_attribute(const char *path, const char *name, int *header_printed) +{ + static char *value; + static size_t value_size; + int rval = 0; + size_t length = 0; + + if (opt_dump || opt_value_only) { + rval = do_getxattr(path, name, NULL, 0); + if (rval < 0) { + fprintf(stderr, "%s: ", xquote(path, "\n\r")); + fprintf(stderr, "%s: %s\n", xquote(name, "\n\r"), + strerror_ea(errno)); + return 1; + } + if (high_water_alloc((void **)&value, &value_size, rval)) { + perror(progname); + had_errors++; + return 1; + } + rval = do_getxattr(path, name, value, value_size); + if (rval < 0) { + fprintf(stderr, "%s: ", xquote(path, "\n\r")); + fprintf(stderr, "%s: %s\n", xquote(name, "\n\r"), + strerror_ea(errno)); + return 1; + } + length = rval; + } + + 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", xquote(path, "\n\r")); + *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", xquote(name, "=\n\r"), enc); + } else + puts(xquote(name, "=\n\r")); + + 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) { + fprintf(stderr, "%s: %s: %s\n", progname, xquote(path, "\n\r"), + strerror_ea(errno)); + had_errors++; + return 1; + } else if (length == 0) + return 0; + + if (high_water_alloc((void **)&list, &list_size, length)) { + perror(progname); + had_errors++; + return 1; + } + + length = do_listxattr(path, list, list_size); + if (length < 0) { + perror(xquote(path, "\n\r")); + had_errors++; + return 1; + } + + for (l = list; l != list + length; l = strchr(l, '\0')+1) { + if (*l == '\0') /* not a name, kernel bug */ + continue; + + 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))) { + perror(progname); + had_errors++; + 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 walk_flags, + void *unused) +{ + int header_printed = 0; + int err = 0; + + if (walk_flags & WALK_TREE_FAILED) { + fprintf(stderr, "%s: %s: %s\n", progname, xquote(path, "\n\r"), + strerror(errno)); + return 1; + } + + if (opt_name) + err = print_attribute(path, opt_name, &header_printed); + else + err = list_attributes(path, &header_printed); + + if (header_printed) + puts(""); + return err; +} + +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_CTYPE, ""); + setlocale(LC_MESSAGES, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + 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 */ + walk_flags &= ~WALK_TREE_DEREFERENCE; + 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': + walk_flags |= WALK_TREE_LOGICAL; + walk_flags &= ~WALK_TREE_PHYSICAL; + break; + + case 'P': + walk_flags |= WALK_TREE_PHYSICAL; + walk_flags &= ~WALK_TREE_LOGICAL; + break; + + case 'R': + walk_flags |= WALK_TREE_RECURSIVE; + 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) { + had_errors += walk_tree(argv[optind], walk_flags, 0, + do_print, NULL); + 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; +} + |