1 /*- 2 * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. 3 * Copyright (c) 2002 Poul-Henning Kamp. 4 * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson 5 * All rights reserved. 6 * 7 * This software was developed for the FreeBSD Project by Poul-Henning 8 * Kamp and Network Associates Laboratories, the Security Research Division 9 * of Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. The names of the authors may not be used to endorse or promote 21 * products derived from this software without specific prior written 22 * permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $FreeBSD$ 37 */ 38 39 #include <sys/types.h> 40 #include <sys/sbuf.h> 41 #include <sys/uio.h> 42 #include <sys/extattr.h> 43 44 #include <libgen.h> 45 #include <libutil.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <vis.h> 51 #include <err.h> 52 #include <errno.h> 53 54 static enum { EADUNNO, EAGET, EASET, EARM, EALS } what = EADUNNO; 55 56 static void __dead2 57 usage(void) 58 { 59 60 switch (what) { 61 case EAGET: 62 fprintf(stderr, "usage: getextattr [-fhqsx] attrnamespace"); 63 fprintf(stderr, " attrname filename ...\n"); 64 exit(-1); 65 case EASET: 66 fprintf(stderr, "usage: setextattr [-fhnq] attrnamespace"); 67 fprintf(stderr, " attrname attrvalue filename ...\n"); 68 fprintf(stderr, " or setextattr -i [-fhnq] attrnamespace"); 69 fprintf(stderr, " attrname filename ...\n"); 70 exit(-1); 71 case EARM: 72 fprintf(stderr, "usage: rmextattr [-fhq] attrnamespace"); 73 fprintf(stderr, " attrname filename ...\n"); 74 exit(-1); 75 case EALS: 76 fprintf(stderr, "usage: lsextattr [-fhq] attrnamespace"); 77 fprintf(stderr, " filename ...\n"); 78 exit(-1); 79 case EADUNNO: 80 default: 81 fprintf(stderr, "usage: (getextattr|lsextattr|rmextattr"); 82 fprintf(stderr, "|setextattr)\n"); 83 exit (-1); 84 } 85 } 86 87 static void 88 mkbuf(char **buf, int *oldlen, int newlen) 89 { 90 91 if (*oldlen >= newlen) 92 return; 93 if (*buf != NULL) 94 free(*buf); 95 *buf = malloc(newlen); 96 if (*buf == NULL) 97 err(1, "malloc"); 98 *oldlen = newlen; 99 return; 100 } 101 102 int 103 main(int argc, char *argv[]) 104 { 105 #define STDIN_BUF_SZ 1024 106 char stdin_data[STDIN_BUF_SZ]; 107 char *p; 108 109 const char *options, *attrname; 110 size_t len; 111 ssize_t ret; 112 int ch, error, i, arg_counter, attrnamespace, minargc; 113 114 char *visbuf = NULL; 115 int visbuflen = 0; 116 char *buf = NULL; 117 int buflen = 0; 118 struct sbuf *attrvalue = NULL; 119 int flag_force = 0; 120 int flag_nofollow = 0; 121 int flag_null = 0; 122 int count_quiet = 0; 123 int flag_from_stdin = 0; 124 int flag_string = 0; 125 int flag_hex = 0; 126 127 p = basename(argv[0]); 128 if (p == NULL) 129 p = argv[0]; 130 if (!strcmp(p, "getextattr")) { 131 what = EAGET; 132 options = "fhqsx"; 133 minargc = 3; 134 } else if (!strcmp(p, "setextattr")) { 135 what = EASET; 136 options = "fhinq"; 137 minargc = 3; 138 } else if (!strcmp(p, "rmextattr")) { 139 what = EARM; 140 options = "fhq"; 141 minargc = 3; 142 } else if (!strcmp(p, "lsextattr")) { 143 what = EALS; 144 options = "fhq"; 145 minargc = 2; 146 } else { 147 usage(); 148 } 149 150 while ((ch = getopt(argc, argv, options)) != -1) { 151 switch (ch) { 152 case 'f': 153 flag_force = 1; 154 break; 155 case 'h': 156 flag_nofollow = 1; 157 break; 158 case 'i': 159 flag_from_stdin = 1; 160 break; 161 case 'n': 162 flag_null = 1; 163 break; 164 case 'q': 165 count_quiet += 1; 166 break; 167 case 's': 168 flag_string = 1; 169 break; 170 case 'x': 171 flag_hex = 1; 172 break; 173 case '?': 174 default: 175 usage(); 176 } 177 } 178 179 argc -= optind; 180 argv += optind; 181 182 if (what == EASET && flag_from_stdin == 0) 183 minargc++; 184 185 if (argc < minargc) 186 usage(); 187 188 error = extattr_string_to_namespace(argv[0], &attrnamespace); 189 if (error) 190 err(-1, "%s", argv[0]); 191 argc--; argv++; 192 193 if (what != EALS) { 194 attrname = argv[0]; 195 argc--; argv++; 196 } else 197 attrname = NULL; 198 199 if (what == EASET) { 200 attrvalue = sbuf_new_auto(); 201 if (flag_from_stdin) { 202 while ((error = read(0, stdin_data, STDIN_BUF_SZ)) > 0) 203 sbuf_bcat(attrvalue, stdin_data, error); 204 } else { 205 sbuf_cpy(attrvalue, argv[0]); 206 argc--; argv++; 207 } 208 sbuf_finish(attrvalue); 209 } 210 211 for (arg_counter = 0; arg_counter < argc; arg_counter++) { 212 switch (what) { 213 case EARM: 214 if (flag_nofollow) 215 error = extattr_delete_link(argv[arg_counter], 216 attrnamespace, attrname); 217 else 218 error = extattr_delete_file(argv[arg_counter], 219 attrnamespace, attrname); 220 if (error >= 0) 221 continue; 222 break; 223 case EASET: 224 len = sbuf_len(attrvalue) + flag_null; 225 if (flag_nofollow) 226 ret = extattr_set_link(argv[arg_counter], 227 attrnamespace, attrname, 228 sbuf_data(attrvalue), len); 229 else 230 ret = extattr_set_file(argv[arg_counter], 231 attrnamespace, attrname, 232 sbuf_data(attrvalue), len); 233 if (ret >= 0) { 234 if ((size_t)ret != len && !count_quiet) { 235 warnx("Set %zd bytes of %zu for %s", 236 ret, len, attrname); 237 } 238 continue; 239 } 240 break; 241 case EALS: 242 if (flag_nofollow) 243 ret = extattr_list_link(argv[arg_counter], 244 attrnamespace, NULL, 0); 245 else 246 ret = extattr_list_file(argv[arg_counter], 247 attrnamespace, NULL, 0); 248 if (ret < 0) 249 break; 250 mkbuf(&buf, &buflen, ret); 251 if (flag_nofollow) 252 ret = extattr_list_link(argv[arg_counter], 253 attrnamespace, buf, buflen); 254 else 255 ret = extattr_list_file(argv[arg_counter], 256 attrnamespace, buf, buflen); 257 if (ret < 0) 258 break; 259 if (!count_quiet) 260 printf("%s\t", argv[arg_counter]); 261 for (i = 0; i < ret; i += ch + 1) { 262 /* The attribute name length is unsigned. */ 263 ch = (unsigned char)buf[i]; 264 printf("%s%*.*s", i ? "\t" : "", 265 ch, ch, buf + i + 1); 266 } 267 if (!count_quiet || ret > 0) 268 printf("\n"); 269 continue; 270 case EAGET: 271 if (flag_nofollow) 272 ret = extattr_get_link(argv[arg_counter], 273 attrnamespace, attrname, NULL, 0); 274 else 275 ret = extattr_get_file(argv[arg_counter], 276 attrnamespace, attrname, NULL, 0); 277 if (ret < 0) 278 break; 279 mkbuf(&buf, &buflen, ret); 280 if (flag_nofollow) 281 ret = extattr_get_link(argv[arg_counter], 282 attrnamespace, attrname, buf, buflen); 283 else 284 ret = extattr_get_file(argv[arg_counter], 285 attrnamespace, attrname, buf, buflen); 286 if (ret < 0) 287 break; 288 if (!count_quiet) 289 printf("%s\t", argv[arg_counter]); 290 if (flag_string) { 291 mkbuf(&visbuf, &visbuflen, ret * 4 + 1); 292 strvisx(visbuf, buf, ret, 293 VIS_SAFE | VIS_WHITE); 294 printf("\"%s\"", visbuf); 295 } else if (flag_hex) { 296 for (i = 0; i < ret; i++) 297 printf("%s%02x", i ? " " : "", 298 (unsigned char)buf[i]); 299 } else { 300 fwrite(buf, ret, 1, stdout); 301 } 302 if (count_quiet < 2) 303 printf("\n"); 304 continue; 305 default: 306 break; 307 } 308 if (!count_quiet) 309 warn("%s: failed", argv[arg_counter]); 310 if (flag_force) 311 continue; 312 return(1); 313 } 314 return (0); 315 } 316