(root)/
attr-2.5.1/
tools/
getfattr.c
       1  /*
       2    File: getfattr.c
       3    (Linux Extended Attributes)
       4  
       5    Copyright (C) 2001-2002 Andreas Gruenbacher <andreas.gruenbacher@gmail.com>
       6    Copyright (C) 2001-2002 Silicon Graphics, Inc.  All Rights Reserved.
       7  
       8    This program is free software: you can redistribute it and/or modify it
       9    under the terms of the GNU General Public License as published by
      10    the Free Software Foundation, either version 2 of the License, or
      11    (at your option) any later version.
      12  
      13    This program is distributed in the hope that it will be useful,
      14    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16    GNU General Public License for more details.
      17  
      18    You should have received a copy of the GNU General Public License
      19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20  */
      21  
      22  #include "config.h"
      23  
      24  #include <stdio.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <errno.h>
      28  #include <ctype.h>
      29  #include <getopt.h>
      30  #include <regex.h>
      31  #include <locale.h>
      32  #include <libgen.h>
      33  #include <sys/xattr.h>
      34  
      35  #include "walk_tree.h"
      36  #include "misc.h"
      37  
      38  #define CMD_LINE_OPTIONS "n:de:m:hRLP"
      39  #define CMD_LINE_SPEC "[-hRLP] [-n name|-d] [-e en] [-m pattern] path..."
      40  
      41  struct option long_options[] = {
      42  	{ "name",		1, 0, 'n' },
      43  	{ "dump",		0, 0, 'd' },
      44  	{ "encoding",		1, 0, 'e' },
      45  	{ "match",		1, 0, 'm' },
      46  	{ "only-values",	0, 0, 'v' },
      47  	{ "no-dereference",	0, 0, 'h' },
      48  	{ "absolute-names",	0, 0, 'a' },
      49  	{ "one-file-system",	0, 0, 1 },
      50  	{ "recursive",		0, 0, 'R' },
      51  	{ "logical",		0, 0, 'L' },
      52  	{ "physical",		0, 0, 'P' },
      53  	{ "version",		0, 0, 'V' },
      54  	{ "help",		0, 0, 'H' },
      55  	{ NULL,			0, 0, 0 }
      56  };
      57  
      58  int walk_flags = WALK_TREE_DEREFERENCE;
      59  int opt_dump;  /* dump attribute values (or only list the names) */
      60  char *opt_name;  /* dump named attributes */
      61  char *opt_name_pattern = "^user\\.";  /* include only matching names */
      62  char *opt_encoding;  /* encode values automatically (NULL), or as "text",
      63                          "hex", or "base64" */
      64  char opt_value_only;  /* dump the value only, without any decoration */
      65  int opt_strip_leading_slash = 1;  /* strip leading '/' from path names */
      66  
      67  const char *progname;
      68  int absolute_warning;
      69  int had_errors;
      70  regex_t name_regex;
      71  
      72  
      73  static const char *xquote(const char *str, const char *quote_chars)
      74  {
      75  	const char *q = quote(str, quote_chars);
      76  	if (q == NULL) {
      77  		fprintf(stderr, "%s: %s\n", progname, strerror(errno));
      78  		exit(1);
      79  	}
      80  	return q;
      81  }
      82  
      83  int do_getxattr(const char *path, const char *name, void *value, size_t size)
      84  {
      85  	return ((walk_flags & WALK_TREE_DEREFERENCE) ?
      86  		getxattr : lgetxattr)(path, name, value, size);
      87  }
      88  
      89  int do_listxattr(const char *path, char *list, size_t size)
      90  {
      91  	return ((walk_flags & WALK_TREE_DEREFERENCE) ?
      92  		listxattr : llistxattr)(path, list, size);
      93  }
      94  
      95  const char *strerror_ea(int err)
      96  {
      97  #ifdef __linux__
      98  	/* The Linux kernel does not define ENOATTR, but maps it to ENODATA. */
      99  	if (err == ENODATA)
     100  		return _("No such attribute");
     101  #endif
     102  	return strerror(err);
     103  }
     104  
     105  int pstrcmp(const void *a, const void *b)
     106  {
     107  	return strcmp(*(const char **)a, *(const char **)b);
     108  }
     109  
     110  int well_enough_printable(const char *value, size_t size)
     111  {
     112  	size_t n, nonpr = 0;
     113  
     114  	/* Don't count the NULL terminator if there is one */
     115  	if (size && !value[size - 1])
     116  		size--;
     117  
     118  	for (n=0; n < size; n++)
     119  		if (!isprint(*value++))
     120  			nonpr++;
     121  
     122  	return (size >= nonpr*8);  /* no more than 1/8 non-printable chars */
     123  }
     124  
     125  const char *encode(const char *value, size_t *size)
     126  {
     127  	static char *encoded;
     128  	static size_t encoded_size;
     129  	char *enc, *e;
     130  	
     131  	if (opt_encoding == NULL) {
     132  		if (well_enough_printable(value, *size))
     133  			enc = "text";
     134  		else
     135  			enc = "base64";
     136  	} else
     137  		enc = opt_encoding;
     138  
     139  	if (strcmp(enc, "text") == 0) {
     140  		size_t n, extra = 0;
     141  
     142  		for (e=(char *)value; e < value + *size; e++) {
     143  			if (*e == '\0' || *e == '\n' || *e == '\r')
     144  				extra += 4;
     145  			else if (*e == '\\' || *e == '"')
     146  				extra++;
     147  		}
     148  		if (high_water_alloc((void **)&encoded, &encoded_size,
     149  				     *size + extra + 3)) {
     150  			perror(progname);
     151  			had_errors++;
     152  			return NULL;
     153  		}
     154  		e = encoded;
     155  		*e++='"';
     156  		for (n = 0; n < *size; n++, value++) {
     157  			if (*value == '\0' && n + 1 == *size)
     158  				break;
     159  			if (*value == '\0' || *value == '\n' || *value == '\r') {
     160  				*e++ = '\\';
     161  				*e++ = '0' + ((unsigned char)*value >> 6);
     162  				*e++ = '0' + (((unsigned char)*value & 070) >> 3);
     163  				*e++ = '0' + ((unsigned char)*value & 07);
     164  			} else if (*value == '\\' || *value == '"') {
     165  				*e++ = '\\';
     166  				*e++ = *value;
     167  			} else {
     168  				*e++ = *value;
     169  			}
     170  		}
     171  		*e++ = '"';
     172  		*e = '\0';
     173  		*size = (e - encoded);
     174  	} else if (strcmp(enc, "hex") == 0) {
     175  		static const char *digits = "0123456789abcdef";
     176  		size_t n;
     177  
     178  		if (high_water_alloc((void **)&encoded, &encoded_size,
     179  				     *size * 2 + 4)) {
     180  			perror(progname);
     181  			had_errors++;
     182  			return NULL;
     183  		}
     184  		e = encoded;
     185  		*e++='0'; *e++ = 'x';
     186  		for (n = 0; n < *size; n++, value++) {
     187  			*e++ = digits[((unsigned char)*value >> 4)];
     188  			*e++ = digits[((unsigned char)*value & 0x0F)];
     189  		}
     190  		*e = '\0';
     191  		*size = (e - encoded);
     192  	} else if (strcmp(enc, "base64") == 0) {
     193  		static const char *digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
     194  					    "ghijklmnopqrstuvwxyz0123456789+/";
     195  		size_t n;
     196  
     197  		if (high_water_alloc((void **)&encoded, &encoded_size,
     198  				     (*size + 2) / 3 * 4 + 1)) {
     199  			perror(progname);
     200  			had_errors++;
     201  			return NULL;
     202  		}
     203  		e = encoded;
     204  		*e++='0'; *e++ = 's';
     205  		for (n=0; n + 2 < *size; n += 3) {
     206  			*e++ = digits[(unsigned char)value[0] >> 2];
     207  			*e++ = digits[(((unsigned char)value[0] & 0x03) << 4) |
     208  			              (((unsigned char)value[1] & 0xF0) >> 4)];
     209  			*e++ = digits[(((unsigned char)value[1] & 0x0F) << 2) |
     210  			              ((unsigned char)value[2] >> 6)];
     211  			*e++ = digits[(unsigned char)value[2] & 0x3F];
     212  			value += 3;
     213  		}
     214  		if (*size - n == 2) {
     215  			*e++ = digits[(unsigned char)value[0] >> 2];
     216  			*e++ = digits[(((unsigned char)value[0] & 0x03) << 4) |
     217  			              (((unsigned char)value[1] & 0xF0) >> 4)];
     218  			*e++ = digits[((unsigned char)value[1] & 0x0F) << 2];
     219  			*e++ = '=';
     220  		} else if (*size - n == 1) {
     221  			*e++ = digits[(unsigned char)value[0] >> 2];
     222  			*e++ = digits[((unsigned char)value[0] & 0x03) << 4];
     223  			*e++ = '=';
     224  			*e++ = '=';
     225  		}
     226  		*e = '\0';
     227  		*size = (e - encoded);
     228  	}
     229  	return encoded;
     230  }
     231  
     232  int print_attribute(const char *path, const char *name, int *header_printed)
     233  {
     234  	static char *value;
     235  	static size_t value_size;
     236  	int rval = 0;
     237  	size_t length = 0;
     238  
     239  	if (opt_dump || opt_value_only) {
     240  		rval = do_getxattr(path, name, NULL, 0);
     241  		if (rval < 0) {
     242  			fprintf(stderr, "%s: ", xquote(path, "\n\r"));
     243  			fprintf(stderr, "%s: %s\n", xquote(name, "\n\r"),
     244  				strerror_ea(errno));
     245  			return 1;
     246  		}
     247  		if (high_water_alloc((void **)&value, &value_size, rval)) {
     248  			perror(progname);
     249  			had_errors++;
     250  			return 1;
     251  		}
     252  		rval = do_getxattr(path, name, value, value_size);
     253  		if (rval < 0) {
     254  			fprintf(stderr, "%s: ", xquote(path, "\n\r"));
     255  			fprintf(stderr, "%s: %s\n", xquote(name, "\n\r"),
     256  				strerror_ea(errno));
     257  			return 1;
     258  		}
     259  		length = rval;
     260  	}
     261  
     262  	if (opt_strip_leading_slash) {
     263  		if (*path == '/') {
     264  			if (!absolute_warning) {
     265  				fprintf(stderr, _("%s: Removing leading '/' "
     266  					"from absolute path names\n"),
     267  					progname);
     268  				absolute_warning = 1;
     269  			}
     270  			while (*path == '/')
     271  				path++;
     272  		} else if (*path == '.' && *(path+1) == '/')
     273  			while (*++path == '/')
     274  				/* nothing */ ;
     275  		if (*path == '\0')
     276  			path = ".";
     277  	}
     278  
     279  	if (!*header_printed && !opt_value_only) {
     280  		printf("# file: %s\n", xquote(path, "\n\r"));
     281  		*header_printed = 1;
     282  	}
     283  
     284  	if (opt_value_only)
     285  		fwrite(value, length, 1, stdout);
     286  	else if (opt_dump) {
     287  		const char *enc = encode(value, &length);
     288  		
     289  		if (enc)
     290  			printf("%s=%s\n", xquote(name, "=\n\r"), enc);
     291  	} else
     292  		puts(xquote(name, "=\n\r"));
     293  
     294  	return 0;
     295  }
     296  
     297  int list_attributes(const char *path, int *header_printed)
     298  {
     299  	static char *list;
     300  	static size_t list_size;
     301  	static char **names;
     302  	static size_t names_size;
     303  	int num_names = 0;
     304  	ssize_t length;
     305  	char *l;
     306  
     307  	length = do_listxattr(path, NULL, 0);
     308  	if (length < 0) {
     309  		fprintf(stderr, "%s: %s: %s\n", progname, xquote(path, "\n\r"),
     310  			strerror_ea(errno));
     311  		had_errors++;
     312  		return 1;
     313  	} else if (length == 0)
     314  		return 0;
     315  		
     316  	if (high_water_alloc((void **)&list, &list_size, length)) {
     317  		perror(progname);
     318  		had_errors++;
     319  		return 1;
     320  	}
     321  
     322  	length = do_listxattr(path, list, list_size);
     323  	if (length < 0) {
     324  		perror(xquote(path, "\n\r"));
     325  		had_errors++;
     326  		return 1;
     327  	}
     328  
     329  	for (l = list; l != list + length; l = strchr(l, '\0')+1) {
     330  		if (*l == '\0')	/* not a name, kernel bug */
     331  			continue;
     332  
     333  		if (regexec(&name_regex, l, 0, NULL, 0) != 0)
     334  			continue;
     335  
     336  		if (names_size < (num_names+1) * sizeof(*names)) {
     337  			if (high_water_alloc((void **)&names, &names_size,
     338  				             (num_names+1) * sizeof(*names))) {
     339  				perror(progname);
     340  				had_errors++;
     341  				return 1;
     342  			}
     343  		}
     344  
     345  		names[num_names++] = l;
     346  	}
     347  
     348  	qsort(names, num_names, sizeof(*names), pstrcmp);
     349  
     350  	if (num_names) {
     351  		int n;
     352  
     353  		for (n = 0; n < num_names; n++)
     354  			print_attribute(path, names[n], header_printed);
     355  	}
     356  	return 0;
     357  }
     358  
     359  int do_print(const char *path, const struct stat *stat, int walk_flags,
     360  	     void *unused)
     361  {
     362  	int header_printed = 0;
     363  	int err = 0;
     364  
     365  	if (walk_flags & WALK_TREE_FAILED) {
     366  		fprintf(stderr, "%s: %s: %s\n", progname, xquote(path, "\n\r"),
     367  			strerror(errno));
     368  		return 1;
     369  	}
     370  
     371  	if (opt_name)
     372  		err = print_attribute(path, opt_name, &header_printed);
     373  	else
     374  		err = list_attributes(path, &header_printed);
     375  
     376  	if (header_printed)
     377  		puts("");
     378  	return err;
     379  }
     380  
     381  void help(void)
     382  {
     383  	printf(_("%s %s -- get extended attributes\n"),
     384  	       progname, VERSION);
     385  	printf(_("Usage: %s %s\n"),
     386  	         progname, _(CMD_LINE_SPEC));
     387  	printf(_(
     388  "  -n, --name=name         get the named extended attribute value\n"
     389  "  -d, --dump              get all extended attribute values\n"
     390  "  -e, --encoding=...      encode values (as 'text', 'hex' or 'base64')\n"
     391  "      --match=pattern     only get attributes with names matching pattern\n"
     392  "      --only-values       print the bare values only\n"
     393  "  -h, --no-dereference    do not dereference symbolic links\n"
     394  "      --one-file-system   skip files on different filesystems\n"
     395  "      --absolute-names    don't strip leading '/' in pathnames\n"
     396  "  -R, --recursive         recurse into subdirectories\n"
     397  "  -L, --logical           logical walk, follow symbolic links\n"
     398  "  -P  --physical          physical walk, do not follow symbolic links\n"
     399  "      --version           print version and exit\n"
     400  "      --help              this help text\n"));
     401  }
     402  
     403  int main(int argc, char *argv[])
     404  {
     405  	int opt;
     406  
     407  	progname = basename(argv[0]);
     408  
     409  	setlocale(LC_CTYPE, "");
     410  	setlocale(LC_MESSAGES, "");
     411  	bindtextdomain(PACKAGE, LOCALEDIR);
     412  	textdomain(PACKAGE);
     413  
     414  	while ((opt = getopt_long(argc, argv, CMD_LINE_OPTIONS,
     415  		                  long_options, NULL)) != -1) {
     416  		switch(opt) {
     417  			case 'a': /* absolute names */
     418  				opt_strip_leading_slash = 0;
     419  				break;
     420  
     421  			case 'd': /* dump attribute values */
     422  				opt_dump = 1;
     423  				break;
     424  
     425  			case 'e':  /* encoding */
     426  				if (strcmp(optarg, "text") != 0 &&
     427  				    strcmp(optarg, "hex") != 0 &&
     428  				    strcmp(optarg, "base64") != 0)
     429  					goto synopsis;
     430  				opt_encoding = optarg;
     431  				break;
     432  
     433  			case 'H':
     434  				help();
     435  				return 0;
     436  
     437  			case 'h': /* do not dereference symlinks */
     438  				walk_flags &= ~WALK_TREE_DEREFERENCE;
     439  				break;
     440  
     441  			case 'n':  /* get named attribute */
     442  				opt_dump = 1;
     443  				opt_name = optarg;
     444  				break;
     445  
     446  			case 'm':  /* regular expression for filtering names */
     447  				opt_name_pattern = optarg;
     448  				if (strcmp(opt_name_pattern, "-") == 0)
     449  					opt_name_pattern = "";
     450  				break;
     451  
     452  			case 'v':  /* get attribute values only */
     453  				opt_value_only = 1;
     454  				break;
     455  
     456  			case 'L':
     457  				walk_flags |= WALK_TREE_LOGICAL;
     458  				walk_flags &= ~WALK_TREE_PHYSICAL;
     459  				break;
     460  
     461  			case 'P':
     462  				walk_flags |= WALK_TREE_PHYSICAL;
     463  				walk_flags &= ~WALK_TREE_LOGICAL;
     464  				break;
     465  
     466  			case 'R':
     467  				walk_flags |= WALK_TREE_RECURSIVE;
     468  				break;
     469  
     470  			case 1: /* one filesystem */
     471  				walk_flags |= WALK_TREE_ONE_FILESYSTEM;
     472  				break;
     473  
     474  			case 'V':
     475  				printf("%s " VERSION "\n", progname);
     476  				return 0;
     477  
     478  			case ':':  /* option missing */
     479  			case '?':  /* unknown option */
     480  			default:
     481  				goto synopsis;
     482  		}
     483  	}
     484  	if (optind >= argc)
     485  		goto synopsis;
     486  
     487  	if (regcomp(&name_regex, opt_name_pattern,
     488  	            REG_EXTENDED | REG_NOSUB) != 0) {
     489  		fprintf(stderr, _("%s: invalid regular expression \"%s\"\n"),
     490  			progname, opt_name_pattern);
     491  		return 1;
     492  	}
     493  
     494  	while (optind < argc) {
     495  		had_errors += walk_tree(argv[optind], walk_flags, 0,
     496  					do_print, NULL);
     497  		optind++;
     498  	}
     499  
     500  	return (had_errors ? 1 : 0);
     501  
     502  synopsis:
     503  	fprintf(stderr, _("Usage: %s %s\n"
     504  	                  "Try `%s --help' for more information.\n"),
     505  		progname, CMD_LINE_SPEC, progname);
     506  	return 2;
     507  }
     508