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