freebsd-skq/contrib/pjdfstest/pjdfstest.c
ngie 2ceba1b134 MFC r272057,r272083,r272084,r272087,r274016:
r272057:

  Import pjdfstest from ^/vendor/pjdfstest/abf03c3a47745d4521b0e4aa141317553ca48f91

  - Remove tools/regression/pjdfstest
  - Add upgrade directions for contrib/pjdfstest
  - Add a note to UPDATING for the move (the reachover Makefiles are coming
    soon)

  Functional differences:
  - ftruncate testcases are added from upstream (github)

  Non-functional differences:
  - The copyright for the project has been updated to 2012
  - pjd's contact information has been updated

  Discussed with: -testing, jmmv, pjd
  Sponsored by: EMC / Isilon Storage Division

r272083:

  Expect ELOOP on Darwin/Linux with "O_NOFOLLOW was specified and the target is a
  symbolic link" case. Assume EMLINK on the rest of the OSes (FreeBSD, Solaris,
  etc)

  Sponsored by: EMC / Isilon Storage Division

r272084:

  Fix the executed testplan count

  Sponsored by: EMC / Isilon Storage Division

r272087:

  Increase the memory disk size in the following testcases to avoid mount
  failures, which would cause cascade failures in the rest of the test
  run:

  link/15.t, open/19.t, mkdir/11.t, mkfifo/11.t, symlink/11.t

  Fail quickly in all of the testcases if mdconfig, mount, umount, etc
  fails to avoid issues similar to this in the future

  Submitted by: Casey Peel <cpeel@isilon.com>
  Sponsored by: EMC / Isilon Storage Division

r274016:

  Integrate pjdfstest test suite execution into kyua

  pjdfstest execution is opt-in and must be done as root due to some of the
  assumptions made by the test suite and lack of error checking in the non-root
  case

  A description of how to execute pjdfstest with kyua is provided in
  share/pjdfstest/README

  Phabric: D824 (an earlier prototype patch)
  Relnotes: yes
  Sponsored by: EMC / Isilon Storage Division
2014-12-05 11:44:18 +00:00

1494 lines
32 KiB
C

