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
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
54static enum { EADUNNO, EAGET, EASET, EARM, EALS } what = EADUNNO;
55
56static void __dead2
57usage(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
87static void
88mkbuf(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}
100
101int
102main(int argc, char *argv[])
103{
104#define STDIN_BUF_SZ 4096
105	char	 stdin_data[STDIN_BUF_SZ];
106	char	*p;
107
108	const char *options, *attrname;
109	size_t	len;
110	ssize_t	ret;
111	int	 ch, error, i, arg_counter, attrnamespace, minargc;
112
113	char   *visbuf = NULL;
114	int	visbuflen = 0;
115	char   *buf = NULL;
116	int	buflen = 0;
117	struct	sbuf *attrvalue = NULL;
118	int	flag_force = 0;
119	int	flag_nofollow = 0;
120	int	flag_null = 0;
121	int	count_quiet = 0;
122	int	flag_from_stdin = 0;
123	int	flag_string = 0;
124	int	flag_hex = 0;
125
126	p = basename(argv[0]);
127	if (p == NULL)
128		p = argv[0];
129	if (!strcmp(p, "getextattr")) {
130		what = EAGET;
131		options = "fhqsx";
132		minargc = 3;
133	} else if (!strcmp(p, "setextattr")) {
134		what = EASET;
135		options = "fhinq";
136		minargc = 3;
137	} else if (!strcmp(p, "rmextattr")) {
138		what = EARM;
139		options = "fhq";
140		minargc = 3;
141	} else if (!strcmp(p, "lsextattr")) {
142		what = EALS;
143		options = "fhq";
144		minargc = 2;
145	} else {
146		usage();
147	}
148
149	while ((ch = getopt(argc, argv, options)) != -1) {
150		switch (ch) {
151		case 'f':
152			flag_force = 1;
153			break;
154		case 'h':
155			flag_nofollow = 1;
156			break;
157		case 'i':
158			flag_from_stdin = 1;
159			break;
160		case 'n':
161			flag_null = 1;
162			break;
163		case 'q':
164			count_quiet += 1;
165			break;
166		case 's':
167			flag_string = 1;
168			break;
169		case 'x':
170			flag_hex = 1;
171			break;
172		case '?':
173		default:
174			usage();
175		}
176	}
177
178	argc -= optind;
179	argv += optind;
180
181	if (what == EASET && flag_from_stdin == 0)
182		minargc++;
183
184	if (argc < minargc)
185		usage();
186
187	error = extattr_string_to_namespace(argv[0], &attrnamespace);
188	if (error)
189		err(-1, "%s", argv[0]);
190	argc--; argv++;
191
192	if (what != EALS) {
193		attrname = argv[0];
194		argc--; argv++;
195	} else
196		attrname = NULL;
197
198	if (what == EASET) {
199		attrvalue = sbuf_new_auto();
200		if (flag_from_stdin) {
201			while ((error = read(0, stdin_data, STDIN_BUF_SZ)) > 0)
202				sbuf_bcat(attrvalue, stdin_data, error);
203		} else {
204			sbuf_cpy(attrvalue, argv[0]);
205			argc--; argv++;
206		}
207		sbuf_finish(attrvalue);
208	}
209
210	for (arg_counter = 0; arg_counter < argc; arg_counter++) {
211		switch (what) {
212		case EARM:
213			if (flag_nofollow)
214				error = extattr_delete_link(argv[arg_counter],
215				    attrnamespace, attrname);
216			else
217				error = extattr_delete_file(argv[arg_counter],
218				    attrnamespace, attrname);
219			if (error >= 0)
220				continue;
221			break;
222		case EASET:
223			len = sbuf_len(attrvalue) + flag_null;
224			if (flag_nofollow)
225				ret = extattr_set_link(argv[arg_counter],
226				    attrnamespace, attrname,
227				    sbuf_data(attrvalue), len);
228			else
229				ret = extattr_set_file(argv[arg_counter],
230				    attrnamespace, attrname,
231				    sbuf_data(attrvalue), len);
232			if (ret >= 0) {
233				if ((size_t)ret != len && !count_quiet) {
234					warnx("Set %zd bytes of %zu for %s",
235					    ret, len, attrname);
236				}
237				continue;
238			}
239			break;
240		case EALS:
241			if (flag_nofollow)
242				ret = extattr_list_link(argv[arg_counter],
243				    attrnamespace, NULL, 0);
244			else
245				ret = extattr_list_file(argv[arg_counter],
246				    attrnamespace, NULL, 0);
247			if (ret < 0)
248				break;
249			mkbuf(&buf, &buflen, ret);
250			if (flag_nofollow)
251				ret = extattr_list_link(argv[arg_counter],
252				    attrnamespace, buf, buflen);
253			else
254				ret = extattr_list_file(argv[arg_counter],
255				    attrnamespace, buf, buflen);
256			if (ret < 0)
257				break;
258			if (!count_quiet)
259				printf("%s\t", argv[arg_counter]);
260			for (i = 0; i < ret; i += ch + 1) {
261			    /* The attribute name length is unsigned. */
262			    ch = (unsigned char)buf[i];
263			    printf("%s%*.*s", i ? "\t" : "",
264				ch, ch, buf + i + 1);
265			}
266			if (!count_quiet || ret > 0)
267				printf("\n");
268			continue;
269		case EAGET:
270			if (flag_nofollow)
271				ret = extattr_get_link(argv[arg_counter],
272				    attrnamespace, attrname, NULL, 0);
273			else
274				ret = extattr_get_file(argv[arg_counter],
275				    attrnamespace, attrname, NULL, 0);
276			if (ret < 0)
277				break;
278			mkbuf(&buf, &buflen, ret);
279			if (flag_nofollow)
280				ret = extattr_get_link(argv[arg_counter],
281				    attrnamespace, attrname, buf, buflen);
282			else
283				ret = extattr_get_file(argv[arg_counter],
284				    attrnamespace, attrname, buf, buflen);
285			if (ret < 0)
286				break;
287			if (!count_quiet)
288				printf("%s\t", argv[arg_counter]);
289			if (flag_string) {
290				mkbuf(&visbuf, &visbuflen, ret * 4 + 1);
291				strvisx(visbuf, buf, ret,
292				    VIS_SAFE | VIS_WHITE);
293				printf("\"%s\"", visbuf);
294			} else if (flag_hex) {
295				for (i = 0; i < ret; i++)
296					printf("%s%02x", i ? " " : "",
297							(unsigned char)buf[i]);
298			} else {
299				fwrite(buf, ret, 1, stdout);
300			}
301			if (count_quiet < 2)
302				printf("\n");
303			continue;
304		default:
305			break;
306		}
307		if (!count_quiet)
308			warn("%s: failed", argv[arg_counter]);
309		if (flag_force)
310			continue;
311		return(1);
312	}
313	return (0);
314}
315