(root)/
attr-2.5.1/
tools/
setfattr.c
       1  /*
       2    File: setfattr.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 <limits.h>
      25  #include <stdio.h>
      26  #include <stdlib.h>
      27  #include <string.h>
      28  #include <getopt.h>
      29  #include <locale.h>
      30  #include <ctype.h>
      31  #include <libgen.h>
      32  #include <sys/xattr.h>
      33  #include <errno.h>
      34  
      35  #include "misc.h"
      36  
      37  #define CMD_LINE_OPTIONS "n:x:v:h"
      38  #define CMD_LINE_SPEC1 "{-n name} [-v value] [-h] file..."
      39  #define CMD_LINE_SPEC2 "{-x name} [-h] file..."
      40  
      41  struct option long_options[] = {
      42  	{ "name",		1, 0, 'n' }, 
      43  	{ "remove",		1, 0, 'x' },
      44  	{ "value",		1, 0, 'v' },
      45  	{ "no-dereference",	0, 0, 'h' },
      46  	{ "restore",		1, 0, 'B' },
      47  	{ "raw",		0, 0, CHAR_MAX + 1 },
      48  	{ "version",		0, 0, 'V' },
      49  	{ "help",		0, 0, 'H' },
      50  	{ NULL,			0, 0, 0 }
      51  };
      52  
      53  char *opt_name;  /* attribute name to set */
      54  char *opt_value;  /* attribute value */
      55  int opt_set;  /* set an attribute */
      56  int opt_remove;  /* remove an attribute */
      57  int opt_restore;  /* restore has been run */
      58  int opt_deref = 1;  /* dereference symbolic links */
      59  int opt_raw;  /* attribute value is not encoded */
      60  
      61  int had_errors;
      62  const char *progname;
      63  
      64  int do_set(const char *path, const char *name, const char *value);
      65  const char *decode(const char *value, size_t *size);
      66  int restore(const char *filename);
      67  int hex_digit(char c);
      68  int base64_digit(char c);
      69  
      70  const char *strerror_ea(int err)
      71  {
      72  #ifdef __linux__
      73  	/* The Linux kernel does not define ENOATTR, but maps it to ENODATA. */
      74  	if (err == ENODATA)
      75  		return _("No such attribute");
      76  #endif
      77  	return strerror(err);
      78  }
      79  
      80  static const char *xquote(const char *str, const char *quote_chars)
      81  {
      82  	const char *q = quote(str, quote_chars);
      83  	if (q == NULL) {
      84  		fprintf(stderr, "%s: %s\n", progname, strerror(errno));
      85  		exit(1);
      86  	}
      87  	return q;
      88  }
      89  
      90  int do_setxattr(const char *path, const char *name,
      91  		const void *value, size_t size)
      92  {
      93  	return (opt_deref ? setxattr : lsetxattr)(path, name, value, size, 0);
      94  }
      95  
      96  int do_removexattr(const char *path, const char *name)
      97  {
      98  	return (opt_deref ? removexattr : lremovexattr)(path, name);
      99  }
     100  
     101  int restore(const char *filename)
     102  {
     103  	static char *path;
     104  	static size_t path_size;
     105  	FILE *file;
     106  	char *l;
     107  	int line = 0, backup_line, status = 0;
     108  	
     109  	if (strcmp(filename, "-") == 0)
     110  		file = stdin;
     111  	else {
     112  		file = fopen(filename, "r");
     113  		if (file == NULL) {
     114  				fprintf(stderr, "%s: %s: %s\n",
     115  					progname, filename, strerror_ea(errno));
     116  				return 1;
     117  		}
     118  	}
     119  
     120  	for(;;) {
     121  		backup_line = line;
     122  		while ((l = next_line(file)) != NULL && *l == '\0')
     123  			line++;
     124  		if (l == NULL)
     125  			break;
     126  		line++;
     127  		if (strncmp(l, "# file: ", 8) != 0) {
     128  			if (file != stdin) {
     129  				fprintf(stderr, _("%s: %s: No filename found "
     130  				                  "in line %d, aborting\n"),
     131  					progname, filename, backup_line);
     132  			} else {
     133  				fprintf(stderr, _("%s: No filename found in "
     134  			                          "line %d of standard input, "
     135  						  "aborting\n"),
     136  					  progname, backup_line);
     137  			}
     138  			status = 1;
     139  			goto cleanup;
     140  		} else
     141  			l += 8;
     142  		l = unquote(l);
     143  		if (high_water_alloc((void **)&path, &path_size, strlen(l)+1)) {
     144  			perror(progname);
     145  			status = 1;
     146  			goto cleanup;
     147  		}
     148  		strcpy(path, l);
     149  
     150  		while ((l = next_line(file)) != NULL && *l != '\0') {
     151  			char *name = l, *value = strchr(l, '=');
     152  			line++;
     153  			if (value)
     154  				*value++ = '\0';
     155  			status = do_set(path, unquote(name), value);
     156  		}
     157  		if (l == NULL)
     158  			break;
     159  		line++;
     160  	}
     161  	if (!feof(file)) {
     162  		fprintf(stderr, "%s: %s: %s\n", progname, filename,
     163  			strerror(errno));
     164  		if (!status)
     165  			status = 1;
     166  	}
     167  
     168  cleanup:
     169  	if (path)
     170  		free(path);
     171  	if (file != stdin)
     172  		fclose(file);
     173  	if (status)
     174  		had_errors++;
     175  	return status;
     176  }
     177  
     178  void help(void)
     179  {
     180  	printf(_("%s %s -- set extended attributes\n"), progname, VERSION);
     181  	printf(_("Usage: %s %s\n"), progname, CMD_LINE_SPEC1);
     182  	printf(_("       %s %s\n"), progname, CMD_LINE_SPEC2);
     183  	printf(_(
     184  "  -n, --name=name         set the value of the named extended attribute\n"
     185  "  -x, --remove=name       remove the named extended attribute\n"
     186  "  -v, --value=value       use value as the attribute value\n"
     187  "  -h, --no-dereference    do not dereference symbolic links\n"
     188  "      --restore=file      restore extended attributes\n"
     189  "      --raw               attribute value is not encoded\n"
     190  "      --version           print version and exit\n"
     191  "      --help              this help text\n"));
     192  }
     193  
     194  int main(int argc, char *argv[])
     195  {
     196  	int opt;
     197  
     198  	progname = basename(argv[0]);
     199  
     200  	setlocale(LC_CTYPE, "");
     201  	setlocale(LC_MESSAGES, "");
     202  	bindtextdomain(PACKAGE, LOCALEDIR);
     203  	textdomain(PACKAGE);
     204  
     205  	while ((opt = getopt_long(argc, argv, CMD_LINE_OPTIONS,
     206  		                  long_options, NULL)) != -1) {
     207  		switch(opt) {
     208  			case 'n':  /* attribute name */
     209  				if (opt_name || opt_remove)
     210  					goto synopsis;
     211  				opt_name = optarg;
     212  				opt_set = 1;
     213  				break;
     214  
     215  			case 'h':  /* set attribute on symlink itself */
     216  				opt_deref = 0;
     217  				break;
     218  
     219  			case 'v':  /* attribute value */
     220  				if (opt_value || opt_remove)
     221  					goto synopsis;
     222  				opt_value = optarg;
     223  				break;
     224  
     225  			case CHAR_MAX + 1:
     226  				opt_raw = 1;
     227  				break;
     228  
     229  			case 'x':  /* remove attribute */
     230  				if (opt_name || opt_set)
     231  					goto synopsis;
     232  				opt_name = optarg;
     233  				opt_remove = 1;
     234  				break;
     235  
     236  			case 'B':  /* restore */
     237  				opt_restore = 1;
     238  				restore(optarg);
     239  				break;
     240  
     241  			case 'V':
     242  				printf("%s " VERSION "\n", progname);
     243  				return 0;
     244  
     245  			case 'H':
     246  				help();
     247  				return 0;
     248  
     249  			default:
     250  				goto synopsis;
     251  		}
     252  	}
     253  	if (!(((opt_remove || opt_set) && optind < argc) || opt_restore))
     254  		goto synopsis;
     255  
     256  	while (optind < argc) {
     257  		do_set(argv[optind], unquote(opt_name), opt_value);
     258  		optind++;
     259  	}
     260  
     261  	return (had_errors ? 1 : 0);
     262  
     263  synopsis:
     264  	fprintf(stderr, _("Usage: %s %s\n"
     265  			  "       %s %s\n"
     266  	                  "Try `%s --help' for more information.\n"),
     267  		progname, CMD_LINE_SPEC1, progname, CMD_LINE_SPEC2, progname);
     268  	return 2;
     269  }
     270  
     271  int do_set(const char *path, const char *name, const char *value)
     272  {
     273  	size_t size = 0;
     274  	int error;
     275  
     276  	if (value) {
     277  		size = strlen(value);
     278  		if (!opt_raw)
     279  			value = decode(value, &size);
     280  		if (!value)
     281  			return 1;
     282  	}
     283  	if (opt_set || opt_restore)
     284  		error = do_setxattr(path, name, value, size);
     285  	else
     286  		error = do_removexattr(path, name);
     287  
     288  	if (error < 0) {
     289  		fprintf(stderr, "%s: %s: %s\n",
     290  			progname, xquote(path, "\n\r"), strerror_ea(errno));
     291  		had_errors++;
     292  		return 1;
     293  	}
     294  	return 0;
     295  }
     296  
     297  const char *decode(const char *value, size_t *size)
     298  {
     299  	static char *decoded;
     300  	static size_t decoded_size;
     301  
     302  	if (*size == 0)
     303  		return "";
     304  	if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) {
     305  		const char *v = value+2, *end = value + *size;
     306  		char *d;
     307  
     308  		if (high_water_alloc((void **)&decoded, &decoded_size,
     309  				     *size / 2)) {
     310  			fprintf(stderr, "%s: %s\n",
     311  				progname, strerror_ea(errno));
     312  			had_errors++;
     313  			return NULL;
     314  		}
     315  		d = decoded;
     316  		while (v < end) {
     317  			int d1, d0;
     318  
     319  			while (v < end && isspace(*v))
     320  				v++;
     321  			if (v == end)
     322  				break;
     323  			d1 = hex_digit(*v++);
     324  			while (v < end && isspace(*v))
     325  				v++;
     326  			if (v == end) {
     327  		bad_hex_encoding:
     328  				fprintf(stderr, "bad input encoding\n");
     329  				had_errors++;
     330  				return NULL;
     331  			}
     332  			d0 = hex_digit(*v++);
     333  			if (d1 < 0 || d0 < 0)
     334  				goto bad_hex_encoding;
     335  			*d++ = ((d1 << 4) | d0);
     336  		}
     337  		*size = d - decoded;
     338  	} else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) {
     339  		const char *v = value+2, *end = value + *size;
     340  		int d0, d1, d2, d3;
     341  		char *d;
     342  
     343  		if (high_water_alloc((void **)&decoded, &decoded_size,
     344  				     *size / 4 * 3)) {
     345  			fprintf(stderr, "%s: %s\n",
     346  				progname, strerror_ea(errno));
     347  			had_errors++;
     348  			return NULL;
     349  		}
     350  		d = decoded;
     351  		for(;;) {
     352  			while (v < end && isspace(*v))
     353  				v++;
     354  			if (v == end) {
     355  				d0 = d1 = d2 = d3 = -2;
     356  				break;
     357  			}
     358  			if (v + 4 > end) {
     359  		bad_base64_encoding:
     360  				fprintf(stderr, "bad input encoding\n");
     361  				had_errors++;
     362  				return NULL;
     363  			}
     364  			d0 = base64_digit(*v++);
     365  			d1 = base64_digit(*v++);
     366  			d2 = base64_digit(*v++);
     367  			d3 = base64_digit(*v++);
     368  			if (d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0)
     369  				break;
     370  
     371  			*d++ = (char)((d0 << 2) | (d1 >> 4));
     372  			*d++ = (char)((d1 << 4) | (d2 >> 2));
     373  			*d++ = (char)((d2 << 6) | d3);
     374  		}
     375  		if (d0 == -2) {
     376  			if (d1 != -2 || d2 != -2 || d3 != -2)
     377  				goto bad_base64_encoding;
     378  			goto base64_end;
     379  		}
     380  		if (d0 == -1 || d1 < 0 || d2 == -1 || d3 == -1)
     381  			goto bad_base64_encoding;
     382  		*d++ = (char)((d0 << 2) | (d1 >> 4));
     383  		if (d2 != -2)
     384  			*d++ = (char)((d1 << 4) | (d2 >> 2));
     385  		else {
     386  			if (d1 & 0x0F || d3 != -2)
     387  				goto bad_base64_encoding;
     388  			goto base64_end;
     389  		}
     390  		if (d3 != -2)
     391  			*d++ = (char)((d2 << 6) | d3);
     392  		else if (d2 & 0x03)
     393  			goto bad_base64_encoding;
     394  	base64_end:
     395  		while (v < end && isspace(*v))
     396  			v++;
     397  		if (v + 4 <= end && *v == '=') {
     398  			if (*++v != '=' || *++v != '=' || *++v != '=')
     399  				goto bad_base64_encoding;
     400  			v++;
     401  		}
     402  		while (v < end && isspace(*v))
     403  			v++;
     404  		if (v < end)
     405  			goto bad_base64_encoding;
     406  		*size = d - decoded;
     407  	} else {
     408  		const char *v = value, *end = value + *size;
     409  		char *d;
     410  
     411  		if (end > v+1 && *v == '"' && *(end-1) == '"') {
     412  			v++;
     413  			end--;
     414  		}
     415  
     416  		if (high_water_alloc((void **)&decoded, &decoded_size, *size)) {
     417  			fprintf(stderr, "%s: %s\n",
     418  				progname, strerror_ea(errno));
     419  			had_errors++;
     420  			return NULL;
     421  		}
     422  		d = decoded;
     423  
     424  		while (v < end) {
     425  			if (v[0] == '\\') {
     426  				if (v[1] == '\\' || v[1] == '"') {
     427  					*d++ = *++v; v++;
     428  				} else if (v[1] >= '0' && v[1] <= '7') {
     429  					int c = 0;
     430  					v++;
     431  					c = (*v++ - '0');
     432  					if (*v >= '0' && *v <= '7')
     433  						c = (c << 3) + (*v++ - '0');
     434  					if (*v >= '0' && *v <= '7')
     435  						c = (c << 3) + (*v++ - '0');
     436  					*d++ = c;
     437  				} else
     438  					*d++ = *v++;
     439  			} else
     440  				*d++ = *v++;
     441  		}
     442  		*size = d - decoded;
     443  	}
     444  	return decoded;
     445  }
     446  
     447  int hex_digit(char c)
     448  {
     449  	if (c >= '0' && c <= '9')
     450  		return c - '0';
     451  	else if (c >= 'A' && c <= 'F')
     452  		return c - 'A' + 10;
     453  	else if (c >= 'a' && c <= 'f')
     454  		return c - 'a' + 10;
     455  	else
     456  		return -1;
     457  }
     458  
     459  int base64_digit(char c)
     460  {
     461  	if (c >= 'A' && c <= 'Z')
     462  		return c - 'A';
     463  	else if (c >= 'a' && c <= 'z')
     464  		return 26 + c - 'a';
     465  	else if (c >= '0' && c <= '9')
     466  		return 52 + c - '0';
     467  	else if (c == '+')
     468  		return 62;
     469  	else if (c == '/')
     470  		return 63;
     471  	else if (c == '=')
     472  		return -2;
     473  	else
     474  		return -1;
     475  }
     476