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