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)
80 #define TYPE_NONE 0x0000
81 #define TYPE_STRING 0x0001
82 #define TYPE_NUMBER 0x0002
84 #define TYPE_OPTIONAL 0x0100
90 enum action sd_action
;
91 int sd_args
[MAX_ARGS
];
94 static struct syscall_desc syscalls
[] = {
95 { "open", ACTION_OPEN
, { TYPE_STRING
, TYPE_STRING
, TYPE_NUMBER
| TYPE_OPTIONAL
, TYPE_NONE
} },
96 { "create", ACTION_CREATE
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NONE
} },
97 { "unlink", ACTION_UNLINK
, { TYPE_STRING
, TYPE_NONE
} },
98 { "mkdir", ACTION_MKDIR
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NONE
} },
99 { "rmdir", ACTION_RMDIR
, { TYPE_STRING
, TYPE_NONE
} },
100 { "link", ACTION_LINK
, { TYPE_STRING
, TYPE_STRING
, TYPE_NONE
} },
101 { "symlink", ACTION_SYMLINK
, { TYPE_STRING
, TYPE_STRING
, TYPE_NONE
} },
102 { "rename", ACTION_RENAME
, { TYPE_STRING
, TYPE_STRING
, TYPE_NONE
} },
103 { "mkfifo", ACTION_MKFIFO
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NONE
} },
104 { "chmod", ACTION_CHMOD
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NONE
} },
106 { "lchmod", ACTION_LCHMOD
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NONE
} },
108 { "chown", ACTION_CHOWN
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NUMBER
, TYPE_NONE
} },
109 { "lchown", ACTION_LCHOWN
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NUMBER
, TYPE_NONE
} },
111 { "chflags", ACTION_CHFLAGS
, { TYPE_STRING
, TYPE_STRING
, TYPE_NONE
} },
114 { "lchflags", ACTION_LCHFLAGS
, { TYPE_STRING
, TYPE_STRING
, TYPE_NONE
} },
116 { "truncate", ACTION_TRUNCATE
, { TYPE_STRING
, TYPE_NUMBER
, TYPE_NONE
} },
117 { "stat", ACTION_STAT
, { TYPE_STRING
, TYPE_STRING
, TYPE_NONE
} },
118 { "lstat", ACTION_LSTAT
, { TYPE_STRING
, TYPE_STRING
, TYPE_NONE
} },
119 { NULL
, -1, { TYPE_NONE
} }
127 static struct flag open_flags
[] = {
129 { O_RDONLY
, "O_RDONLY" },
132 { O_WRONLY
, "O_WRONLY" },
135 { O_RDWR
, "O_RDWR" },
138 { O_NONBLOCK
, "O_NONBLOCK" },
141 { O_APPEND
, "O_APPEND" },
144 { O_CREAT
, "O_CREAT" },
147 { O_TRUNC
, "O_TRUNC" },
150 { O_EXCL
, "O_EXCL" },
153 { O_SHLOCK
, "O_SHLOCK" },
156 { O_EXLOCK
, "O_EXLOCK" },
159 { O_DIRECT
, "O_DIRECT" },
162 { O_FSYNC
, "O_FSYNC" },
165 { O_SYNC
, "O_SYNC" },
168 { O_NOFOLLOW
, "O_NOFOLLOW" },
171 { O_NOCTTY
, "O_NOCTTY" },
177 static struct flag chflags_flags
[] = {
179 { UF_NODUMP
, "UF_NODUMP" },
182 { UF_IMMUTABLE
, "UF_IMMUTABLE" },
185 { UF_APPEND
, "UF_APPEND" },
188 { UF_NOUNLINK
, "UF_NOUNLINK" },
191 { UF_OPAQUE
, "UF_OPAQUE" },
194 { SF_ARCHIVED
, "SF_ARCHIVED" },
197 { SF_IMMUTABLE
, "SF_IMMUTABLE" },
200 { SF_APPEND
, "SF_APPEND" },
203 { SF_NOUNLINK
, "SF_NOUNLINK" },
206 { SF_SNAPSHOT
, "SF_SNAPSHOT" },
212 static const char *err2str(int error
);
218 fprintf(stderr
, "usage: fstest [-u uid] [-g gid1[,gid2[...]]] syscall args ...\n");
223 str2flags(struct flag
*tflags
, char *sflags
)
229 for (f
= strtok(sflags
, ","); f
!= NULL
; f
= strtok(NULL
, ",")) {
230 /* Support magic 'none' flag which just reset all flags. */
231 if (strcmp(f
, "none") == 0)
233 for (i
= 0; tflags
[i
].f_str
!= NULL
; i
++) {
234 if (strcmp(tflags
[i
].f_str
, f
) == 0)
237 if (tflags
[i
].f_str
== NULL
) {
238 fprintf(stderr
, "unknown flag '%s'\n", f
);
241 flags
|= tflags
[i
].f_flag
;
248 flags2str(struct flag
*tflags
, long long flags
)
250 static char sflags
[1024];
254 for (i
= 0; tflags
[i
].f_str
!= NULL
; i
++) {
255 if (flags
& tflags
[i
].f_flag
) {
256 if (sflags
[0] != '\0')
257 strlcat(sflags
, ",", sizeof(sflags
));
258 strlcat(sflags
, tflags
[i
].f_str
, sizeof(sflags
));
261 if (sflags
[0] == '\0')
262 strlcpy(sflags
, "none", sizeof(sflags
));
267 static struct syscall_desc
*
268 find_syscall(const char *name
)
272 for (i
= 0; syscalls
[i
].sd_name
!= NULL
; i
++) {
273 if (strcmp(syscalls
[i
].sd_name
, name
) == 0)
274 return (&syscalls
[i
]);
280 show_stat(struct stat64
*sp
, const char *what
)
283 if (strcmp(what
, "mode") == 0)
284 printf("0%o", (unsigned int)(sp
->st_mode
& ALLPERMS
));
285 else if (strcmp(what
, "inode") == 0)
286 printf("%lld", (long long)sp
->st_ino
);
287 else if (strcmp(what
, "nlink") == 0)
288 printf("%lld", (long long)sp
->st_nlink
);
289 else if (strcmp(what
, "uid") == 0)
290 printf("%d", (int)sp
->st_uid
);
291 else if (strcmp(what
, "gid") == 0)
292 printf("%d", (int)sp
->st_gid
);
293 else if (strcmp(what
, "size") == 0)
294 printf("%lld", (long long)sp
->st_size
);
295 else if (strcmp(what
, "blocks") == 0)
296 printf("%lld", (long long)sp
->st_blocks
);
297 else if (strcmp(what
, "atime") == 0)
298 printf("%lld", (long long)sp
->st_atime
);
299 else if (strcmp(what
, "mtime") == 0)
300 printf("%lld", (long long)sp
->st_mtime
);
301 else if (strcmp(what
, "ctime") == 0)
302 printf("%lld", (long long)sp
->st_ctime
);
304 else if (strcmp(what
, "flags") == 0)
305 printf("%s", flags2str(chflags_flags
, sp
->st_flags
));
307 else if (strcmp(what
, "type") == 0) {
308 switch (sp
->st_mode
& S_IFMT
) {
340 show_stats(struct stat64
*sp
, char *what
)
345 for (w
= strtok(what
, ","); w
!= NULL
; w
= strtok(NULL
, ",")) {
354 call_syscall(struct syscall_desc
*scall
, char *argv
[])
367 * Verify correctness of the arguments.
369 for (i
= 0; i
< sizeof(args
)/sizeof(args
[0]); i
++) {
370 if (scall
->sd_args
[i
] == TYPE_NONE
) {
371 if (argv
[i
] == NULL
|| strcmp(argv
[i
], ":") == 0)
373 fprintf(stderr
, "too many arguments [%s]\n", argv
[i
]);
376 if (argv
[i
] == NULL
|| strcmp(argv
[i
], ":") == 0) {
377 if (scall
->sd_args
[i
] & TYPE_OPTIONAL
)
379 fprintf(stderr
, "too few arguments\n");
382 if (scall
->sd_args
[i
] & TYPE_STRING
) {
383 if (strcmp(argv
[i
], "NULL") == 0)
385 else if (strcmp(argv
[i
], "DEADCODE") == 0)
386 args
[i
].str
= (void *)0xdeadc0de;
388 args
[i
].str
= argv
[i
];
389 } else if (scall
->sd_args
[i
] & TYPE_NUMBER
) {
390 args
[i
].num
= strtoll(argv
[i
], &endp
, 0);
391 if (*endp
!= '\0' && !isspace((unsigned char)*endp
)) {
392 fprintf(stderr
, "invalid argument %u, number expected [%s]\n", i
, endp
);
399 * Call the given syscall.
401 #define NUM(n) (args[(n)].num)
402 #define STR(n) (args[(n)].str)
403 switch (scall
->sd_action
) {
405 flags
= str2flags(open_flags
, STR(1));
406 if (flags
& O_CREAT
) {
408 fprintf(stderr
, "too few arguments\n");
411 rval
= open(STR(0), flags
, (mode_t
)NUM(2));
414 fprintf(stderr
, "too many arguments\n");
417 rval
= open(STR(0), flags
);
421 rval
= open(STR(0), O_CREAT
| O_EXCL
, NUM(1));
426 rval
= unlink(STR(0));
429 rval
= mkdir(STR(0), NUM(1));
432 rval
= rmdir(STR(0));
435 rval
= link(STR(0), STR(1));
438 rval
= symlink(STR(0), STR(1));
441 rval
= rename(STR(0), STR(1));
444 rval
= mkfifo(STR(0), NUM(1));
447 rval
= chmod(STR(0), NUM(1));
451 rval
= lchmod(STR(0), NUM(1));
455 rval
= chown(STR(0), NUM(1), NUM(2));
458 rval
= lchown(STR(0), NUM(1), NUM(2));
462 rval
= chflags(STR(0), str2flags(chflags_flags
, STR(1)));
466 case ACTION_LCHFLAGS
:
467 rval
= lchflags(STR(0), str2flags(chflags_flags
, STR(1)));
470 case ACTION_TRUNCATE
:
471 rval
= truncate64(STR(0), NUM(1));
474 rval
= stat64(STR(0), &sb
);
476 show_stats(&sb
, STR(1));
481 rval
= lstat64(STR(0), &sb
);
483 show_stats(&sb
, STR(1));
488 fprintf(stderr
, "unsupported syscall\n");
496 serrno
= err2str(errno
);
497 fprintf(stderr
, "%s returned %d\n", scall
->sd_name
, rval
);
498 printf("%s\n", serrno
);
513 ngroups
= sysconf(_SC_NGROUPS_MAX
);
515 gidset
= malloc(sizeof(*gidset
) * ngroups
);
516 assert(gidset
!= NULL
);
517 for (i
= 0, g
= strtok(gids
, ","); g
!= NULL
; g
= strtok(NULL
, ","), i
++) {
519 fprintf(stderr
, "too many gids\n");
522 gidset
[i
] = strtol(g
, &endp
, 0);
523 if (*endp
!= '\0' && !isspace((unsigned char)*endp
)) {
524 fprintf(stderr
, "invalid gid '%s' - number expected\n",
529 if (setgroups(i
, gidset
) < 0) {
530 fprintf(stderr
, "cannot change groups: %s\n", strerror(errno
));
533 if (setegid(gidset
[0]) < 0) {
534 fprintf(stderr
, "cannot change effective gid: %s\n", strerror(errno
));
541 main(int argc
, char *argv
[])
543 struct syscall_desc
*scall
;
552 while ((ch
= getopt(argc
, argv
, "g:u:U:")) != -1) {
558 uid
= (int)strtol(optarg
, &endp
, 0);
559 if (*endp
!= '\0' && !isspace((unsigned char)*endp
)) {
560 fprintf(stderr
, "invalid uid '%s' - number "
561 "expected\n", optarg
);
566 umsk
= (int)strtol(optarg
, &endp
, 0);
567 if (*endp
!= '\0' && !isspace((unsigned char)*endp
)) {
568 fprintf(stderr
, "invalid umask '%s' - number "
569 "expected\n", optarg
);
581 fprintf(stderr
, "too few arguments\n");
586 fprintf(stderr
, "changing groups to %s\n", gids
);
590 fprintf(stderr
, "changing uid to %d\n", uid
);
591 if (setuid(uid
) < 0) {
592 fprintf(stderr
, "cannot change uid: %s\n",
598 /* Change umask to requested value or to 0, if not requested. */
602 scall
= find_syscall(argv
[0]);
604 fprintf(stderr
, "syscall '%s' not supported\n", argv
[0]);
609 n
= call_syscall(scall
, argv
);
624 static char errnum
[8];
769 return ("EINPROGRESS");
781 return ("EDESTADDRREQ");
789 return ("EPROTOTYPE");
793 return ("ENOPROTOOPT");
795 #ifdef EPROTONOSUPPORT
796 case EPROTONOSUPPORT
:
797 return ("EPROTONOSUPPORT");
799 #ifdef ESOCKTNOSUPPORT
800 case ESOCKTNOSUPPORT
:
801 return ("ESOCKTNOSUPPORT");
805 return ("EOPNOTSUPP");
809 return ("EPFNOSUPPORT");
813 return ("EAFNOSUPPORT");
817 return ("EADDRINUSE");
821 return ("EADDRNOTAVAIL");
829 return ("ENETUNREACH");
833 return ("ENETRESET");
837 return ("ECONNABORTED");
841 return ("ECONNRESET");
857 return ("ESHUTDOWN");
861 return ("ETOOMANYREFS");
865 return ("ETIMEDOUT");
869 return ("ECONNREFUSED");
877 return ("ENAMETOOLONG");
881 return ("EHOSTDOWN");
885 return ("EHOSTUNREACH");
889 return ("ENOTEMPTY");
917 return ("ERPCMISMATCH");
921 return ("EPROGUNAVAIL");
925 return ("EPROGMISMATCH");
929 return ("EPROCUNAVAIL");
949 return ("ENEEDAUTH");
961 return ("EOVERFLOW");
965 return ("ECANCELED");
985 return ("EMULTIHOP");
996 snprintf(errnum
, sizeof(errnum
), "%d", error
);