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