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
50 #define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
81 #define TYPE_NONE 0x0000
82 #define TYPE_STRING 0x0001
83 #define TYPE_NUMBER 0x0002
85 #define TYPE_OPTIONAL 0x0100
91 enum action sd_action;
92 int sd_args[MAX_ARGS];
95 static struct syscall_desc syscalls[] = {
96 { "open", ACTION_OPEN, { TYPE_STRING, TYPE_STRING, TYPE_NUMBER | TYPE_OPTIONAL, TYPE_NONE } },
97 { "create", ACTION_CREATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
98 { "unlink", ACTION_UNLINK, { TYPE_STRING, TYPE_NONE } },
99 { "mkdir", ACTION_MKDIR, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
100 { "rmdir", ACTION_RMDIR, { TYPE_STRING, TYPE_NONE } },
101 { "link", ACTION_LINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
102 { "symlink", ACTION_SYMLINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
103 { "rename", ACTION_RENAME, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
104 { "mkfifo", ACTION_MKFIFO, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
105 { "chmod", ACTION_CHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
107 { "lchmod", ACTION_LCHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
109 { "chown", ACTION_CHOWN, { TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE } },
110 { "lchown", ACTION_LCHOWN, { TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE } },
112 { "chflags", ACTION_CHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
115 { "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
117 { "truncate", ACTION_TRUNCATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
118 { "stat", ACTION_STAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
119 { "lstat", ACTION_LSTAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
120 { "pathconf", ACTION_PATHCONF, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
121 { NULL, -1, { TYPE_NONE } }
129 static struct flag open_flags[] = {
131 { O_RDONLY, "O_RDONLY" },
134 { O_WRONLY, "O_WRONLY" },
137 { O_RDWR, "O_RDWR" },
140 { O_NONBLOCK, "O_NONBLOCK" },
143 { O_APPEND, "O_APPEND" },
146 { O_CREAT, "O_CREAT" },
149 { O_TRUNC, "O_TRUNC" },
152 { O_EXCL, "O_EXCL" },
155 { O_SHLOCK, "O_SHLOCK" },
158 { O_EXLOCK, "O_EXLOCK" },
161 { O_DIRECT, "O_DIRECT" },
164 { O_FSYNC, "O_FSYNC" },
167 { O_SYNC, "O_SYNC" },
170 { O_NOFOLLOW, "O_NOFOLLOW" },
173 { O_NOCTTY, "O_NOCTTY" },
179 static struct flag chflags_flags[] = {
181 { UF_NODUMP, "UF_NODUMP" },
184 { UF_IMMUTABLE, "UF_IMMUTABLE" },
187 { UF_APPEND, "UF_APPEND" },
190 { UF_NOUNLINK, "UF_NOUNLINK" },
193 { UF_OPAQUE, "UF_OPAQUE" },
196 { SF_ARCHIVED, "SF_ARCHIVED" },
199 { SF_IMMUTABLE, "SF_IMMUTABLE" },
202 { SF_APPEND, "SF_APPEND" },
205 { SF_NOUNLINK, "SF_NOUNLINK" },
208 { SF_SNAPSHOT, "SF_SNAPSHOT" },
219 static struct name pathconf_names[] = {
221 { _PC_LINK_MAX, "_PC_LINK_MAX" },
224 { _PC_NAME_MAX, "_PC_NAME_MAX" },
227 { _PC_PATH_MAX, "_PC_PATH_MAX" },
229 #ifdef _PC_SYMLINK_MAX
230 { _PC_SYMLINK_MAX, "_PC_SYMLINK_MAX" },
235 static const char *err2str(int error);
241 fprintf(stderr, "usage: fstest [-u uid] [-g gid1[,gid2[...]]] syscall args ...\n");
246 str2flags(struct flag *tflags, char *sflags)
252 for (f = strtok(sflags, ","); f != NULL; f = strtok(NULL, ",")) {
253 /* Support magic 'none' flag which just reset all flags. */
254 if (strcmp(f, "none") == 0)
256 for (i = 0; tflags[i].f_str != NULL; i++) {
257 if (strcmp(tflags[i].f_str, f) == 0)
260 if (tflags[i].f_str == NULL) {
261 fprintf(stderr, "unknown flag '%s'\n", f);
264 flags |= tflags[i].f_flag;
271 flags2str(struct flag *tflags, long long flags)
273 static char sflags[1024];
277 for (i = 0; tflags[i].f_str != NULL; i++) {
278 if (flags & tflags[i].f_flag) {
279 if (sflags[0] != '\0')
280 strlcat(sflags, ",", sizeof(sflags));
281 strlcat(sflags, tflags[i].f_str, sizeof(sflags));
284 if (sflags[0] == '\0')
285 strlcpy(sflags, "none", sizeof(sflags));
291 str2name(struct name *names, char *name)
295 for (i = 0; names[i].n_str != NULL; i++) {
296 if (strcmp(names[i].n_str, name) == 0)
297 return (names[i].n_name);
302 static struct syscall_desc *
303 find_syscall(const char *name)
307 for (i = 0; syscalls[i].sd_name != NULL; i++) {
308 if (strcmp(syscalls[i].sd_name, name) == 0)
309 return (&syscalls[i]);
315 show_stat(struct stat64 *sp, const char *what)
318 if (strcmp(what, "mode") == 0)
319 printf("0%o", (unsigned int)(sp->st_mode & ALLPERMS));
320 else if (strcmp(what, "inode") == 0)
321 printf("%lld", (long long)sp->st_ino);
322 else if (strcmp(what, "nlink") == 0)
323 printf("%lld", (long long)sp->st_nlink);
324 else if (strcmp(what, "uid") == 0)
325 printf("%d", (int)sp->st_uid);
326 else if (strcmp(what, "gid") == 0)
327 printf("%d", (int)sp->st_gid);
328 else if (strcmp(what, "size") == 0)
329 printf("%lld", (long long)sp->st_size);
330 else if (strcmp(what, "blocks") == 0)
331 printf("%lld", (long long)sp->st_blocks);
332 else if (strcmp(what, "atime") == 0)
333 printf("%lld", (long long)sp->st_atime);
334 else if (strcmp(what, "mtime") == 0)
335 printf("%lld", (long long)sp->st_mtime);
336 else if (strcmp(what, "ctime") == 0)
337 printf("%lld", (long long)sp->st_ctime);
339 else if (strcmp(what, "flags") == 0)
340 printf("%s", flags2str(chflags_flags, (long long)sp->st_flags));
342 else if (strcmp(what, "type") == 0) {
343 switch (sp->st_mode & S_IFMT) {
375 show_stats(struct stat64 *sp, char *what)
380 for (w = strtok(what, ","); w != NULL; w = strtok(NULL, ",")) {
389 call_syscall(struct syscall_desc *scall, char *argv[])
402 * Verify correctness of the arguments.
404 for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) {
405 if (scall->sd_args[i] == TYPE_NONE) {
406 if (argv[i] == NULL || strcmp(argv[i], ":") == 0)
408 fprintf(stderr, "too many arguments [%s]\n", argv[i]);
411 if (argv[i] == NULL || strcmp(argv[i], ":") == 0) {
412 if (scall->sd_args[i] & TYPE_OPTIONAL)
414 fprintf(stderr, "too few arguments\n");
417 if (scall->sd_args[i] & TYPE_STRING) {
418 if (strcmp(argv[i], "NULL") == 0)
420 else if (strcmp(argv[i], "DEADCODE") == 0)
421 args[i].str = (void *)0xdeadc0de;
423 args[i].str = argv[i];
424 } else if (scall->sd_args[i] & TYPE_NUMBER) {
425 args[i].num = strtoll(argv[i], &endp, 0);
426 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
427 fprintf(stderr, "invalid argument %u, number expected [%s]\n", i, endp);
434 * Call the given syscall.
436 #define NUM(n) (args[(n)].num)
437 #define STR(n) (args[(n)].str)
438 switch (scall->sd_action) {
440 flags = str2flags(open_flags, STR(1));
441 if (flags & O_CREAT) {
443 fprintf(stderr, "too few arguments\n");
446 rval = open(STR(0), (int)flags, (mode_t)NUM(2));
449 fprintf(stderr, "too many arguments\n");
452 rval = open(STR(0), (int)flags);
456 rval = open(STR(0), O_CREAT | O_EXCL, (mode_t)NUM(1));
461 rval = unlink(STR(0));
464 rval = mkdir(STR(0), (mode_t)NUM(1));
467 rval = rmdir(STR(0));
470 rval = link(STR(0), STR(1));
473 rval = symlink(STR(0), STR(1));
476 rval = rename(STR(0), STR(1));
479 rval = mkfifo(STR(0), (mode_t)NUM(1));
482 rval = chmod(STR(0), (mode_t)NUM(1));
486 rval = lchmod(STR(0), (mode_t)NUM(1));
490 rval = chown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
493 rval = lchown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
497 rval = chflags(STR(0), (unsigned long)str2flags(chflags_flags, STR(1)));
501 case ACTION_LCHFLAGS:
502 rval = lchflags(STR(0), (int)str2flags(chflags_flags, STR(1)));
505 case ACTION_TRUNCATE:
506 rval = truncate64(STR(0), NUM(1));
509 rval = stat64(STR(0), &sb);
511 show_stats(&sb, STR(1));
516 rval = lstat64(STR(0), &sb);
518 show_stats(&sb, STR(1));
522 case ACTION_PATHCONF:
526 name = str2name(pathconf_names, STR(1));
528 fprintf(stderr, "unknown name %s", STR(1));
532 lrval = pathconf(STR(0), name);
533 if (lrval == -1 && errno == 0) {
534 printf("unlimited\n");
536 } else if (lrval >= 0) {
537 printf("%ld\n", lrval);
544 fprintf(stderr, "unsupported syscall\n");
552 serrno = err2str(errno);
553 fprintf(stderr, "%s returned %d\n", scall->sd_name, rval);
554 printf("%s\n", serrno);
569 ngroups = sysconf(_SC_NGROUPS_MAX);
571 gidset = malloc(sizeof(*gidset) * ngroups);
572 assert(gidset != NULL);
573 for (i = 0, g = strtok(gids, ","); g != NULL; g = strtok(NULL, ","), i++) {
575 fprintf(stderr, "too many gids\n");
578 gidset[i] = strtol(g, &endp, 0);
579 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
580 fprintf(stderr, "invalid gid '%s' - number expected\n",
585 if (setgroups(i, gidset) < 0) {
586 fprintf(stderr, "cannot change groups: %s\n", strerror(errno));
589 if (setegid(gidset[0]) < 0) {
590 fprintf(stderr, "cannot change effective gid: %s\n", strerror(errno));
597 main(int argc, char *argv[])
599 struct syscall_desc *scall;
608 while ((ch = getopt(argc, argv, "g:u:U:")) != -1) {
614 uid = (int)strtol(optarg, &endp, 0);
615 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
616 fprintf(stderr, "invalid uid '%s' - number "
617 "expected\n", optarg);
622 umsk = (int)strtol(optarg, &endp, 0);
623 if (*endp != '\0' && !isspace((unsigned char)*endp)) {
624 fprintf(stderr, "invalid umask '%s' - number "
625 "expected\n", optarg);
637 fprintf(stderr, "too few arguments\n");
642 fprintf(stderr, "changing groups to %s\n", gids);
646 fprintf(stderr, "changing uid to %d\n", uid);
647 if (setuid(uid) < 0) {
648 fprintf(stderr, "cannot change uid: %s\n",
654 /* Change umask to requested value or to 0, if not requested. */
658 scall = find_syscall(argv[0]);
660 fprintf(stderr, "syscall '%s' not supported\n", argv[0]);
665 n = call_syscall(scall, argv);
680 static char errnum[8];
825 return ("EINPROGRESS");
837 return ("EDESTADDRREQ");
845 return ("EPROTOTYPE");
849 return ("ENOPROTOOPT");
851 #ifdef EPROTONOSUPPORT
852 case EPROTONOSUPPORT:
853 return ("EPROTONOSUPPORT");
855 #ifdef ESOCKTNOSUPPORT
856 case ESOCKTNOSUPPORT:
857 return ("ESOCKTNOSUPPORT");
861 return ("EOPNOTSUPP");
865 return ("EPFNOSUPPORT");
869 return ("EAFNOSUPPORT");
873 return ("EADDRINUSE");
877 return ("EADDRNOTAVAIL");
885 return ("ENETUNREACH");
889 return ("ENETRESET");
893 return ("ECONNABORTED");
897 return ("ECONNRESET");
913 return ("ESHUTDOWN");
917 return ("ETOOMANYREFS");
921 return ("ETIMEDOUT");
925 return ("ECONNREFUSED");
933 return ("ENAMETOOLONG");
937 return ("EHOSTDOWN");
941 return ("EHOSTUNREACH");
945 return ("ENOTEMPTY");
973 return ("ERPCMISMATCH");
977 return ("EPROGUNAVAIL");
981 return ("EPROGMISMATCH");
985 return ("EPROCUNAVAIL");
1005 return ("ENEEDAUTH");
1017 return ("EOVERFLOW");
1021 return ("ECANCELED");
1041 return ("EMULTIHOP");
1052 snprintf(errnum, sizeof(errnum), "%d", error);