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