2 * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
41 #ifndef HAS_TRUNCATE64
42 #define truncate64 truncate
48 #ifdef HAS_FREEBSD_ACL
53 #define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
82 #ifdef HAS_FREEBSD_ACL
89 #define TYPE_NONE 0x0000
90 #define TYPE_STRING 0x0001
91 #define TYPE_NUMBER 0x0002
93 #define TYPE_OPTIONAL 0x0100
99 enum action sd_action;
100 int sd_args[MAX_ARGS];
103 static struct syscall_desc syscalls[] = {
104 { "open", ACTION_OPEN, { TYPE_STRING, TYPE_STRING, TYPE_NUMBER | TYPE_OPTIONAL, TYPE_NONE } },
105 { "create", ACTION_CREATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
106 { "unlink", ACTION_UNLINK, { TYPE_STRING, TYPE_NONE } },
107 { "mkdir", ACTION_MKDIR, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
108 { "rmdir", ACTION_RMDIR, { TYPE_STRING, TYPE_NONE } },
109 { "link", ACTION_LINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
110 { "symlink", ACTION_SYMLINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
111 { "rename", ACTION_RENAME, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
112 { "mkfifo", ACTION_MKFIFO, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
113 { "chmod", ACTION_CHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
115 { "lchmod", ACTION_LCHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
117 { "chown", ACTION_CHOWN, { TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE } },
118 { "lchown", ACTION_LCHOWN, { TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE } },
120 { "chflags", ACTION_CHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
123 { "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
125 { "truncate", ACTION_TRUNCATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
126 { "stat", ACTION_STAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
127 { "lstat", ACTION_LSTAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
128 { "pathconf", ACTION_PATHCONF, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
129 #ifdef HAS_FREEBSD_ACL
130 { "prependacl", ACTION_PREPENDACL, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
131 { "readacl", ACTION_READACL, { TYPE_STRING, TYPE_NONE } },
133 { "write", ACTION_WRITE, { TYPE_STRING, TYPE_NONE } },
134 { NULL, -1, { TYPE_NONE } }
142 static struct flag open_flags[] = {
144 { O_RDONLY, "O_RDONLY" },
147 { O_WRONLY, "O_WRONLY" },
150 { O_RDWR, "O_RDWR" },
153 { O_NONBLOCK, "O_NONBLOCK" },
156 { O_APPEND, "O_APPEND" },
159 { O_CREAT, "O_CREAT" },
162 { O_TRUNC, "O_TRUNC" },
165 { O_EXCL, "O_EXCL" },
168 { O_SHLOCK, "O_SHLOCK" },
171 { O_EXLOCK, "O_EXLOCK" },
174 { O_DIRECT, "O_DIRECT" },
177 { O_FSYNC, "O_FSYNC" },
180 { O_SYNC, "O_SYNC" },
183 { O_NOFOLLOW, "O_NOFOLLOW" },
186 { O_NOCTTY, "O_NOCTTY" },
192 static struct flag chflags_flags[] = {
194 { UF_NODUMP, "UF_NODUMP" },
197 { UF_IMMUTABLE, "UF_IMMUTABLE" },
200 { UF_APPEND, "UF_APPEND" },
203 { UF_NOUNLINK, "UF_NOUNLINK" },
206 { UF_OPAQUE, "UF_OPAQUE" },
209 { SF_ARCHIVED, "SF_ARCHIVED" },
212 { SF_IMMUTABLE, "SF_IMMUTABLE" },
215 { SF_APPEND, "SF_APPEND" },
218 { SF_NOUNLINK, "SF_NOUNLINK" },
221 { SF_SNAPSHOT, "SF_SNAPSHOT" },
232 static struct name pathconf_names[] = {
234 { _PC_LINK_MAX, "_PC_LINK_MAX" },
237 { _PC_NAME_MAX, "_PC_NAME_MAX" },
240 { _PC_PATH_MAX, "_PC_PATH_MAX" },
242 #ifdef _PC_SYMLINK_MAX
243 { _PC_SYMLINK_MAX, "_PC_SYMLINK_MAX" },
248 static const char *err2str(int error);
254 fprintf(stderr, "usage: fstest [-u uid] [-g gid1[,gid2[...]]] syscall args ...\n");
259 str2flags(struct flag *tflags, char *sflags)
265 for (f = strtok(sflags, ","); f != NULL; f = strtok(NULL, ",")) {
266 /* Support magic 'none' flag which just reset all flags. */
267 if (strcmp(f, "none") == 0)
269 for (i = 0; tflags[i].f_str != NULL; i++) {
270 if (strcmp(tflags[i].f_str, f) == 0)
273 if (tflags[i].f_str == NULL) {
274 fprintf(stderr, "unknown flag '%s'\n", f);
277 flags |= tflags[i].f_flag;
284 flags2str(struct flag *tflags, long long flags)
286 static char sflags[1024];
290 for (i = 0; tflags[i].f_str != NULL; i++) {
291 if (flags & tflags[i].f_flag) {
292 if (sflags[0] != '\0')
293 strlcat(sflags, ",", sizeof(sflags));
294 strlcat(sflags, tflags[i].f_str, sizeof(sflags));
297 if (sflags[0] == '\0')
298 strlcpy(sflags, "none", sizeof(sflags));
304 str2name(struct name *names, char *name)
308 for (i = 0; names[i].n_str != NULL; i++) {
309 if (strcmp(names[i].n_str, name) == 0)
310 return (names[i].n_name);
315 static struct syscall_desc *
316 find_syscall(const char *name)
320 for (i = 0; syscalls[i].sd_name != NULL; i++) {
321 if (strcmp(syscalls[i].sd_name, name) == 0)
322 return (&syscalls[i]);
328 show_stat(struct stat64 *sp, const char *what)
331 if (strcmp(what, "mode") == 0)
332 printf("0%o", (unsigned int)(sp->st_mode & ALLPERMS));
333 else if (strcmp(what, "inode") == 0)
334 printf("%lld", (long long)sp->st_ino);
335 else if (strcmp(what, "nlink") == 0)
336 printf("%lld", (long long)sp->st_nlink);
337 else if (strcmp(what, "uid") == 0)
338 printf("%d", (int)sp->st_uid);
339 else if (strcmp(what, "gid") == 0)
340 printf("%d", (int)sp->st_gid);
341 else if (strcmp(what, "size") == 0)
342 printf("%lld", (long long)sp->st_size);
343 else if (strcmp(what, "blocks") == 0)
344 printf("%lld", (long long)sp->st_blocks);
345 else if (strcmp(what, "atime") == 0)
346 printf("%lld", (long long)sp->st_atime);
347 else if (strcmp(what, "mtime") == 0)
348 printf("%lld", (long long)sp->st_mtime);
349 else if (strcmp(what, "ctime") == 0)
350 printf("%lld", (long long)sp->st_ctime);
352 else if (strcmp(what, "flags") == 0)
353 printf("%s", flags2str(chflags_flags, (long long)sp->st_flags));
355 else if (strcmp(what, "type") == 0) {
356 switch (sp->st_mode & S_IFMT) {
388 show_stats(struct stat64 *sp, char *what)
393 for (w = strtok(what, ","); w != NULL; w = strtok(NULL, ",")) {
402 call_syscall(struct syscall_desc *scall, char *argv[])
413 #ifdef HAS_FREEBSD_ACL
414 int entry_id = ACL_FIRST_ENTRY;
416 acl_entry_t entry, newentry;
420 * Verify correctness of the arguments.
422 for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) {
423 if (scall->sd_args[i] == TYPE_NONE) {
424 if (argv[i] == NULL || strcmp(argv[i], ":") == 0)
426 fprintf(stderr, "too many arguments [%s]\n", argv[i]);
429 if (argv[i] == NULL || strcmp(argv[i], ":") == 0) {
430 if (scall->sd_args[i] & TYPE_OPTIONAL)
432 fprintf(stderr, "too few arguments\n");
435 if (scall->sd_args[i] & TYPE_STRING) {
436 if (strcmp(argv[i], "NULL") == 0)
438 else if (strcmp(argv[i], "DEADCODE") == 0)
439 args[i].str = (void *)0xdeadc0de;
441 args[i].str = argv[i];
442 } else if (scall->sd_args[i] & TYPE_NUMBER) {
443 args[i].num = strtoll(argv[i], &endp, 0);
444 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
445 fprintf(stderr, "invalid argument %u, number expected [%s]\n", i, endp);
452 * Call the given syscall.
454 #define NUM(n) (args[(n)].num)
455 #define STR(n) (args[(n)].str)
456 switch (scall->sd_action) {
458 flags = str2flags(open_flags, STR(1));
459 if (flags & O_CREAT) {
461 fprintf(stderr, "too few arguments\n");
464 rval = open(STR(0), (int)flags, (mode_t)NUM(2));
467 fprintf(stderr, "too many arguments\n");
470 rval = open(STR(0), (int)flags);
474 rval = open(STR(0), O_CREAT | O_EXCL, (mode_t)NUM(1));
479 rval = unlink(STR(0));
482 rval = mkdir(STR(0), (mode_t)NUM(1));
485 rval = rmdir(STR(0));
488 rval = link(STR(0), STR(1));
491 rval = symlink(STR(0), STR(1));
494 rval = rename(STR(0), STR(1));
497 rval = mkfifo(STR(0), (mode_t)NUM(1));
500 rval = chmod(STR(0), (mode_t)NUM(1));
504 rval = lchmod(STR(0), (mode_t)NUM(1));
508 rval = chown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
511 rval = lchown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
515 rval = chflags(STR(0), (unsigned long)str2flags(chflags_flags, STR(1)));
519 case ACTION_LCHFLAGS:
520 rval = lchflags(STR(0), (int)str2flags(chflags_flags, STR(1)));
523 case ACTION_TRUNCATE:
524 rval = truncate64(STR(0), NUM(1));
527 rval = stat64(STR(0), &sb);
529 show_stats(&sb, STR(1));
534 rval = lstat64(STR(0), &sb);
536 show_stats(&sb, STR(1));
540 case ACTION_PATHCONF:
544 name = str2name(pathconf_names, STR(1));
546 fprintf(stderr, "unknown name %s", STR(1));
550 lrval = pathconf(STR(0), name);
551 if (lrval == -1 && errno == 0) {
552 printf("unlimited\n");
554 } else if (lrval >= 0) {
555 printf("%ld\n", lrval);
561 #ifdef HAS_FREEBSD_ACL
562 case ACTION_PREPENDACL:
565 acl = acl_get_file(STR(0), ACL_TYPE_NFS4);
569 newacl = acl_from_text(STR(1));
573 while (acl_get_entry(newacl, entry_id, &newentry) == 1) {
574 entry_id = ACL_NEXT_ENTRY;
576 if (acl_create_entry_np(&acl, &entry, 0))
579 if (acl_copy_entry(entry, newentry))
583 rval = acl_set_file(STR(0), ACL_TYPE_NFS4, acl);
587 acl = acl_get_file(STR(0), ACL_TYPE_NFS4);
596 rval = open(STR(0), O_WRONLY);
600 rval = write(rval, "x", 1);
604 fprintf(stderr, "unsupported syscall\n");
612 serrno = err2str(errno);
613 fprintf(stderr, "%s returned %d\n", scall->sd_name, rval);
614 printf("%s\n", serrno);
629 ngroups = sysconf(_SC_NGROUPS_MAX);
631 gidset = malloc(sizeof(*gidset) * ngroups);
632 assert(gidset != NULL);
633 for (i = 0, g = strtok(gids, ","); g != NULL; g = strtok(NULL, ","), i++) {
635 fprintf(stderr, "too many gids\n");
638 gidset[i] = strtol(g, &endp, 0);
639 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
640 fprintf(stderr, "invalid gid '%s' - number expected\n",
645 if (setgroups(i, gidset) < 0) {
646 fprintf(stderr, "cannot change groups: %s\n", strerror(errno));
649 if (setegid(gidset[0]) < 0) {
650 fprintf(stderr, "cannot change effective gid: %s\n", strerror(errno));
657 main(int argc, char *argv[])
659 struct syscall_desc *scall;
668 while ((ch = getopt(argc, argv, "g:u:U:")) != -1) {
674 uid = (int)strtol(optarg, &endp, 0);
675 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
676 fprintf(stderr, "invalid uid '%s' - number "
677 "expected\n", optarg);
682 umsk = (int)strtol(optarg, &endp, 0);
683 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
684 fprintf(stderr, "invalid umask '%s' - number "
685 "expected\n", optarg);
697 fprintf(stderr, "too few arguments\n");
702 fprintf(stderr, "changing groups to %s\n", gids);
706 fprintf(stderr, "changing uid to %d\n", uid);
707 if (setuid(uid) < 0) {
708 fprintf(stderr, "cannot change uid: %s\n",
714 /* Change umask to requested value or to 0, if not requested. */
718 scall = find_syscall(argv[0]);
720 fprintf(stderr, "syscall '%s' not supported\n", argv[0]);
725 n = call_syscall(scall, argv);
740 static char errnum[8];
885 return ("EINPROGRESS");
897 return ("EDESTADDRREQ");
905 return ("EPROTOTYPE");
909 return ("ENOPROTOOPT");
911 #ifdef EPROTONOSUPPORT
912 case EPROTONOSUPPORT:
913 return ("EPROTONOSUPPORT");
915 #ifdef ESOCKTNOSUPPORT
916 case ESOCKTNOSUPPORT:
917 return ("ESOCKTNOSUPPORT");
921 return ("EOPNOTSUPP");
925 return ("EPFNOSUPPORT");
929 return ("EAFNOSUPPORT");
933 return ("EADDRINUSE");
937 return ("EADDRNOTAVAIL");
945 return ("ENETUNREACH");
949 return ("ENETRESET");
953 return ("ECONNABORTED");
957 return ("ECONNRESET");
973 return ("ESHUTDOWN");
977 return ("ETOOMANYREFS");
981 return ("ETIMEDOUT");
985 return ("ECONNREFUSED");
993 return ("ENAMETOOLONG");
997 return ("EHOSTDOWN");
1001 return ("EHOSTUNREACH");
1005 return ("ENOTEMPTY");
1009 return ("EPROCLIM");
1033 return ("ERPCMISMATCH");
1037 return ("EPROGUNAVAIL");
1039 #ifdef EPROGMISMATCH
1041 return ("EPROGMISMATCH");
1045 return ("EPROCUNAVAIL");
1065 return ("ENEEDAUTH");
1077 return ("EOVERFLOW");
1081 return ("ECANCELED");
1101 return ("EMULTIHOP");
1112 snprintf(errnum, sizeof(errnum), "%d", error);