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