/* File: setfattr.c (Linux Extended Attributes) Copyright (C) 2001-2002 Andreas Gruenbacher Copyright (C) 2001-2002 Silicon Graphics, Inc. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include "config.h" #include "misc.h" #define CMD_LINE_OPTIONS "n:x:v:h" #define CMD_LINE_SPEC "{-n name|-x name} [-v value] [-h] file..." struct option long_options[] = { { "name", 1, 0, 'n' }, { "remove", 1, 0, 'x' }, { "value", 1, 0, 'v' }, { "no-dereference", 0, 0, 'h' }, { "restore", 1, 0, 'B' }, { "version", 0, 0, 'V' }, { "help", 0, 0, 'H' }, { NULL, 0, 0, 0 } }; char *opt_name; /* attribute name to set */ char *opt_value; /* attribute value */ int opt_set; /* set an attribute */ int opt_remove; /* remove an attribute */ int opt_restore; /* restore has been run */ int opt_deref = 1; /* dereference symbolic links */ int had_errors; const char *progname; int do_set(const char *path, const char *name, const char *value); const char *decode(const char *value, size_t *size); int restore(const char *filename); char *next_line(FILE *file); int hex_digit(char c); int base64_digit(char c); const char *strerror_ea(int err) { if (err == ENODATA) return _("No such attribute"); return strerror(err); } static const char *xquote(const char *str) { const char *q = quote(str); if (q == NULL) { fprintf(stderr, "%s: %s\n", progname, strerror(errno)); exit(1); } return q; } int do_setxattr(const char *path, const char *name, const void *value, size_t size) { return (opt_deref ? setxattr : lsetxattr)(path, name, value, size, 0); } int do_removexattr(const char *path, const char *name) { return (opt_deref ? removexattr : lremovexattr)(path, name); } int restore(const char *filename) { static char *path; static size_t path_size; FILE *file; char *l; int line = 0, backup_line, status = 0; if (strcmp(filename, "-") == 0) file = stdin; else { file = fopen(filename, "r"); if (file == NULL) { fprintf(stderr, "%s: %s: %s\n", progname, filename, strerror_ea(errno)); return 1; } } for(;;) { backup_line = line; while ((l = next_line(file)) != NULL && *l == '\0') line++; if (l == NULL) break; line++; if (strncmp(l, "# file: ", 8) != 0) { if (filename) { fprintf(stderr, _("%s: %s: No filename found " "in line %d, aborting\n"), progname, filename, backup_line); } else { fprintf(stderr, _("%s: No filename found in" "line %d of standard input, " "aborting\n"), progname, backup_line); } status = 1; goto cleanup; } else l += 8; l = unquote(l); if (high_water_alloc((void **)&path, &path_size, strlen(l)+1)) { perror(progname); status = 1; goto cleanup; } strcpy(path, l); while ((l = next_line(file)) != NULL && *l != '\0') { char *name = l, *value = strchr(l, '='); line++; if (value) *value++ = '\0'; status = do_set(path, unquote(name), value); } if (l != NULL) line++; } cleanup: if (path) free(path); if (file != stdin) fclose(file); if (status) had_errors++; return status; } void help(void) { printf(_("%s %s -- set extended attributes\n"), progname, VERSION); printf(_("Usage: %s %s\n"), progname, CMD_LINE_SPEC); printf(_( " -n, --name=name set the value of the named extended attribute\n" " -x, --remove=name remove the named extended attribute\n" " -v, --value=value use value as the attribute value\n" " -h, --no-dereference do not dereference symbolic links\n" " --restore=file restore extended attributes\n" " --version print version and exit\n" " --help this help text\n")); } char *next_line(FILE *file) { static char line[_POSIX_PATH_MAX+32], *c; if (!fgets(line, sizeof(line), file)) return NULL; c = strrchr(line, '\0'); while (c > line && (*(c-1) == '\n' || *(c-1) == '\r')) { c--; *c = '\0'; } return line; } int main(int argc, char *argv[]) { int opt; progname = basename(argv[0]); setlocale(LC_CTYPE, ""); setlocale(LC_MESSAGES, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); while ((opt = getopt_long(argc, argv, CMD_LINE_OPTIONS, long_options, NULL)) != -1) { switch(opt) { case 'n': /* attribute name */ if (opt_name || opt_remove) goto synopsis; opt_name = optarg; opt_set = 1; break; case 'h': /* set attribute on symlink itself */ opt_deref = 0; break; case 'v': /* attribute value */ if (opt_value || opt_remove) goto synopsis; opt_value = optarg; break; case 'x': /* remove attribute */ if (opt_name || opt_set) goto synopsis; opt_name = optarg; opt_remove = 1; break; case 'B': /* restore */ opt_restore = 1; restore(optarg); break; case 'V': printf("%s " VERSION "\n", progname); return 0; case 'H': help(); return 0; default: goto synopsis; } } if (!(((opt_remove || opt_set) && optind < argc) || opt_restore)) goto synopsis; while (optind < argc) { do_set(argv[optind], unquote(opt_name), opt_value); optind++; } return (had_errors ? 1 : 0); synopsis: fprintf(stderr, _("Usage: %s %s\n" "Try `%s --help' for more information.\n"), progname, CMD_LINE_SPEC, progname); return 2; } int do_set(const char *path, const char *name, const char *value) { size_t size = 0; int error; if (value) { size = strlen(value); value = decode(value, &size); if (!value) return 1; } if (opt_set || opt_restore) error = do_setxattr(path, name, value, size); else error = do_removexattr(path, name); if (error < 0) { fprintf(stderr, "%s: %s: %s\n", progname, xquote(path), strerror_ea(errno)); had_errors++; return 1; } return 0; } const char *decode(const char *value, size_t *size) { static char *decoded; static size_t decoded_size; if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { const char *v = value+2, *end = value + *size; char *d; if (high_water_alloc((void **)&decoded, &decoded_size, *size / 2)) { fprintf(stderr, "%s: %s\n", progname, strerror_ea(errno)); had_errors++; return NULL; } d = decoded; while (v < end) { int d1, d0; while (v < end && isspace(*v)) v++; if (v == end) break; d1 = hex_digit(*v++); while (v < end && isspace(*v)) v++; if (v == end) { bad_hex_encoding: fprintf(stderr, "bad input encoding\n"); had_errors++; return NULL; } d0 = hex_digit(*v++); if (d1 < 0 || d0 < 0) goto bad_hex_encoding; *d++ = ((d1 << 4) | d0); } *size = d - decoded; } else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) { const char *v = value+2, *end = value + *size; int d0, d1, d2, d3; char *d; if (high_water_alloc((void **)&decoded, &decoded_size, *size / 4 * 3)) { fprintf(stderr, "%s: %s\n", progname, strerror_ea(errno)); had_errors++; return NULL; } d = decoded; for(;;) { while (v < end && isspace(*v)) v++; if (v == end) { d0 = d1 = d2 = d3 = -2; break; } if (v + 4 > end) { bad_base64_encoding: fprintf(stderr, "bad input encoding\n"); had_errors++; return NULL; } d0 = base64_digit(*v++); d1 = base64_digit(*v++); d2 = base64_digit(*v++); d3 = base64_digit(*v++); if (d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0) break; *d++ = (char)((d0 << 2) | (d1 >> 4)); *d++ = (char)((d1 << 4) | (d2 >> 2)); *d++ = (char)((d2 << 6) | d3); } if (d0 == -2) { if (d1 != -2 || d2 != -2 || d3 != -2) goto bad_base64_encoding; goto base64_end; } if (d0 == -1 || d1 < 0 || d2 == -1 || d3 == -1) goto bad_base64_encoding; *d++ = (char)((d0 << 2) | (d1 >> 4)); if (d2 != -2) *d++ = (char)((d1 << 4) | (d2 >> 2)); else { if (d1 & 0x0F || d3 != -2) goto bad_base64_encoding; goto base64_end; } if (d3 != -2) *d++ = (char)((d2 << 6) | d3); else if (d2 & 0x03) goto bad_base64_encoding; base64_end: while (v < end && isspace(*v)) v++; if (v + 4 <= end && *v == '=') { if (*++v != '=' || *++v != '=' || *++v != '=') goto bad_base64_encoding; v++; } while (v < end && isspace(*v)) v++; if (v < end) goto bad_base64_encoding; *size = d - decoded; } else { const char *v = value, *end = value + *size; char *d; if (end > v+1 && *v == '"' && *(end-1) == '"') { v++; end--; } if (high_water_alloc((void **)&decoded, &decoded_size, *size)) { fprintf(stderr, "%s: %s\n", progname, strerror_ea(errno)); had_errors++; return NULL; } d = decoded; while (v < end) { if (v[0] == '\\') { if (v[1] == '\\' || v[1] == '"') { *d++ = *++v; v++; } else if (v[1] >= '0' && v[1] <= '7') { int c = 0; v++; c = (*v++ - '0'); if (*v >= '0' && *v <= '7') c = (c << 3) + (*v++ - '0'); if (*v >= '0' && *v <= '7') c = (c << 3) + (*v++ - '0'); *d++ = c; } else *d++ = *v++; } else *d++ = *v++; } *size = d - decoded; } return decoded; } int hex_digit(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'A' && c <= 'F') return c - 'A' + 10; else if (c >= 'a' && c <= 'f') return c - 'a' + 10; else return -1; } int base64_digit(char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; else if (c >= 'a' && c <= 'z') return 26 + c - 'a'; else if (c >= '0' && c <= '9') return 52 + c - '0'; else if (c == '+') return 62; else if (c == '/') return 63; else if (c == '=') return -2; else return -1; }