/*-
* Copyright (c) 2006-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#ifndef makedev
#include <sys/mkdev.h>
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef HAS_TRUNCATE64
#define truncate64 truncate
#define ftruncate64 ftruncate
#endif
#ifndef HAS_STAT64
#define stat64 stat
#define fstat64 fstat
#define lstat64 lstat
#endif
#ifdef HAS_FREEBSD_ACL
#include <sys/acl.h>
#endif
#ifndef ALLPERMS
#define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
#endif
enum action {
ACTION_OPEN,
ACTION_OPENAT,
ACTION_CREATE,
ACTION_UNLINK,
ACTION_UNLINKAT,
ACTION_MKDIR,
ACTION_MKDIRAT,
ACTION_RMDIR,
ACTION_LINK,
ACTION_LINKAT,
ACTION_SYMLINK,
ACTION_SYMLINKAT,
ACTION_RENAME,
ACTION_RENAMEAT,
ACTION_MKFIFO,
ACTION_MKFIFOAT,
ACTION_MKNOD,
ACTION_MKNODAT,
ACTION_BIND,
#ifdef HAS_BINDAT
ACTION_BINDAT,
#endif
ACTION_CONNECT,
#ifdef HAS_CONNECTAT
ACTION_CONNECTAT,
#endif
ACTION_CHMOD,
ACTION_FCHMOD,
#ifdef HAS_LCHMOD
ACTION_LCHMOD,
#endif
ACTION_FCHMODAT,
ACTION_CHOWN,
ACTION_FCHOWN,
ACTION_LCHOWN,
ACTION_FCHOWNAT,
#ifdef HAS_CHFLAGS
ACTION_CHFLAGS,
#endif
#ifdef HAS_FCHFLAGS
ACTION_FCHFLAGS,
#endif
#ifdef HAS_CHFLAGSAT
ACTION_CHFLAGSAT,
#endif
#ifdef HAS_LCHFLAGS
ACTION_LCHFLAGS,
#endif
ACTION_TRUNCATE,
ACTION_FTRUNCATE,
ACTION_STAT,
ACTION_FSTAT,
ACTION_LSTAT,
ACTION_FSTATAT,
ACTION_PATHCONF,
ACTION_FPATHCONF,
#ifdef HAS_LPATHCONF
ACTION_LPATHCONF,
#endif
#ifdef HAS_FREEBSD_ACL
ACTION_PREPENDACL,
ACTION_READACL,
#endif
ACTION_WRITE,
};
#define TYPE_NONE 0x0000
#define TYPE_STRING 0x0001
#define TYPE_NUMBER 0x0002
#define TYPE_DESCRIPTOR 0x0003
#define TYPE_MASK 0x000f
#define TYPE_OPTIONAL 0x0100
#define MAX_ARGS 8
struct syscall_desc {
const char *sd_name;
enum action sd_action;
int sd_args[MAX_ARGS];
};
static struct syscall_desc syscalls[] = {
{ "open", ACTION_OPEN, { TYPE_STRING, TYPE_STRING, TYPE_NUMBER | TYPE_OPTIONAL, TYPE_NONE } },
{ "openat", ACTION_OPENAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_NUMBER | TYPE_OPTIONAL, TYPE_NONE } },
{ "create", ACTION_CREATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "unlink", ACTION_UNLINK, { TYPE_STRING, TYPE_NONE } },
{ "unlinkat", ACTION_UNLINKAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "mkdir", ACTION_MKDIR, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "mkdirat", ACTION_MKDIRAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "rmdir", ACTION_RMDIR, { TYPE_STRING, TYPE_NONE } },
{ "link", ACTION_LINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "linkat", ACTION_LINKAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "symlink", ACTION_SYMLINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "symlinkat", ACTION_SYMLINKAT, { TYPE_STRING, TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
{ "rename", ACTION_RENAME, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "renameat", ACTION_RENAMEAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
{ "mkfifo", ACTION_MKFIFO, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "mkfifoat", ACTION_MKFIFOAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "mknod", ACTION_MKNOD, { TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE} },
{ "mknodat", ACTION_MKNODAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE} },
{ "bind", ACTION_BIND, { TYPE_STRING, TYPE_NONE } },
#ifdef HAS_BINDAT
{ "bindat", ACTION_BINDAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
#endif
{ "connect", ACTION_CONNECT, { TYPE_STRING, TYPE_NONE } },
#ifdef HAS_CONNECTAT
{ "connectat", ACTION_CONNECTAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
#endif
{ "chmod", ACTION_CHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "fchmod", ACTION_FCHMOD, { TYPE_DESCRIPTOR, TYPE_NUMBER, TYPE_NONE } },
#ifdef HAS_LCHMOD
{ "lchmod", ACTION_LCHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
#endif
{ "fchmodat", ACTION_FCHMODAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NUMBER, TYPE_STRING, TYPE_NONE } },
{ "chown", ACTION_CHOWN, { TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE } },
{ "fchown", ACTION_FCHOWN, { TYPE_DESCRIPTOR, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE } },
{ "lchown", ACTION_LCHOWN, { TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE } },
{ "fchownat", ACTION_FCHOWNAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_STRING, TYPE_NONE } },
#ifdef HAS_CHFLAGS
{ "chflags", ACTION_CHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
#endif
#ifdef HAS_FCHFLAGS
{ "fchflags", ACTION_FCHFLAGS, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
#endif
#ifdef HAS_CHFLAGSAT
{ "chflagsat", ACTION_CHFLAGSAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_NONE } },
#endif
#ifdef HAS_LCHFLAGS
{ "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
#endif
{ "truncate", ACTION_TRUNCATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "ftruncate", ACTION_FTRUNCATE, { TYPE_DESCRIPTOR, TYPE_NUMBER, TYPE_NONE } },
{ "stat", ACTION_STAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "fstat", ACTION_FSTAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
{ "lstat", ACTION_LSTAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "fstatat", ACTION_FSTATAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "pathconf", ACTION_PATHCONF, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "fpathconf", ACTION_FPATHCONF, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
#ifdef HAS_LPATHCONF
{ "lpathconf", ACTION_LPATHCONF, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
#endif
#ifdef HAS_FREEBSD_ACL
{ "prependacl", ACTION_PREPENDACL, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
{ "readacl", ACTION_READACL, { TYPE_STRING, TYPE_NONE } },
#endif
{ "write", ACTION_WRITE, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
{ NULL, -1, { TYPE_NONE } }
};
struct flag {
long long f_flag;
const char *f_str;
};
static struct flag open_flags[] = {
#ifdef O_RDONLY
{ O_RDONLY, "O_RDONLY" },
#endif
#ifdef O_WRONLY
{ O_WRONLY, "O_WRONLY" },
#endif
#ifdef O_RDWR
{ O_RDWR, "O_RDWR" },
#endif
#ifdef O_NONBLOCK
{ O_NONBLOCK, "O_NONBLOCK" },
#endif
#ifdef O_APPEND
{ O_APPEND, "O_APPEND" },
#endif
#ifdef O_CREAT
{ O_CREAT, "O_CREAT" },
#endif
#ifdef O_TRUNC
{ O_TRUNC, "O_TRUNC" },
#endif
#ifdef O_EXCL
{ O_EXCL, "O_EXCL" },
#endif
#ifdef O_SHLOCK
{ O_SHLOCK, "O_SHLOCK" },
#endif
#ifdef O_EXLOCK
{ O_EXLOCK, "O_EXLOCK" },
#endif
#ifdef O_DIRECT
{ O_DIRECT, "O_DIRECT" },
#endif
#ifdef O_FSYNC
{ O_FSYNC, "O_FSYNC" },
#endif
#ifdef O_SYNC
{ O_SYNC, "O_SYNC" },
#endif
#ifdef O_NOFOLLOW
{ O_NOFOLLOW, "O_NOFOLLOW" },
#endif
#ifdef O_NOCTTY
{ O_NOCTTY, "O_NOCTTY" },
#endif
#ifdef O_DIRECTORY
{ O_DIRECTORY, "O_DIRECTORY" },
#endif
{ 0, NULL }
};
#ifdef HAS_CHFLAGS
static struct flag chflags_flags[] = {
#ifdef UF_NODUMP
{ UF_NODUMP, "UF_NODUMP" },
#endif
#ifdef UF_IMMUTABLE
{ UF_IMMUTABLE, "UF_IMMUTABLE" },
#endif
#ifdef UF_APPEND
{ UF_APPEND, "UF_APPEND" },
#endif
#ifdef UF_NOUNLINK
{ UF_NOUNLINK, "UF_NOUNLINK" },
#endif
#ifdef UF_OPAQUE
{ UF_OPAQUE, "UF_OPAQUE" },
#endif
#ifdef SF_ARCHIVED
{ SF_ARCHIVED, "SF_ARCHIVED" },
#endif
#ifdef SF_IMMUTABLE
{ SF_IMMUTABLE, "SF_IMMUTABLE" },
#endif
#ifdef SF_APPEND
{ SF_APPEND, "SF_APPEND" },
#endif
#ifdef SF_NOUNLINK
{ SF_NOUNLINK, "SF_NOUNLINK" },
#endif
#ifdef SF_SNAPSHOT
{ SF_SNAPSHOT, "SF_SNAPSHOT" },
#endif
{ 0, NULL }
};
#endif
static struct flag unlinkat_flags[] = {
{ AT_REMOVEDIR, "AT_REMOVEDIR" },
{ 0, NULL }
};
static struct flag linkat_flags[] = {
{ AT_SYMLINK_FOLLOW, "AT_SYMLINK_FOLLOW" },
{ 0, NULL }
};
static struct flag chflagsat_flags[] = {
{ AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" },
{ 0, NULL }
};
static struct flag fchmodat_flags[] = {
{ AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" },
{ 0, NULL }
};
static struct flag fchownat_flags[] = {
{ AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" },
{ 0, NULL }
};
static struct flag fstatat_flags[] = {
{ AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" },
{ 0, NULL }
};
struct name {
int n_name;
const char *n_str;
};
static struct name pathconf_names[] = {
#ifdef _PC_LINK_MAX
{ _PC_LINK_MAX, "_PC_LINK_MAX" },
#endif
#ifdef _PC_NAME_MAX
{ _PC_NAME_MAX, "_PC_NAME_MAX" },
#endif
#ifdef _PC_PATH_MAX
{ _PC_PATH_MAX, "_PC_PATH_MAX" },
#endif
#ifdef _PC_SYMLINK_MAX
{ _PC_SYMLINK_MAX, "_PC_SYMLINK_MAX" },
#endif
{ 0, NULL }
};
static const char *err2str(int error);
static int *descriptors;
static int ndescriptors;
static void
usage(void)
{
fprintf(stderr, "usage: pjdfstest [-U umask] [-u uid] [-g gid1[,gid2[...]]] syscall args ...\n");
exit(1);
}
static long long
str2flags(struct flag *tflags, char *sflags)
{
long long flags = 0;
unsigned int i;
char *f;
/* 'none' or '0' means no flags */
if (strcmp(sflags, "none") == 0 || strcmp(sflags, "0") == 0)
return (0);
for (f = strtok(sflags, ",|"); f != NULL; f = strtok(NULL, ",|")) {
for (i = 0; tflags[i].f_str != NULL; i++) {
if (strcmp(tflags[i].f_str, f) == 0)
break;
}
if (tflags[i].f_str == NULL) {
fprintf(stderr, "unknown flag '%s'\n", f);
exit(1);
}
flags |= tflags[i].f_flag;
}
return (flags);
}
#ifdef HAS_CHFLAGS
static char *
flags2str(struct flag *tflags, long long flags)
{
static char sflags[1024];
unsigned int i;
sflags[0] = '\0';
for (i = 0; tflags[i].f_str != NULL; i++) {
if (flags & tflags[i].f_flag) {
if (sflags[0] != '\0')
strlcat(sflags, ",", sizeof(sflags));
strlcat(sflags, tflags[i].f_str, sizeof(sflags));
}
}
if (sflags[0] == '\0')
strlcpy(sflags, "none", sizeof(sflags));
return (sflags);
}
#endif
static int
str2name(struct name *names, char *name)
{
unsigned int i;
for (i = 0; names[i].n_str != NULL; i++) {
if (strcmp(names[i].n_str, name) == 0)
return (names[i].n_name);
}
return (-1);
}
static struct syscall_desc *
find_syscall(const char *name)
{
int i;
for (i = 0; syscalls[i].sd_name != NULL; i++) {
if (strcmp(syscalls[i].sd_name, name) == 0)
return (&syscalls[i]);
}
return (NULL);
}
static void
show_stat(struct stat64 *sp, const char *what)
{
if (strcmp(what, "mode") == 0)
printf("0%o", (unsigned int)(sp->st_mode & ALLPERMS));
else if (strcmp(what, "inode") == 0)
printf("%lld", (long long)sp->st_ino);
else if (strcmp(what, "nlink") == 0)
printf("%lld", (long long)sp->st_nlink);
else if (strcmp(what, "uid") == 0)
printf("%d", (int)sp->st_uid);
else if (strcmp(what, "gid") == 0)
printf("%d", (int)sp->st_gid);
else if (strcmp(what, "size") == 0)
printf("%lld", (long long)sp->st_size);
else if (strcmp(what, "blocks") == 0)
printf("%lld", (long long)sp->st_blocks);
else if (strcmp(what, "atime") == 0)
printf("%lld", (long long)sp->st_atime);
else if (strcmp(what, "mtime") == 0)
printf("%lld", (long long)sp->st_mtime);
else if (strcmp(what, "ctime") == 0)
printf("%lld", (long long)sp->st_ctime);
#ifdef HAS_CHFLAGS
else if (strcmp(what, "flags") == 0)
printf("%s", flags2str(chflags_flags, (long long)sp->st_flags));
#endif
else if (strcmp(what, "major") == 0)
printf("%u", (unsigned int)major(sp->st_rdev));
else if (strcmp(what, "minor") == 0)
printf("%u", (unsigned int)minor(sp->st_rdev));
else if (strcmp(what, "type") == 0) {
switch (sp->st_mode & S_IFMT) {
case S_IFIFO:
printf("fifo");
break;
case S_IFCHR:
printf("char");
break;
case S_IFDIR:
printf("dir");
break;
case S_IFBLK:
printf("block");
break;
case S_IFREG:
printf("regular");
break;
case S_IFLNK:
printf("symlink");
break;
case S_IFSOCK:
printf("socket");
break;
default:
printf("unknown");
break;
}
} else {
printf("unknown");
}
}
static void
show_stats(struct stat64 *sp, char *what)
{
const char *s = "";
char *w;
for (w = strtok(what, ","); w != NULL; w = strtok(NULL, ",")) {
printf("%s", s);
show_stat(sp, w);
s = ",";
}
printf("\n");
}
static void
descriptor_add(int fd)
{
ndescriptors++;
if (descriptors == NULL) {
descriptors = malloc(sizeof(descriptors[0]) * ndescriptors);
} else {
descriptors = realloc(descriptors,
sizeof(descriptors[0]) * ndescriptors);
}
assert(descriptors != NULL);
descriptors[ndescriptors - 1] = fd;
}
static int
descriptor_get(int pos)
{
if (pos < 0 || pos >= ndescriptors) {
fprintf(stderr, "invalid descriptor %d\n", pos);
exit(1);
}
return (descriptors[pos]);
}
static unsigned int
call_syscall(struct syscall_desc *scall, char *argv[])
{
struct stat64 sb;
long long flags;
unsigned int i;
char *endp;
int name, rval;
union {
char *str;
long long num;
} args[MAX_ARGS];
#ifdef HAS_FREEBSD_ACL
int entry_id = ACL_FIRST_ENTRY;
acl_t acl, newacl;
acl_entry_t entry, newentry;
#endif
/*
* Verify correctness of the arguments.
*/
for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) {
if (scall->sd_args[i] == TYPE_NONE) {
if (argv[i] == NULL || strcmp(argv[i], ":") == 0)
break;
fprintf(stderr, "too many arguments [%s]\n", argv[i]);
exit(1);
} else {
if (argv[i] == NULL || strcmp(argv[i], ":") == 0) {
if (scall->sd_args[i] & TYPE_OPTIONAL)
break;
fprintf(stderr, "too few arguments\n");
exit(1);
}
if ((scall->sd_args[i] & TYPE_MASK) == TYPE_STRING) {
if (strcmp(argv[i], "NULL") == 0)
args[i].str = NULL;
else if (strcmp(argv[i], "DEADCODE") == 0)
args[i].str = (void *)0xdeadc0de;
else
args[i].str = argv[i];
} else if ((scall->sd_args[i] & TYPE_MASK) ==
TYPE_NUMBER) {
args[i].num = strtoll(argv[i], &endp, 0);
if (*endp != '\0' &&
!isspace((unsigned char)*endp)) {
fprintf(stderr,
"invalid argument %u, number expected [%s]\n",
i, endp);
exit(1);
}
} else if ((scall->sd_args[i] & TYPE_MASK) ==
TYPE_DESCRIPTOR) {
if (strcmp(argv[i], "AT_FDCWD") == 0) {
args[i].num = AT_FDCWD;
} else if (strcmp(argv[i], "BADFD") == 0) {
/* In case AT_FDCWD is -1 on some systems... */
if (AT_FDCWD == -1)
args[i].num = -2;
else
args[i].num = -1;
} else {
int pos;
pos = strtoll(argv[i], &endp, 0);
if (*endp != '\0' &&
!isspace((unsigned char)*endp)) {
fprintf(stderr,
"invalid argument %u, number expected [%s]\n",
i, endp);
exit(1);
}
args[i].num = descriptor_get(pos);
}
}
}
}
/*
* Call the given syscall.
*/
#define NUM(n) (args[(n)].num)
#define STR(n) (args[(n)].str)
switch (scall->sd_action) {
case ACTION_OPEN:
flags = str2flags(open_flags, STR(1));
if (flags & O_CREAT) {
if (i == 2) {
fprintf(stderr, "too few arguments\n");
exit(1);
}
rval = open(STR(0), (int)flags, (mode_t)NUM(2));
} else {
if (i == 3) {
fprintf(stderr, "too many arguments\n");
exit(1);
}
rval = open(STR(0), (int)flags);
}
if (rval >= 0)
descriptor_add(rval);
break;
case ACTION_OPENAT:
flags = str2flags(open_flags, STR(2));
if (flags & O_CREAT) {
if (i == 3) {
fprintf(stderr, "too few arguments\n");
exit(1);
}
rval = openat(NUM(0), STR(1), (int)flags,
(mode_t)NUM(3));
} else {
if (i == 4) {
fprintf(stderr, "too many arguments\n");
exit(1);
}
rval = openat(NUM(0), STR(1), (int)flags);
}
if (rval >= 0)
descriptor_add(rval);
break;
case ACTION_CREATE:
rval = open(STR(0), O_CREAT | O_EXCL, (mode_t)NUM(1));
if (rval >= 0)
close(rval);
break;
case ACTION_UNLINK:
rval = unlink(STR(0));
break;
case ACTION_UNLINKAT:
rval = unlinkat(NUM(0), STR(1),
(int)str2flags(unlinkat_flags, STR(2)));
break;
case ACTION_MKDIR:
rval = mkdir(STR(0), (mode_t)NUM(1));
break;
case ACTION_MKDIRAT:
rval = mkdirat(NUM(0), STR(1), (mode_t)NUM(2));
break;
case ACTION_RMDIR:
rval = rmdir(STR(0));
break;
case ACTION_LINK:
rval = link(STR(0), STR(1));
break;
case ACTION_LINKAT:
rval = linkat(NUM(0), STR(1), NUM(2), STR(3),
(int)str2flags(linkat_flags, STR(4)));
break;
case ACTION_SYMLINK:
rval = symlink(STR(0), STR(1));
break;
case ACTION_SYMLINKAT:
rval = symlinkat(STR(0), NUM(1), STR(2));
break;
case ACTION_RENAME:
rval = rename(STR(0), STR(1));
break;
case ACTION_RENAMEAT:
rval = renameat(NUM(0), STR(1), NUM(2), STR(3));
break;
case ACTION_MKFIFO:
rval = mkfifo(STR(0), (mode_t)NUM(1));
break;
case ACTION_MKFIFOAT:
rval = mkfifoat(NUM(0), STR(1), (mode_t)NUM(2));
break;
case ACTION_MKNOD:
case ACTION_MKNODAT:
{
mode_t ntype;
dev_t dev;
int fa;
switch (scall->sd_action) {
case ACTION_MKNOD:
fa = 0;
break;
case ACTION_MKNODAT:
fa = 1;
break;
default:
abort();
}
dev = makedev(NUM(fa + 3), NUM(fa + 4));
if (strcmp(STR(fa + 1), "c") == 0) /* character device */
ntype = S_IFCHR;
else if (strcmp(STR(fa + 1), "b") == 0) /* block device */
ntype = S_IFBLK;
else if (strcmp(STR(fa + 1), "f") == 0) /* fifo special */
ntype = S_IFIFO;
else if (strcmp(STR(fa + 1), "d") == 0) /* directory */
ntype = S_IFDIR;
else if (strcmp(STR(fa + 1), "o") == 0) /* regular file */
ntype = S_IFREG;
else {
fprintf(stderr, "wrong argument 1\n");
exit(1);
}
switch (scall->sd_action) {
case ACTION_MKNOD:
rval = mknod(STR(0), ntype | NUM(2), dev);
break;
case ACTION_MKNODAT:
rval = mknodat(NUM(0), STR(1), ntype | NUM(3), dev);
break;
default:
abort();
}
break;
}
case ACTION_BIND:
{
struct sockaddr_un sunx;
sunx.sun_family = AF_UNIX;
strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1);
sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
rval = socket(AF_UNIX, SOCK_STREAM, 0);
if (rval < 0)
break;
rval = bind(rval, (struct sockaddr *)&sunx, sizeof(sunx));
break;
}
#ifdef HAS_BINDAT
case ACTION_BINDAT:
{
struct sockaddr_un sunx;
sunx.sun_family = AF_UNIX;
strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1);
sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
rval = socket(AF_UNIX, SOCK_STREAM, 0);
if (rval < 0)
break;
rval = bindat(NUM(0), rval, (struct sockaddr *)&sunx,
sizeof(sunx));
break;
}
#endif
case ACTION_CONNECT:
{
struct sockaddr_un sunx;
sunx.sun_family = AF_UNIX;
strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1);
sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
rval = socket(AF_UNIX, SOCK_STREAM, 0);
if (rval < 0)
break;
rval = connect(rval, (struct sockaddr *)&sunx, sizeof(sunx));
break;
}
#ifdef HAS_CONNECTAT
case ACTION_CONNECTAT:
{
struct sockaddr_un sunx;
sunx.sun_family = AF_UNIX;
strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1);
sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
rval = socket(AF_UNIX, SOCK_STREAM, 0);
if (rval < 0)
break;
rval = connectat(NUM(0), rval, (struct sockaddr *)&sunx,
sizeof(sunx));
break;
}
#endif
case ACTION_CHMOD:
rval = chmod(STR(0), (mode_t)NUM(1));
break;
case ACTION_FCHMOD:
rval = fchmod(NUM(0), (mode_t)NUM(1));
break;
#ifdef HAS_LCHMOD
case ACTION_LCHMOD:
rval = lchmod(STR(0), (mode_t)NUM(1));
break;
#endif
case ACTION_FCHMODAT:
rval = fchmodat(NUM(0), STR(1), (mode_t)NUM(2),
str2flags(fchmodat_flags, STR(3)));
break;
case ACTION_CHOWN:
rval = chown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
break;
case ACTION_FCHOWN:
rval = fchown(NUM(0), (uid_t)NUM(1), (gid_t)NUM(2));
break;
case ACTION_LCHOWN:
rval = lchown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2));
break;
case ACTION_FCHOWNAT:
rval = fchownat(NUM(0), STR(1), (uid_t)NUM(2), (gid_t)NUM(3),
(int)str2flags(fchownat_flags, STR(4)));
break;
#ifdef HAS_CHFLAGS
case ACTION_CHFLAGS:
rval = chflags(STR(0),
(unsigned long)str2flags(chflags_flags, STR(1)));
break;
#endif
#ifdef HAS_FCHFLAGS
case ACTION_FCHFLAGS:
rval = fchflags(NUM(0),
(unsigned long)str2flags(chflags_flags, STR(1)));
break;
#endif
#ifdef HAS_CHFLAGSAT
case ACTION_CHFLAGSAT:
rval = chflagsat(NUM(0), STR(1),
(unsigned long)str2flags(chflags_flags, STR(2)),
(int)str2flags(chflagsat_flags, STR(3)));
break;
#endif
#ifdef HAS_LCHFLAGS
case ACTION_LCHFLAGS:
rval = lchflags(STR(0),
(unsigned long)str2flags(chflags_flags, STR(1)));
break;
#endif
case ACTION_TRUNCATE:
rval = truncate64(STR(0), NUM(1));
break;
case ACTION_FTRUNCATE:
rval = ftruncate64(NUM(0), NUM(1));
break;
case ACTION_STAT:
rval = stat64(STR(0), &sb);
if (rval == 0) {
show_stats(&sb, STR(1));
return (i);
}
break;
case ACTION_FSTAT:
rval = fstat64(NUM(0), &sb);
if (rval == 0) {
show_stats(&sb, STR(1));
return (i);
}
break;
case ACTION_LSTAT:
rval = lstat64(STR(0), &sb);
if (rval == 0) {
show_stats(&sb, STR(1));
return (i);
}
break;
case ACTION_FSTATAT:
rval = fstatat(NUM(0), STR(1), &sb,
(int)str2flags(fstatat_flags, STR(2)));
if (rval == 0) {
show_stats(&sb, STR(3));
return (i);
}
break;
case ACTION_PATHCONF:
case ACTION_FPATHCONF:
#ifdef HAS_LPATHCONF
case ACTION_LPATHCONF:
#endif
{
long lrval;
name = str2name(pathconf_names, STR(1));
if (name == -1) {
fprintf(stderr, "unknown name %s", STR(1));
exit(1);
}
errno = 0;
switch (scall->sd_action) {
case ACTION_PATHCONF:
lrval = pathconf(STR(0), name);
break;
case ACTION_FPATHCONF:
lrval = fpathconf(NUM(0), name);
break;
#ifdef HAS_LPATHCONF
case ACTION_LPATHCONF:
lrval = lpathconf(STR(0), name);
break;
#endif
default:
abort();
}
if (lrval == -1 && errno == 0) {
printf("unlimited\n");
return (i);
} else if (lrval >= 0) {
printf("%ld\n", lrval);
return (i);
}
rval = -1;
break;
}
#ifdef HAS_FREEBSD_ACL
case ACTION_PREPENDACL:
rval = -1;
acl = acl_get_file(STR(0), ACL_TYPE_NFS4);
if (acl == NULL)
break;
newacl = acl_from_text(STR(1));
if (acl == NULL)
break;
while (acl_get_entry(newacl, entry_id, &newentry) == 1) {
entry_id = ACL_NEXT_ENTRY;
if (acl_create_entry_np(&acl, &entry, 0))
break;
if (acl_copy_entry(entry, newentry))
break;
}
rval = acl_set_file(STR(0), ACL_TYPE_NFS4, acl);
break;
case ACTION_READACL:
acl = acl_get_file(STR(0), ACL_TYPE_NFS4);
if (acl == NULL)
rval = -1;
else
rval = 0;
break;
#endif
case ACTION_WRITE:
rval = write(NUM(0), STR(1), strlen(STR(1)));
break;
default:
fprintf(stderr, "unsupported syscall\n");
exit(1);
}
#undef STR
#undef NUM
if (rval < 0) {
const char *serrno;
serrno = err2str(errno);
fprintf(stderr, "%s returned %d\n", scall->sd_name, rval);
printf("%s\n", serrno);
exit(1);
}
printf("0\n");
return (i);
}
static void
set_gids(char *gids)
{
gid_t *gidset;
long ngroups;
char *g, *endp;
unsigned i;
ngroups = sysconf(_SC_NGROUPS_MAX);
assert(ngroups > 0);
gidset = malloc(sizeof(*gidset) * ngroups);
assert(gidset != NULL);
for (i = 0, g = strtok(gids, ","); g != NULL;
g = strtok(NULL, ","), i++) {
if (i >= ngroups) {
fprintf(stderr, "too many gids\n");
exit(1);
}
gidset[i] = strtol(g, &endp, 0);
if (*endp != '\0' && !isspace((unsigned char)*endp)) {
fprintf(stderr, "invalid gid '%s' - number expected\n",
g);
exit(1);
}
}
if (setgroups(i, gidset) < 0) {
fprintf(stderr, "cannot change groups: %s\n", strerror(errno));
exit(1);
}
if (setegid(gidset[0]) < 0) {
fprintf(stderr, "cannot change effective gid: %s\n",
strerror(errno));
exit(1);
}
free(gidset);
}
int
main(int argc, char *argv[])
{
struct syscall_desc *scall;
unsigned int n;
char *gids, *endp;
int uid, umsk, ch;
uid = -1;
gids = NULL;
umsk = 0;
while ((ch = getopt(argc, argv, "g:u:U:")) != -1) {
switch(ch) {
case 'g':
gids = optarg;
break;
case 'u':
uid = (int)strtol(optarg, &endp, 0);
if (*endp != '\0' && !isspace((unsigned char)*endp)) {
fprintf(stderr, "invalid uid '%s' - number "
"expected\n", optarg);
exit(1);
}
break;
case 'U':
umsk = (int)strtol(optarg, &endp, 0);
if (*endp != '\0' && !isspace((unsigned char)*endp)) {
fprintf(stderr, "invalid umask '%s' - number "
"expected\n", optarg);
exit(1);
}
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
fprintf(stderr, "too few arguments\n");
usage();
}
if (gids != NULL) {
fprintf(stderr, "changing groups to %s\n", gids);
set_gids(gids);
}
if (uid != -1) {
fprintf(stderr, "changing uid to %d\n", uid);
if (setuid(uid) < 0) {
fprintf(stderr, "cannot change uid: %s\n",
strerror(errno));
exit(1);
}
}
/* Change umask to requested value or to 0, if not requested. */
umask(umsk);
for (;;) {
scall = find_syscall(argv[0]);
if (scall == NULL) {
fprintf(stderr, "syscall '%s' not supported\n",
argv[0]);
exit(1);
}
argc++;
argv++;
n = call_syscall(scall, argv);
argc += n;
argv += n;
if (argv[0] == NULL)
break;
argc++;
argv++;
}
exit(0);
}
static const char *
err2str(int error)
{
static char errnum[8];
switch (error) {
#ifdef EPERM
case EPERM:
return ("EPERM");
#endif
#ifdef ENOENT
case ENOENT:
return ("ENOENT");
#endif
#ifdef ESRCH
case ESRCH:
return ("ESRCH");
#endif
#ifdef EINTR
case EINTR:
return ("EINTR");
#endif
#ifdef EIO
case EIO:
return ("EIO");
#endif
#ifdef ENXIO
case ENXIO:
return ("ENXIO");
#endif
#ifdef E2BIG
case E2BIG:
return ("E2BIG");
#endif
#ifdef ENOEXEC
case ENOEXEC:
return ("ENOEXEC");
#endif
#ifdef EBADF
case EBADF:
return ("EBADF");
#endif
#ifdef ECHILD
case ECHILD:
return ("ECHILD");
#endif
#ifdef EDEADLK
case EDEADLK:
return ("EDEADLK");
#endif
#ifdef ENOMEM
case ENOMEM:
return ("ENOMEM");
#endif
#ifdef EACCES
case EACCES:
return ("EACCES");
#endif
#ifdef EFAULT
case EFAULT:
return ("EFAULT");
#endif
#ifdef ENOTBLK
case ENOTBLK:
return ("ENOTBLK");
#endif
#ifdef EBUSY
case EBUSY:
return ("EBUSY");
#endif
#ifdef EEXIST
case EEXIST:
return ("EEXIST");
#endif
#ifdef EXDEV
case EXDEV:
return ("EXDEV");
#endif
#ifdef ENODEV
case ENODEV:
return ("ENODEV");
#endif
#ifdef ENOTDIR
case ENOTDIR:
return ("ENOTDIR");
#endif
#ifdef EISDIR
case EISDIR:
return ("EISDIR");
#endif
#ifdef EINVAL
case EINVAL:
return ("EINVAL");
#endif
#ifdef ENFILE
case ENFILE:
return ("ENFILE");
#endif
#ifdef EMFILE
case EMFILE:
return ("EMFILE");
#endif
#ifdef ENOTTY
case ENOTTY:
return ("ENOTTY");
#endif
#ifdef ETXTBSY
case ETXTBSY:
return ("ETXTBSY");
#endif
#ifdef EFBIG
case EFBIG:
return ("EFBIG");
#endif
#ifdef ENOSPC
case ENOSPC:
return ("ENOSPC");
#endif
#ifdef ESPIPE
case ESPIPE:
return ("ESPIPE");
#endif
#ifdef EROFS
case EROFS:
return ("EROFS");
#endif
#ifdef EMLINK
case EMLINK:
return ("EMLINK");
#endif
#ifdef EPIPE
case EPIPE:
return ("EPIPE");
#endif
#ifdef EDOM
case EDOM:
return ("EDOM");
#endif
#ifdef ERANGE
case ERANGE:
return ("ERANGE");
#endif
#ifdef EAGAIN
case EAGAIN:
return ("EAGAIN");
#endif
#ifdef EINPROGRESS
case EINPROGRESS:
return ("EINPROGRESS");
#endif
#ifdef EALREADY
case EALREADY:
return ("EALREADY");
#endif
#ifdef ENOTSOCK
case ENOTSOCK:
return ("ENOTSOCK");
#endif
#ifdef EDESTADDRREQ
case EDESTADDRREQ:
return ("EDESTADDRREQ");
#endif
#ifdef EMSGSIZE
case EMSGSIZE:
return ("EMSGSIZE");
#endif
#ifdef EPROTOTYPE
case EPROTOTYPE:
return ("EPROTOTYPE");
#endif
#ifdef ENOPROTOOPT
case ENOPROTOOPT:
return ("ENOPROTOOPT");
#endif
#ifdef EPROTONOSUPPORT
case EPROTONOSUPPORT:
return ("EPROTONOSUPPORT");
#endif
#ifdef ESOCKTNOSUPPORT
case ESOCKTNOSUPPORT:
return ("ESOCKTNOSUPPORT");
#endif
#ifdef EOPNOTSUPP
case EOPNOTSUPP:
return ("EOPNOTSUPP");
#endif
#ifdef EPFNOSUPPORT
case EPFNOSUPPORT:
return ("EPFNOSUPPORT");
#endif
#ifdef EAFNOSUPPORT
case EAFNOSUPPORT:
return ("EAFNOSUPPORT");
#endif
#ifdef EADDRINUSE
case EADDRINUSE:
return ("EADDRINUSE");
#endif
#ifdef EADDRNOTAVAIL
case EADDRNOTAVAIL:
return ("EADDRNOTAVAIL");
#endif
#ifdef ENETDOWN
case ENETDOWN:
return ("ENETDOWN");
#endif
#ifdef ENETUNREACH
case ENETUNREACH:
return ("ENETUNREACH");
#endif
#ifdef ENETRESET
case ENETRESET:
return ("ENETRESET");
#endif
#ifdef ECONNABORTED
case ECONNABORTED:
return ("ECONNABORTED");
#endif
#ifdef ECONNRESET
case ECONNRESET:
return ("ECONNRESET");
#endif
#ifdef ENOBUFS
case ENOBUFS:
return ("ENOBUFS");
#endif
#ifdef EISCONN
case EISCONN:
return ("EISCONN");
#endif
#ifdef ENOTCONN
case ENOTCONN:
return ("ENOTCONN");
#endif
#ifdef ESHUTDOWN
case ESHUTDOWN:
return ("ESHUTDOWN");
#endif
#ifdef ETOOMANYREFS
case ETOOMANYREFS:
return ("ETOOMANYREFS");
#endif
#ifdef ETIMEDOUT
case ETIMEDOUT:
return ("ETIMEDOUT");
#endif
#ifdef ECONNREFUSED
case ECONNREFUSED:
return ("ECONNREFUSED");
#endif
#ifdef ELOOP
case ELOOP:
return ("ELOOP");
#endif
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
return ("ENAMETOOLONG");
#endif
#ifdef EHOSTDOWN
case EHOSTDOWN:
return ("EHOSTDOWN");
#endif
#ifdef EHOSTUNREACH
case EHOSTUNREACH:
return ("EHOSTUNREACH");
#endif
#ifdef ENOTEMPTY
case ENOTEMPTY:
return ("ENOTEMPTY");
#endif
#ifdef EPROCLIM
case EPROCLIM:
return ("EPROCLIM");
#endif
#ifdef EUSERS
case EUSERS:
return ("EUSERS");
#endif
#ifdef EDQUOT
case EDQUOT:
return ("EDQUOT");
#endif
#ifdef ESTALE
case ESTALE:
return ("ESTALE");
#endif
#ifdef EREMOTE
case EREMOTE:
return ("EREMOTE");
#endif
#ifdef EBADRPC
case EBADRPC:
return ("EBADRPC");
#endif
#ifdef ERPCMISMATCH
case ERPCMISMATCH:
return ("ERPCMISMATCH");
#endif
#ifdef EPROGUNAVAIL
case EPROGUNAVAIL:
return ("EPROGUNAVAIL");
#endif
#ifdef EPROGMISMATCH
case EPROGMISMATCH:
return ("EPROGMISMATCH");
#endif
#ifdef EPROCUNAVAIL
case EPROCUNAVAIL:
return ("EPROCUNAVAIL");
#endif
#ifdef ENOLCK
case ENOLCK:
return ("ENOLCK");
#endif
#ifdef ENOSYS
case ENOSYS:
return ("ENOSYS");
#endif
#ifdef EFTYPE
case EFTYPE:
return ("EFTYPE");
#endif
#ifdef EAUTH
case EAUTH:
return ("EAUTH");
#endif
#ifdef ENEEDAUTH
case ENEEDAUTH:
return ("ENEEDAUTH");
#endif
#ifdef EIDRM
case EIDRM:
return ("EIDRM");
#endif
#ifdef ENOMSG
case ENOMSG:
return ("ENOMSG");
#endif
#ifdef EOVERFLOW
case EOVERFLOW:
return ("EOVERFLOW");
#endif
#ifdef ECANCELED
case ECANCELED:
return ("ECANCELED");
#endif
#ifdef EILSEQ
case EILSEQ:
return ("EILSEQ");
#endif
#ifdef ENOATTR
case ENOATTR:
return ("ENOATTR");
#endif
#ifdef EDOOFUS
case EDOOFUS:
return ("EDOOFUS");
#endif
#ifdef EBADMSG
case EBADMSG:
return ("EBADMSG");
#endif
#ifdef EMULTIHOP
case EMULTIHOP:
return ("EMULTIHOP");
#endif
#ifdef ENOLINK
case ENOLINK:
return ("ENOLINK");
#endif
#ifdef EPROTO
case EPROTO:
return ("EPROTO");
#endif
default:
snprintf(errnum, sizeof(errnum), "%d", error);
return (errnum);
}
}