xref: /openbsd-src/regress/sys/ffs/fstest.c (revision 49a6e16f2c2c8e509184b1f777366d1a6f337e1c)
1*49a6e16fSderaadt /*	$OpenBSD: fstest.c,v 1.7 2021/12/13 16:56:49 deraadt Exp $	*/
2ec7c50bfSpedro 
3ec7c50bfSpedro /*
4ec7c50bfSpedro  * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5ec7c50bfSpedro  * All rights reserved.
6ec7c50bfSpedro  *
7ec7c50bfSpedro  * Redistribution and use in source and binary forms, with or without
8ec7c50bfSpedro  * modification, are permitted provided that the following conditions
9ec7c50bfSpedro  * are met:
10ec7c50bfSpedro  * 1. Redistributions of source code must retain the above copyright
11ec7c50bfSpedro  *    notice, this list of conditions and the following disclaimer.
12ec7c50bfSpedro  * 2. Redistributions in binary form must reproduce the above copyright
13ec7c50bfSpedro  *    notice, this list of conditions and the following disclaimer in the
14ec7c50bfSpedro  *    documentation and/or other materials provided with the distribution.
15ec7c50bfSpedro  *
16ec7c50bfSpedro  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17ec7c50bfSpedro  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18ec7c50bfSpedro  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ec7c50bfSpedro  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20ec7c50bfSpedro  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21ec7c50bfSpedro  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22ec7c50bfSpedro  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23ec7c50bfSpedro  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24ec7c50bfSpedro  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25ec7c50bfSpedro  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26ec7c50bfSpedro  * SUCH DAMAGE.
27ec7c50bfSpedro  *
28ec7c50bfSpedro  * $FreeBSD: src/tools/regression/fstest/fstest.c,v 1.1 2007/01/17 01:42:07 pjd Exp $
29ec7c50bfSpedro  */
30ec7c50bfSpedro 
31*49a6e16fSderaadt #include <sys/types.h>
32ec7c50bfSpedro #include <sys/stat.h>
3337dd51d1Sthib #include <sys/sysctl.h>
3437dd51d1Sthib 
35ec7c50bfSpedro #include <stdio.h>
36ec7c50bfSpedro #include <stdlib.h>
37ec7c50bfSpedro #include <unistd.h>
38ec7c50bfSpedro #include <fcntl.h>
39ec7c50bfSpedro #include <grp.h>
40ec7c50bfSpedro #include <string.h>
41ec7c50bfSpedro #include <ctype.h>
42ec7c50bfSpedro #include <errno.h>
43ec7c50bfSpedro #include <assert.h>
44ec7c50bfSpedro 
45ec7c50bfSpedro enum action {
46ec7c50bfSpedro 	ACTION_OPEN,
47ec7c50bfSpedro 	ACTION_CREATE,
48ec7c50bfSpedro 	ACTION_UNLINK,
49ec7c50bfSpedro 	ACTION_MKDIR,
50ec7c50bfSpedro 	ACTION_RMDIR,
51ec7c50bfSpedro 	ACTION_LINK,
52ec7c50bfSpedro 	ACTION_SYMLINK,
53ec7c50bfSpedro 	ACTION_RENAME,
54ec7c50bfSpedro 	ACTION_MKFIFO,
55ec7c50bfSpedro 	ACTION_CHMOD,
56ec7c50bfSpedro 	ACTION_CHOWN,
57ec7c50bfSpedro 	ACTION_LCHOWN,
58ec7c50bfSpedro 	ACTION_CHFLAGS,
590350e6d8Sbluhm 	ACTION_LCHFLAGS,
60ec7c50bfSpedro 	ACTION_TRUNCATE,
61ec7c50bfSpedro 	ACTION_STAT,
62ec7c50bfSpedro 	ACTION_LSTAT,
63ec7c50bfSpedro };
64ec7c50bfSpedro 
65ec7c50bfSpedro #define	TYPE_NONE	0x0000
66ec7c50bfSpedro #define	TYPE_STRING	0x0001
67ec7c50bfSpedro #define	TYPE_NUMBER	0x0002
68ec7c50bfSpedro #define	TYPE_OPTIONAL	0x0100
69ec7c50bfSpedro #define	MAX_ARGS	8
70ec7c50bfSpedro 
71ec7c50bfSpedro struct syscall_desc {
72ec7c50bfSpedro 	char *sd_name;
73ec7c50bfSpedro 	enum action  sd_action;
74ec7c50bfSpedro 	int sd_args[MAX_ARGS];
75ec7c50bfSpedro };
76ec7c50bfSpedro 
77ec7c50bfSpedro static struct syscall_desc syscalls[] = {
78ec7c50bfSpedro 	{ "open", ACTION_OPEN, { TYPE_STRING, TYPE_STRING,
79ec7c50bfSpedro 				 TYPE_NUMBER | TYPE_OPTIONAL, TYPE_NONE } },
80ec7c50bfSpedro 	{ "create", ACTION_CREATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
81ec7c50bfSpedro 	{ "unlink", ACTION_UNLINK, { TYPE_STRING, TYPE_NONE } },
82ec7c50bfSpedro 	{ "mkdir", ACTION_MKDIR, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
83ec7c50bfSpedro 	{ "rmdir", ACTION_RMDIR, { TYPE_STRING, TYPE_NONE } },
84ec7c50bfSpedro 	{ "link", ACTION_LINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
85ec7c50bfSpedro 	{ "symlink", ACTION_SYMLINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
86ec7c50bfSpedro 	{ "rename", ACTION_RENAME, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
87ec7c50bfSpedro 	{ "mkfifo", ACTION_MKFIFO, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
88ec7c50bfSpedro 	{ "chmod", ACTION_CHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
89ec7c50bfSpedro 	{ "chown", ACTION_CHOWN, { TYPE_STRING, TYPE_NUMBER,
90ec7c50bfSpedro 				   TYPE_NUMBER, TYPE_NONE } },
91ec7c50bfSpedro 	{ "lchown", ACTION_LCHOWN, { TYPE_STRING, TYPE_NUMBER,
92ec7c50bfSpedro 				     TYPE_NUMBER, TYPE_NONE } },
93ec7c50bfSpedro 	{ "chflags", ACTION_CHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
940350e6d8Sbluhm 	{ "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING,
950350e6d8Sbluhm 					 TYPE_NONE } },
96ec7c50bfSpedro 	{ "truncate", ACTION_TRUNCATE, { TYPE_STRING, TYPE_NUMBER,
97ec7c50bfSpedro 					 TYPE_NONE } },
98ec7c50bfSpedro 	{ "stat", ACTION_STAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
99ec7c50bfSpedro 	{ "lstat", ACTION_LSTAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
100ec7c50bfSpedro 	{ NULL, -1, { TYPE_NONE } }
101ec7c50bfSpedro };
102ec7c50bfSpedro 
103ec7c50bfSpedro struct flag {
104ec7c50bfSpedro 	long long f_flag;
105ec7c50bfSpedro 	char *f_str;
106ec7c50bfSpedro };
107ec7c50bfSpedro 
108ec7c50bfSpedro static struct flag open_flags[] = {
109ec7c50bfSpedro 	{ O_RDONLY, "O_RDONLY" },
110ec7c50bfSpedro 	{ O_WRONLY, "O_WRONLY" },
111ec7c50bfSpedro 	{ O_RDWR, "O_RDWR" },
112ec7c50bfSpedro 	{ O_NONBLOCK, "O_NONBLOCK" },
113ec7c50bfSpedro 	{ O_APPEND, "O_APPEND" },
114ec7c50bfSpedro 	{ O_CREAT, "O_CREAT" },
115ec7c50bfSpedro 	{ O_TRUNC, "O_TRUNC" },
116ec7c50bfSpedro 	{ O_EXCL, "O_EXCL" },
117ec7c50bfSpedro 	{ O_SHLOCK, "O_SHLOCK" },
118ec7c50bfSpedro 	{ O_EXLOCK, "O_EXLOCK" },
119ec7c50bfSpedro 	{ O_FSYNC, "O_FSYNC" },
120ec7c50bfSpedro 	{ O_SYNC, "O_SYNC" },
121ec7c50bfSpedro 	{ O_NOFOLLOW, "O_NOFOLLOW" },
122ec7c50bfSpedro 	{ O_NOCTTY, "O_NOCTTY" },
123ec7c50bfSpedro 	{ 0, NULL }
124ec7c50bfSpedro };
125ec7c50bfSpedro 
126ec7c50bfSpedro static struct flag chflags_flags[] = {
127ec7c50bfSpedro 	{ UF_NODUMP, "UF_NODUMP" },
128ec7c50bfSpedro 	{ UF_IMMUTABLE, "UF_IMMUTABLE" },
129ec7c50bfSpedro 	{ UF_APPEND, "UF_APPEND" },
130ec7c50bfSpedro 	{ UF_OPAQUE, "UF_OPAQUE" },
131ec7c50bfSpedro 	{ SF_ARCHIVED, "SF_ARCHIVED" },
132ec7c50bfSpedro 	{ SF_IMMUTABLE, "SF_IMMUTABLE" },
133ec7c50bfSpedro 	{ SF_APPEND, "SF_APPEND" },
134ec7c50bfSpedro 	{ 0, NULL }
135ec7c50bfSpedro };
136ec7c50bfSpedro 
137ec7c50bfSpedro static const char *err2str(int error);
13837dd51d1Sthib int use_appimm;		/* use the SF_APPEND and SF_IMMUTABLE chflags */
139ec7c50bfSpedro 
140ec7c50bfSpedro __dead static void
usage(void)141ec7c50bfSpedro usage(void)
142ec7c50bfSpedro {
143ec7c50bfSpedro 	fprintf(stderr, "usage: fstest [-u uid] [-g gid1[,gid2[...]]] syscall "
144ec7c50bfSpedro 	    "args ...\n");
145ec7c50bfSpedro 	exit(1);
146ec7c50bfSpedro }
147ec7c50bfSpedro 
148ec7c50bfSpedro static long long
str2flags(struct flag * tflags,char * sflags)149ec7c50bfSpedro str2flags(struct flag *tflags, char *sflags)
150ec7c50bfSpedro {
151ec7c50bfSpedro 	long long flags = 0;
152ec7c50bfSpedro 	unsigned int i;
153ec7c50bfSpedro 	char *f;
154ec7c50bfSpedro 
155ec7c50bfSpedro 	for (f = strtok(sflags, ","); f != NULL; f = strtok(NULL, ",")) {
156ec7c50bfSpedro 		/* Support magic 'none' flag which just reset all flags. */
157ec7c50bfSpedro 		if (strcmp(f, "none") == 0)
158ec7c50bfSpedro 			return (0);
159ec7c50bfSpedro 		for (i = 0; tflags[i].f_str != NULL; i++) {
160ec7c50bfSpedro 			if (strcmp(tflags[i].f_str, f) == 0)
161ec7c50bfSpedro 				break;
162ec7c50bfSpedro 		}
163ec7c50bfSpedro 		if (tflags[i].f_str == NULL) {
164ec7c50bfSpedro 			fprintf(stderr, "unknown flag '%s'\n", f);
165ec7c50bfSpedro 			exit(1);
166ec7c50bfSpedro 		}
167ec7c50bfSpedro 		flags |= tflags[i].f_flag;
168ec7c50bfSpedro 	}
169ec7c50bfSpedro 	return (flags);
170ec7c50bfSpedro }
171ec7c50bfSpedro 
172ec7c50bfSpedro static char *
flags2str(struct flag * tflags,long long flags)173ec7c50bfSpedro flags2str(struct flag *tflags, long long flags)
174ec7c50bfSpedro {
175ec7c50bfSpedro 	static char sflags[1024];
176ec7c50bfSpedro 	unsigned int i;
177ec7c50bfSpedro 
178ec7c50bfSpedro 	sflags[0] = '\0';
179ec7c50bfSpedro 	for (i = 0; tflags[i].f_str != NULL; i++) {
180ec7c50bfSpedro 		if (flags & tflags[i].f_flag) {
181ec7c50bfSpedro 			if (sflags[0] != '\0')
182ec7c50bfSpedro 				strlcat(sflags, ",", sizeof(sflags));
183ec7c50bfSpedro 			strlcat(sflags, tflags[i].f_str, sizeof(sflags));
184ec7c50bfSpedro 		}
185ec7c50bfSpedro 	}
186ec7c50bfSpedro 	if (sflags[0] == '\0')
187ec7c50bfSpedro 		strlcpy(sflags, "none", sizeof(sflags));
188ec7c50bfSpedro 	return (sflags);
189ec7c50bfSpedro }
190ec7c50bfSpedro 
191ec7c50bfSpedro static struct syscall_desc *
find_syscall(const char * name)192ec7c50bfSpedro find_syscall(const char *name)
193ec7c50bfSpedro {
194ec7c50bfSpedro 	int i;
195ec7c50bfSpedro 
196ec7c50bfSpedro 	for (i = 0; syscalls[i].sd_name != NULL; i++) {
197ec7c50bfSpedro 		if (strcmp(syscalls[i].sd_name, name) == 0)
198ec7c50bfSpedro 			return (&syscalls[i]);
199ec7c50bfSpedro 	}
200ec7c50bfSpedro 	return (NULL);
201ec7c50bfSpedro }
202ec7c50bfSpedro 
203ec7c50bfSpedro static void
show_stat(struct stat * sp,const char * what)204ec7c50bfSpedro show_stat(struct stat *sp, const char *what)
205ec7c50bfSpedro {
206ec7c50bfSpedro 
207ec7c50bfSpedro 	if (strcmp(what, "mode") == 0)
208ec7c50bfSpedro 		printf("0%o", (unsigned int)(sp->st_mode & ALLPERMS));
209ec7c50bfSpedro 	else if (strcmp(what, "inode") == 0)
210404bddbdSguenther 		printf("%llu", (unsigned long long)sp->st_ino);
211ec7c50bfSpedro 	else if (strcmp(what, "nlink") == 0)
212ec7c50bfSpedro 		printf("%lld", (long long)sp->st_nlink);
213ec7c50bfSpedro 	else if (strcmp(what, "uid") == 0)
214ec7c50bfSpedro 		printf("%d", (int)sp->st_uid);
215ec7c50bfSpedro 	else if (strcmp(what, "gid") == 0)
216ec7c50bfSpedro 		printf("%d", (int)sp->st_gid);
217ec7c50bfSpedro 	else if (strcmp(what, "size") == 0)
218ec7c50bfSpedro 		printf("%lld", (long long)sp->st_size);
219ec7c50bfSpedro 	else if (strcmp(what, "blocks") == 0)
220ec7c50bfSpedro 		printf("%lld", (long long)sp->st_blocks);
221ec7c50bfSpedro 	else if (strcmp(what, "atime") == 0)
222ec7c50bfSpedro 		printf("%lld", (long long)sp->st_atime);
223ec7c50bfSpedro 	else if (strcmp(what, "mtime") == 0)
224ec7c50bfSpedro 		printf("%lld", (long long)sp->st_mtime);
225ec7c50bfSpedro 	else if (strcmp(what, "ctime") == 0)
226ec7c50bfSpedro 		printf("%lld", (long long)sp->st_ctime);
227ec7c50bfSpedro 	else if (strcmp(what, "flags") == 0)
228ec7c50bfSpedro 		printf("%s", flags2str(chflags_flags, sp->st_flags));
229ec7c50bfSpedro 	else if (strcmp(what, "type") == 0) {
230ec7c50bfSpedro 		switch (sp->st_mode & S_IFMT) {
231ec7c50bfSpedro 		case S_IFIFO:
232ec7c50bfSpedro 			printf("fifo");
233ec7c50bfSpedro 			break;
234ec7c50bfSpedro 		case S_IFCHR:
235ec7c50bfSpedro 			printf("char");
236ec7c50bfSpedro 			break;
237ec7c50bfSpedro 		case S_IFDIR:
238ec7c50bfSpedro 			printf("dir");
239ec7c50bfSpedro 			break;
240ec7c50bfSpedro 		case S_IFBLK:
241ec7c50bfSpedro 			printf("block");
242ec7c50bfSpedro 			break;
243ec7c50bfSpedro 		case S_IFREG:
244ec7c50bfSpedro 			printf("regular");
245ec7c50bfSpedro 			break;
246ec7c50bfSpedro 		case S_IFLNK:
247ec7c50bfSpedro 			printf("symlink");
248ec7c50bfSpedro 			break;
249ec7c50bfSpedro 		case S_IFSOCK:
250ec7c50bfSpedro 			printf("socket");
251ec7c50bfSpedro 			break;
252ec7c50bfSpedro 		default:
253ec7c50bfSpedro 			printf("unknown");
254ec7c50bfSpedro 			break;
255ec7c50bfSpedro 		}
256ec7c50bfSpedro 	} else {
257ec7c50bfSpedro 		printf("unknown");
258ec7c50bfSpedro 	}
259ec7c50bfSpedro }
260ec7c50bfSpedro 
261ec7c50bfSpedro static void
show_stats(struct stat * sp,char * what)262ec7c50bfSpedro show_stats(struct stat *sp, char *what)
263ec7c50bfSpedro {
264ec7c50bfSpedro 	const char *s = "";
265ec7c50bfSpedro 	char *w;
266ec7c50bfSpedro 
267ec7c50bfSpedro 	for (w = strtok(what, ","); w != NULL; w = strtok(NULL, ",")) {
268ec7c50bfSpedro 		printf("%s", s);
269ec7c50bfSpedro 		show_stat(sp, w);
270ec7c50bfSpedro 		s = ",";
271ec7c50bfSpedro 	}
272ec7c50bfSpedro 	printf("\n");
273ec7c50bfSpedro }
274ec7c50bfSpedro 
275ec7c50bfSpedro static unsigned int
call_syscall(struct syscall_desc * scall,char * argv[])276ec7c50bfSpedro call_syscall(struct syscall_desc *scall, char *argv[])
277ec7c50bfSpedro {
278ec7c50bfSpedro 	struct stat sb;
279ec7c50bfSpedro 	long long flags;
280ec7c50bfSpedro 	unsigned int i;
281ec7c50bfSpedro 	char *endp;
282ec7c50bfSpedro 	int rval;
283ec7c50bfSpedro 	union {
284ec7c50bfSpedro 		char *str;
285ec7c50bfSpedro 		long long num;
286ec7c50bfSpedro 	} args[MAX_ARGS];
28737dd51d1Sthib 	unsigned int ch_flags;
288ec7c50bfSpedro 
289ec7c50bfSpedro 	/*
290ec7c50bfSpedro 	 * Verify correctness of the arguments.
291ec7c50bfSpedro 	 */
292ec7c50bfSpedro 	for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) {
293ec7c50bfSpedro 		if (scall->sd_args[i] == TYPE_NONE) {
294ec7c50bfSpedro 			if (argv[i] == NULL || strcmp(argv[i], ":") == 0)
295ec7c50bfSpedro 				break;
296ec7c50bfSpedro 			fprintf(stderr, "too many arguments [%s]\n", argv[i]);
297ec7c50bfSpedro 			exit(1);
298ec7c50bfSpedro 		} else {
299ec7c50bfSpedro 			if (argv[i] == NULL || strcmp(argv[i], ":") == 0) {
300ec7c50bfSpedro 				if (scall->sd_args[i] & TYPE_OPTIONAL)
301ec7c50bfSpedro 					break;
302ec7c50bfSpedro 				fprintf(stderr, "too few arguments\n");
303ec7c50bfSpedro 				exit(1);
304ec7c50bfSpedro 			}
305ec7c50bfSpedro 			if (scall->sd_args[i] & TYPE_STRING) {
306ec7c50bfSpedro 				if (strcmp(argv[i], "NULL") == 0)
307ec7c50bfSpedro 					args[i].str = NULL;
308ec7c50bfSpedro 				else if (strcmp(argv[i], "DEADCODE") == 0)
309ec7c50bfSpedro 					args[i].str = (void *)0xdeadc0de;
310ec7c50bfSpedro 				else
311ec7c50bfSpedro 					args[i].str = argv[i];
312ec7c50bfSpedro 			} else if (scall->sd_args[i] & TYPE_NUMBER) {
313ec7c50bfSpedro 				args[i].num = strtoll(argv[i], &endp, 0);
314ec7c50bfSpedro 				if (*endp != '\0' &&
315ec7c50bfSpedro 				    !isspace((unsigned char)*endp)) {
316ec7c50bfSpedro 					fprintf(stderr, "invalid argument %u, "
317ec7c50bfSpedro 					    "number expected [%s]\n", i, endp);
318ec7c50bfSpedro 					exit(1);
319ec7c50bfSpedro 				}
320ec7c50bfSpedro 			}
321ec7c50bfSpedro 		}
322ec7c50bfSpedro 	}
323ec7c50bfSpedro 	/*
324ec7c50bfSpedro 	 * Call the given syscall.
325ec7c50bfSpedro 	 */
326ec7c50bfSpedro #define	NUM(n)	(args[(n)].num)
327ec7c50bfSpedro #define	STR(n)	(args[(n)].str)
328ec7c50bfSpedro 	switch (scall->sd_action) {
329ec7c50bfSpedro 	case ACTION_OPEN:
330ec7c50bfSpedro 		flags = str2flags(open_flags, STR(1));
331ec7c50bfSpedro 		if (flags & O_CREAT) {
332ec7c50bfSpedro 			if (i == 2) {
333ec7c50bfSpedro 				fprintf(stderr, "too few arguments\n");
334ec7c50bfSpedro 				exit(1);
335ec7c50bfSpedro 			}
336ec7c50bfSpedro 			rval = open(STR(0), flags, (mode_t)NUM(2));
337ec7c50bfSpedro 		} else {
338ec7c50bfSpedro 			if (i == 3) {
339ec7c50bfSpedro 				fprintf(stderr, "too many arguments\n");
340ec7c50bfSpedro 				exit(1);
341ec7c50bfSpedro 			}
342ec7c50bfSpedro 			rval = open(STR(0), flags);
343ec7c50bfSpedro 		}
344ec7c50bfSpedro 		break;
345ec7c50bfSpedro 	case ACTION_CREATE:
346ec7c50bfSpedro 		rval = open(STR(0), O_CREAT | O_EXCL, NUM(1));
347ec7c50bfSpedro 		if (rval >= 0)
348ec7c50bfSpedro 			close(rval);
349ec7c50bfSpedro 		break;
350ec7c50bfSpedro 	case ACTION_UNLINK:
351ec7c50bfSpedro 		rval = unlink(STR(0));
352ec7c50bfSpedro 		break;
353ec7c50bfSpedro 	case ACTION_MKDIR:
354ec7c50bfSpedro 		rval = mkdir(STR(0), NUM(1));
355ec7c50bfSpedro 		break;
356ec7c50bfSpedro 	case ACTION_RMDIR:
357ec7c50bfSpedro 		rval = rmdir(STR(0));
358ec7c50bfSpedro 		break;
359ec7c50bfSpedro 	case ACTION_LINK:
360ec7c50bfSpedro 		rval = link(STR(0), STR(1));
361ec7c50bfSpedro 		break;
362ec7c50bfSpedro 	case ACTION_SYMLINK:
363ec7c50bfSpedro 		rval = symlink(STR(0), STR(1));
364ec7c50bfSpedro 		break;
365ec7c50bfSpedro 	case ACTION_RENAME:
366ec7c50bfSpedro 		rval = rename(STR(0), STR(1));
367ec7c50bfSpedro 		break;
368ec7c50bfSpedro 	case ACTION_MKFIFO:
369ec7c50bfSpedro 		rval = mkfifo(STR(0), NUM(1));
370ec7c50bfSpedro 		break;
371ec7c50bfSpedro 	case ACTION_CHMOD:
372ec7c50bfSpedro 		rval = chmod(STR(0), NUM(1));
373ec7c50bfSpedro 		break;
374ec7c50bfSpedro 	case ACTION_CHOWN:
375ec7c50bfSpedro 		rval = chown(STR(0), NUM(1), NUM(2));
376ec7c50bfSpedro 		break;
377ec7c50bfSpedro 	case ACTION_LCHOWN:
378ec7c50bfSpedro 		rval = lchown(STR(0), NUM(1), NUM(2));
379ec7c50bfSpedro 		break;
380ec7c50bfSpedro 	case ACTION_CHFLAGS:
38137dd51d1Sthib 		ch_flags = str2flags(chflags_flags, STR(1));
38237dd51d1Sthib 		if (!use_appimm)
38337dd51d1Sthib 			ch_flags &= ~(SF_APPEND|SF_IMMUTABLE);
38437dd51d1Sthib 		rval = chflags(STR(0), ch_flags);
385ec7c50bfSpedro 		break;
3860350e6d8Sbluhm 	case ACTION_LCHFLAGS:
3870350e6d8Sbluhm 		ch_flags = str2flags(chflags_flags, STR(1));
3880350e6d8Sbluhm 		if (!use_appimm)
3890350e6d8Sbluhm 			ch_flags &= ~(SF_APPEND|SF_IMMUTABLE);
3900350e6d8Sbluhm 		rval = chflagsat(AT_FDCWD, STR(0), ch_flags,
3910350e6d8Sbluhm 		    AT_SYMLINK_NOFOLLOW);
3920350e6d8Sbluhm 		break;
393ec7c50bfSpedro 	case ACTION_TRUNCATE:
394ec7c50bfSpedro 		rval = truncate(STR(0), NUM(1));
395ec7c50bfSpedro 		break;
396ec7c50bfSpedro 	case ACTION_STAT:
397ec7c50bfSpedro 		rval = stat(STR(0), &sb);
398ec7c50bfSpedro 		if (rval == 0) {
399ec7c50bfSpedro 			show_stats(&sb, STR(1));
400ec7c50bfSpedro 			return (i);
401ec7c50bfSpedro 		}
402ec7c50bfSpedro 		break;
403ec7c50bfSpedro 	case ACTION_LSTAT:
404ec7c50bfSpedro 		rval = lstat(STR(0), &sb);
405ec7c50bfSpedro 		if (rval == 0) {
406ec7c50bfSpedro 			show_stats(&sb, STR(1));
407ec7c50bfSpedro 			return (i);
408ec7c50bfSpedro 		}
409ec7c50bfSpedro 		break;
410ec7c50bfSpedro 	default:
411ec7c50bfSpedro 		fprintf(stderr, "unsupported syscall\n");
412ec7c50bfSpedro 		exit(1);
413ec7c50bfSpedro 	}
414ec7c50bfSpedro #undef STR
415ec7c50bfSpedro #undef NUM
416ec7c50bfSpedro 	if (rval < 0) {
417d59dbe75Sbluhm 		printf("%s\n", err2str(errno));
418ec7c50bfSpedro 		exit(1);
419ec7c50bfSpedro 	}
420ec7c50bfSpedro 	printf("0\n");
421ec7c50bfSpedro 	return (i);
422ec7c50bfSpedro }
423ec7c50bfSpedro 
424ec7c50bfSpedro static void
set_gids(char * gids)425ec7c50bfSpedro set_gids(char *gids)
426ec7c50bfSpedro {
427ec7c50bfSpedro 	gid_t *gidset;
428ec7c50bfSpedro 	long ngroups;
429ec7c50bfSpedro 	char *g, *endp;
430ec7c50bfSpedro 	unsigned i;
431ec7c50bfSpedro 
432ec7c50bfSpedro 	ngroups = sysconf(_SC_NGROUPS_MAX);
433ec7c50bfSpedro 	assert(ngroups > 0);
43424b9810bSdoug 	gidset = reallocarray(NULL, ngroups, sizeof(*gidset));
435ec7c50bfSpedro 	assert(gidset != NULL);
436ec7c50bfSpedro 	for (i = 0, g = strtok(gids, ","); g != NULL;
437ec7c50bfSpedro 	    g = strtok(NULL, ","), i++) {
438ec7c50bfSpedro 		if (i >= ngroups) {
439ec7c50bfSpedro 			fprintf(stderr, "too many gids\n");
440ec7c50bfSpedro 			exit(1);
441ec7c50bfSpedro 		}
442ec7c50bfSpedro 		gidset[i] = strtol(g, &endp, 0);
443ec7c50bfSpedro 		if (*endp != '\0' && !isspace((unsigned char)*endp)) {
444ec7c50bfSpedro 			fprintf(stderr, "invalid gid '%s' - number expected\n",
445ec7c50bfSpedro 			    g);
446ec7c50bfSpedro 			exit(1);
447ec7c50bfSpedro 		}
448ec7c50bfSpedro 	}
449ec7c50bfSpedro 	if (setgroups(i, gidset) < 0) {
450ec7c50bfSpedro 		fprintf(stderr, "cannot change groups: %s\n", strerror(errno));
451ec7c50bfSpedro 		exit(1);
452ec7c50bfSpedro 	}
453ec7c50bfSpedro 	free(gidset);
454ec7c50bfSpedro }
455ec7c50bfSpedro 
456ec7c50bfSpedro int
main(int argc,char * argv[])457ec7c50bfSpedro main(int argc, char *argv[])
458ec7c50bfSpedro {
459ec7c50bfSpedro 	struct syscall_desc *scall;
460ec7c50bfSpedro 	unsigned int n;
461ec7c50bfSpedro 	char *gids, *endp;
462ec7c50bfSpedro 	int uid, umsk, ch;
46337dd51d1Sthib 	int mib[2];
46437dd51d1Sthib 	size_t len;
46537dd51d1Sthib 	int securelevel;
466ec7c50bfSpedro 
467ec7c50bfSpedro 	uid = -1;
468ec7c50bfSpedro 	gids = NULL;
469ec7c50bfSpedro 	umsk = 0;
470ec7c50bfSpedro 
471ec7c50bfSpedro 	while ((ch = getopt(argc, argv, "g:u:U:")) != -1) {
472ec7c50bfSpedro 		switch(ch) {
473ec7c50bfSpedro 		case 'g':
474ec7c50bfSpedro 			gids = optarg;
475ec7c50bfSpedro 			break;
476ec7c50bfSpedro 		case 'u':
477ec7c50bfSpedro 			uid = (int)strtol(optarg, &endp, 0);
478ec7c50bfSpedro 			if (*endp != '\0' && !isspace((unsigned char)*endp)) {
479ec7c50bfSpedro 				fprintf(stderr, "invalid uid '%s' - number "
480ec7c50bfSpedro 				    "expected\n", optarg);
481ec7c50bfSpedro 				exit(1);
482ec7c50bfSpedro 			}
483ec7c50bfSpedro 			break;
484ec7c50bfSpedro 		case 'U':
485ec7c50bfSpedro 			umsk = (int)strtol(optarg, &endp, 0);
486ec7c50bfSpedro 			if (*endp != '\0' && !isspace((unsigned char)*endp)) {
487ec7c50bfSpedro 				fprintf(stderr, "invalid umask '%s' - number "
488ec7c50bfSpedro 				    "expected\n", optarg);
489ec7c50bfSpedro 				exit(1);
490ec7c50bfSpedro 			}
491ec7c50bfSpedro 			break;
492ec7c50bfSpedro 		default:
493ec7c50bfSpedro 			usage();
494ec7c50bfSpedro 		}
495ec7c50bfSpedro 	}
496ec7c50bfSpedro 	argc -= optind;
497ec7c50bfSpedro 	argv += optind;
498ec7c50bfSpedro 
499ec7c50bfSpedro 	if (argc < 1) {
500ec7c50bfSpedro 		fprintf(stderr, "too few arguments\n");
501ec7c50bfSpedro 		usage();
502ec7c50bfSpedro 	}
503ec7c50bfSpedro 
504d59dbe75Sbluhm 	if (gids != NULL)
505ec7c50bfSpedro 		set_gids(gids);
506ec7c50bfSpedro 	if (uid != -1) {
507ec7c50bfSpedro 		if (setuid(uid) < 0) {
508ec7c50bfSpedro 			fprintf(stderr, "cannot change uid: %s\n",
509ec7c50bfSpedro 			    strerror(errno));
510ec7c50bfSpedro 			exit(1);
511ec7c50bfSpedro 		}
512ec7c50bfSpedro 	}
513ec7c50bfSpedro 
51437dd51d1Sthib 	/*
51537dd51d1Sthib 	 * Find out if we should use the SF_IMMUTABLE and SF_APPEND flags;
51637dd51d1Sthib 	 * Since we run by default on kern.securelevel=1 these cause false
51737dd51d1Sthib 	 * positives.
51837dd51d1Sthib 	 */
51937dd51d1Sthib 	mib[0] = CTL_KERN;
52037dd51d1Sthib 	mib[1] = KERN_SECURELVL;
52137dd51d1Sthib 	len = sizeof(securelevel);
52237dd51d1Sthib 	if (sysctl(mib, 2, &securelevel, &len, NULL, 0) == -1) {
52337dd51d1Sthib 		fprintf(stderr, "cannot get kernel securelevel\n");
52437dd51d1Sthib 		exit(1);
52537dd51d1Sthib 	}
52637dd51d1Sthib 	if (securelevel == 0 || securelevel == -1)
52737dd51d1Sthib 		use_appimm = 1;
52837dd51d1Sthib 	else
52937dd51d1Sthib 		use_appimm = 0;
53037dd51d1Sthib 
531ec7c50bfSpedro 	/* Change umask to requested value or to 0, if not requested. */
532ec7c50bfSpedro 	umask(umsk);
533ec7c50bfSpedro 
534ec7c50bfSpedro 	for (;;) {
535ec7c50bfSpedro 		scall = find_syscall(argv[0]);
536ec7c50bfSpedro 		if (scall == NULL) {
5370350e6d8Sbluhm 			fprintf(stderr, "syscall '%s' not supported\n",
5380350e6d8Sbluhm 			    argv[0]);
539ec7c50bfSpedro 			exit(1);
540ec7c50bfSpedro 		}
541ec7c50bfSpedro 		argc++;
542ec7c50bfSpedro 		argv++;
543ec7c50bfSpedro 		n = call_syscall(scall, argv);
544ec7c50bfSpedro 		argc += n;
545ec7c50bfSpedro 		argv += n;
546ec7c50bfSpedro 		if (argv[0] == NULL)
547ec7c50bfSpedro 			break;
548ec7c50bfSpedro 		argc++;
549ec7c50bfSpedro 		argv++;
550ec7c50bfSpedro 	}
551ec7c50bfSpedro 
552ec7c50bfSpedro 	exit(0);
553ec7c50bfSpedro }
554ec7c50bfSpedro 
555ec7c50bfSpedro static const char *
err2str(int error)556ec7c50bfSpedro err2str(int error)
557ec7c50bfSpedro {
558ec7c50bfSpedro 	static char errnum[8];
559ec7c50bfSpedro 
560ec7c50bfSpedro 	switch (error) {
561ec7c50bfSpedro 	case EPERM:
562ec7c50bfSpedro 		return ("EPERM");
563ec7c50bfSpedro 	case ENOENT:
564ec7c50bfSpedro 		return ("ENOENT");
565ec7c50bfSpedro 	case ESRCH:
566ec7c50bfSpedro 		return ("ESRCH");
567ec7c50bfSpedro 	case EINTR:
568ec7c50bfSpedro 		return ("EINTR");
569ec7c50bfSpedro 	case EIO:
570ec7c50bfSpedro 		return ("EIO");
571ec7c50bfSpedro 	case ENXIO:
572ec7c50bfSpedro 		return ("ENXIO");
573ec7c50bfSpedro 	case E2BIG:
574ec7c50bfSpedro 		return ("E2BIG");
575ec7c50bfSpedro 	case ENOEXEC:
576ec7c50bfSpedro 		return ("ENOEXEC");
577ec7c50bfSpedro 	case EBADF:
578ec7c50bfSpedro 		return ("EBADF");
579ec7c50bfSpedro 	case ECHILD:
580ec7c50bfSpedro 		return ("ECHILD");
581ec7c50bfSpedro 	case EDEADLK:
582ec7c50bfSpedro 		return ("EDEADLK");
583ec7c50bfSpedro 	case ENOMEM:
584ec7c50bfSpedro 		return ("ENOMEM");
585ec7c50bfSpedro 	case EACCES:
586ec7c50bfSpedro 		return ("EACCES");
587ec7c50bfSpedro 	case EFAULT:
588ec7c50bfSpedro 		return ("EFAULT");
589ec7c50bfSpedro 	case ENOTBLK:
590ec7c50bfSpedro 		return ("ENOTBLK");
591ec7c50bfSpedro 	case EBUSY:
592ec7c50bfSpedro 		return ("EBUSY");
593ec7c50bfSpedro 	case EEXIST:
594ec7c50bfSpedro 		return ("EEXIST");
595ec7c50bfSpedro 	case EXDEV:
596ec7c50bfSpedro 		return ("EXDEV");
597ec7c50bfSpedro 	case ENODEV:
598ec7c50bfSpedro 		return ("ENODEV");
599ec7c50bfSpedro 	case ENOTDIR:
600ec7c50bfSpedro 		return ("ENOTDIR");
601ec7c50bfSpedro 	case EISDIR:
602ec7c50bfSpedro 		return ("EISDIR");
603ec7c50bfSpedro 	case EINVAL:
604ec7c50bfSpedro 		return ("EINVAL");
605ec7c50bfSpedro 	case ENFILE:
606ec7c50bfSpedro 		return ("ENFILE");
607ec7c50bfSpedro 	case EMFILE:
608ec7c50bfSpedro 		return ("EMFILE");
609ec7c50bfSpedro 	case ENOTTY:
610ec7c50bfSpedro 		return ("ENOTTY");
611ec7c50bfSpedro 	case ETXTBSY:
612ec7c50bfSpedro 		return ("ETXTBSY");
613ec7c50bfSpedro 	case EFBIG:
614ec7c50bfSpedro 		return ("EFBIG");
615ec7c50bfSpedro 	case ENOSPC:
616ec7c50bfSpedro 		return ("ENOSPC");
617ec7c50bfSpedro 	case ESPIPE:
618ec7c50bfSpedro 		return ("ESPIPE");
619ec7c50bfSpedro 	case EROFS:
620ec7c50bfSpedro 		return ("EROFS");
621ec7c50bfSpedro 	case EMLINK:
622ec7c50bfSpedro 		return ("EMLINK");
623ec7c50bfSpedro 	case EPIPE:
624ec7c50bfSpedro 		return ("EPIPE");
625ec7c50bfSpedro 	case EDOM:
626ec7c50bfSpedro 		return ("EDOM");
627ec7c50bfSpedro 	case ERANGE:
628ec7c50bfSpedro 		return ("ERANGE");
629ec7c50bfSpedro 	case EAGAIN:
630ec7c50bfSpedro 		return ("EAGAIN");
631ec7c50bfSpedro 	case EINPROGRESS:
632ec7c50bfSpedro 		return ("EINPROGRESS");
633ec7c50bfSpedro 	case EALREADY:
634ec7c50bfSpedro 		return ("EALREADY");
635ec7c50bfSpedro 	case ENOTSOCK:
636ec7c50bfSpedro 		return ("ENOTSOCK");
637ec7c50bfSpedro 	case EDESTADDRREQ:
638ec7c50bfSpedro 		return ("EDESTADDRREQ");
639ec7c50bfSpedro 	case EMSGSIZE:
640ec7c50bfSpedro 		return ("EMSGSIZE");
641ec7c50bfSpedro 	case EPROTOTYPE:
642ec7c50bfSpedro 		return ("EPROTOTYPE");
643ec7c50bfSpedro 	case ENOPROTOOPT:
644ec7c50bfSpedro 		return ("ENOPROTOOPT");
645ec7c50bfSpedro 	case EPROTONOSUPPORT:
646ec7c50bfSpedro 		return ("EPROTONOSUPPORT");
647ec7c50bfSpedro 	case ESOCKTNOSUPPORT:
648ec7c50bfSpedro 		return ("ESOCKTNOSUPPORT");
649ec7c50bfSpedro 	case EOPNOTSUPP:
650ec7c50bfSpedro 		return ("EOPNOTSUPP");
651ec7c50bfSpedro 	case EPFNOSUPPORT:
652ec7c50bfSpedro 		return ("EPFNOSUPPORT");
653ec7c50bfSpedro 	case EAFNOSUPPORT:
654ec7c50bfSpedro 		return ("EAFNOSUPPORT");
655ec7c50bfSpedro 	case EADDRINUSE:
656ec7c50bfSpedro 		return ("EADDRINUSE");
657ec7c50bfSpedro 	case EADDRNOTAVAIL:
658ec7c50bfSpedro 		return ("EADDRNOTAVAIL");
659ec7c50bfSpedro 	case ENETDOWN:
660ec7c50bfSpedro 		return ("ENETDOWN");
661ec7c50bfSpedro 	case ENETUNREACH:
662ec7c50bfSpedro 		return ("ENETUNREACH");
663ec7c50bfSpedro 	case ENETRESET:
664ec7c50bfSpedro 		return ("ENETRESET");
665ec7c50bfSpedro 	case ECONNABORTED:
666ec7c50bfSpedro 		return ("ECONNABORTED");
667ec7c50bfSpedro 	case ECONNRESET:
668ec7c50bfSpedro 		return ("ECONNRESET");
669ec7c50bfSpedro 	case ENOBUFS:
670ec7c50bfSpedro 		return ("ENOBUFS");
671ec7c50bfSpedro 	case EISCONN:
672ec7c50bfSpedro 		return ("EISCONN");
673ec7c50bfSpedro 	case ENOTCONN:
674ec7c50bfSpedro 		return ("ENOTCONN");
675ec7c50bfSpedro 	case ESHUTDOWN:
676ec7c50bfSpedro 		return ("ESHUTDOWN");
677ec7c50bfSpedro 	case ETOOMANYREFS:
678ec7c50bfSpedro 		return ("ETOOMANYREFS");
679ec7c50bfSpedro 	case ETIMEDOUT:
680ec7c50bfSpedro 		return ("ETIMEDOUT");
681ec7c50bfSpedro 	case ECONNREFUSED:
682ec7c50bfSpedro 		return ("ECONNREFUSED");
683ec7c50bfSpedro 	case ELOOP:
684ec7c50bfSpedro 		return ("ELOOP");
685ec7c50bfSpedro 	case ENAMETOOLONG:
686ec7c50bfSpedro 		return ("ENAMETOOLONG");
687ec7c50bfSpedro 	case EHOSTDOWN:
688ec7c50bfSpedro 		return ("EHOSTDOWN");
689ec7c50bfSpedro 	case EHOSTUNREACH:
690ec7c50bfSpedro 		return ("EHOSTUNREACH");
691ec7c50bfSpedro 	case ENOTEMPTY:
692ec7c50bfSpedro 		return ("ENOTEMPTY");
693ec7c50bfSpedro 	case EPROCLIM:
694ec7c50bfSpedro 		return ("EPROCLIM");
695ec7c50bfSpedro 	case EUSERS:
696ec7c50bfSpedro 		return ("EUSERS");
697ec7c50bfSpedro 	case EDQUOT:
698ec7c50bfSpedro 		return ("EDQUOT");
699ec7c50bfSpedro 	case ESTALE:
700ec7c50bfSpedro 		return ("ESTALE");
701ec7c50bfSpedro 	case EREMOTE:
702ec7c50bfSpedro 		return ("EREMOTE");
703ec7c50bfSpedro 	case EBADRPC:
704ec7c50bfSpedro 		return ("EBADRPC");
705ec7c50bfSpedro 	case ERPCMISMATCH:
706ec7c50bfSpedro 		return ("ERPCMISMATCH");
707ec7c50bfSpedro 	case EPROGUNAVAIL:
708ec7c50bfSpedro 		return ("EPROGUNAVAIL");
709ec7c50bfSpedro 	case EPROGMISMATCH:
710ec7c50bfSpedro 		return ("EPROGMISMATCH");
711ec7c50bfSpedro 	case EPROCUNAVAIL:
712ec7c50bfSpedro 		return ("EPROCUNAVAIL");
713ec7c50bfSpedro 	case ENOLCK:
714ec7c50bfSpedro 		return ("ENOLCK");
715ec7c50bfSpedro 	case ENOSYS:
716ec7c50bfSpedro 		return ("ENOSYS");
717ec7c50bfSpedro 	case EFTYPE:
718ec7c50bfSpedro 		return ("EFTYPE");
719ec7c50bfSpedro 	case EAUTH:
720ec7c50bfSpedro 		return ("EAUTH");
721ec7c50bfSpedro 	case ENEEDAUTH:
722ec7c50bfSpedro 		return ("ENEEDAUTH");
723ec7c50bfSpedro 	case EILSEQ:
724ec7c50bfSpedro 		return ("EILSEQ");
725ec7c50bfSpedro 	case ENOATTR:
726ec7c50bfSpedro 		return ("ENOATTR");
727ec7c50bfSpedro 	default:
728ec7c50bfSpedro 		snprintf(errnum, sizeof(errnum), "%d", error);
729ec7c50bfSpedro 		return (errnum);
730ec7c50bfSpedro 	}
731ec7c50bfSpedro }
732