aboutsummaryrefslogtreecommitdiffstats
path: root/guts
diff options
context:
space:
mode:
Diffstat (limited to 'guts')
-rw-r--r--guts/COPYRIGHT17
-rw-r--r--guts/README109
-rw-r--r--guts/__fxstat.c17
-rw-r--r--guts/__fxstat64.c28
-rw-r--r--guts/__fxstatat.c29
-rw-r--r--guts/__fxstatat64.c71
-rw-r--r--guts/__lxstat.c11
-rw-r--r--guts/__lxstat64.c11
-rw-r--r--guts/__openat64_2.c11
-rw-r--r--guts/__openat_2.c11
-rw-r--r--guts/__xmknod.c11
-rw-r--r--guts/__xmknodat.c68
-rw-r--r--guts/__xstat.c11
-rw-r--r--guts/__xstat64.c10
-rw-r--r--guts/chdir.c15
-rw-r--r--guts/chmod.c11
-rw-r--r--guts/chown.c11
-rw-r--r--guts/close.c14
-rw-r--r--guts/creat.c11
-rw-r--r--guts/creat64.c11
-rw-r--r--guts/dup.c16
-rw-r--r--guts/dup2.c19
-rw-r--r--guts/fchdir.c15
-rw-r--r--guts/fchmod.c30
-rw-r--r--guts/fchmodat.c70
-rw-r--r--guts/fchown.c54
-rw-r--r--guts/fchownat.c64
-rw-r--r--guts/fclose.c17
-rw-r--r--guts/fcntl.c68
-rw-r--r--guts/fopen.c32
-rw-r--r--guts/fopen64.c32
-rw-r--r--guts/fork.c13
-rw-r--r--guts/freopen.c32
-rw-r--r--guts/getegid.c11
-rw-r--r--guts/geteuid.c11
-rw-r--r--guts/getgid.c11
-rw-r--r--guts/getresgid.c20
-rw-r--r--guts/getresuid.c20
-rw-r--r--guts/getuid.c11
-rw-r--r--guts/lchown.c48
-rw-r--r--guts/link.c29
-rw-r--r--guts/mkdir.c11
-rw-r--r--guts/mkdirat.c34
-rw-r--r--guts/mkstemp.c25
-rw-r--r--guts/open.c11
-rw-r--r--guts/open64.c11
-rw-r--r--guts/openat.c64
-rw-r--r--guts/openat64.c11
-rw-r--r--guts/rename.c77
-rw-r--r--guts/renameat.c12
-rw-r--r--guts/rmdir.c22
-rw-r--r--guts/setegid.c17
-rw-r--r--guts/seteuid.c17
-rw-r--r--guts/setfsgid.c16
-rw-r--r--guts/setfsuid.c16
-rw-r--r--guts/setgid.c24
-rw-r--r--guts/setgroups.c12
-rw-r--r--guts/setregid.c27
-rw-r--r--guts/setresgid.c34
-rw-r--r--guts/setresuid.c34
-rw-r--r--guts/setreuid.c27
-rw-r--r--guts/setuid.c24
-rw-r--r--guts/symlink.c11
-rw-r--r--guts/symlinkat.c38
-rw-r--r--guts/unlink.c11
-rw-r--r--guts/unlinkat.c44
-rw-r--r--guts/vfork.c14
67 files changed, 1755 insertions, 0 deletions
diff --git a/guts/COPYRIGHT b/guts/COPYRIGHT
new file mode 100644
index 0000000..c96e1b1
--- /dev/null
+++ b/guts/COPYRIGHT
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2008-2010 Wind River Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Lesser GNU General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the Lesser GNU General Public License for more details.
+ *
+ * You should have received a copy of the Lesser GNU General Public License
+ * version 2.1 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
diff --git a/guts/README b/guts/README
new file mode 100644
index 0000000..be00d30
--- /dev/null
+++ b/guts/README
@@ -0,0 +1,109 @@
+The files in this directory are partially machine-generated, and are
+all covered by the COPYRIGHT file in this directory.
+
+The set of functions covered here may seem surprising. For instance,
+obviously, fopen(3) simply calls the underlying open(2) syscall. But...
+There is a problem. In a few places in glibc, the syscalls are inlined
+such that there is no actual call to the C function open(2), just a raw
+call. So there are a couple of functions (fopen, freopen) which are
+wrapped with intent only to detect the possible creation of files.
+
+Many of these functions are closely related. Some programs may have
+calls to openat(), while others have calls to __openat_2(). To reduce
+code duplication, a number of functions are implemented purely as calls
+to other functions.
+
+When a *at() function exists, the regular function is implemented
+as *at() with AT_FDCWD as the directory fd (see the dummy #define of
+this in pseudo_client.h, used for systems which lack these.) On systems
+where AT_NOFOLLOW_SYMLINKS is not defined, the underlying *at() functions
+don't exist, so we provide a bare implementation which works only when
+the fd is AT_FDCWD...
+
+The creat64 and open64 families are equivalent to the plain versions with
+O_LARGEFILE in mode bits. (Again, there's a suitable dummy #define
+in pseudo_client.h.) By contrast, the stat64 functions actually do have
+some difference -- the structure they manipulate is not the same.
+
+The following table shows which functions are merely wrappers around
+other functions:
+
+ chmod: fchmodat
+ chown: fchownat
+ creat64: openat
+ creat: openat
+ __fxstatat: __fxstatat64
+ __fxstat: __fxstat64
+ __lxstat64: __fxstatat64
+ __lxstat: __fxstatat
+ mkdir: mkdirat
+ open64: openat
+ __openat_2: openat
+ __openat64_2: openat
+ openat64: openat
+ open: openat
+ rename: renameat
+ symlink: symlinkat
+ unlink: unlinkat
+ __xmknod: __xmknodat
+ __xstat64: __fxstatat64
+ __xstat: __fxstatat
+
+The following functions are full implementations:
+
+ chdir
+ fchdir
+ fchmod
+ fchmodat
+ fchown
+ fchownat
+ __fxstat64
+ __fxstatat64
+ lchown
+ mkdirat
+ openat
+ renameat
+ rmdir
+ symlinkat
+ unlinkat
+ __xmknodat
+
+The following functions provide only partial implementations, to trap special
+cases, to track internal data structures (for instance, close() is tracked so
+that the path to a file descriptor can be dropped when the file descriptor
+is closed), or to handle functions which may not use the underlying syscall
+wrappers:
+
+ close
+ dup
+ dup2
+ fclose
+ fopen
+ fopen64
+ freopen
+ mkstemp
+ fcntl
+ fork
+ link
+ vfork
+
+The following functions don't have any direct database interactions,
+but are used to simulate the permissions system:
+
+ getegid
+ getuid
+ setgid
+ setreuid
+ geteuid
+ setegid
+ setgroups
+ setuid
+ getgid
+ seteuid
+ setregid
+ getresgid
+ setfsgid
+ setresgid
+ getresuid
+ setfsuid
+ setresuid
diff --git a/guts/__fxstat.c b/guts/__fxstat.c
new file mode 100644
index 0000000..e715f6c
--- /dev/null
+++ b/guts/__fxstat.c
@@ -0,0 +1,17 @@
+/*
+ * int
+ * wrap___fxstat(int ver, int fd, struct stat *buf) {
+ * int rc = -1;
+ */
+
+ struct stat64 buf64;
+ /* populate buffer with complete data */
+ real___fxstat(ver, fd, buf);
+ /* obtain fake data */
+ rc = wrap___fxstat64(ver, fd, &buf64);
+ /* overwrite */
+ pseudo_stat32_from64(buf, &buf64);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__fxstat64.c b/guts/__fxstat64.c
new file mode 100644
index 0000000..59f8a59
--- /dev/null
+++ b/guts/__fxstat64.c
@@ -0,0 +1,28 @@
+/*
+ * int
+ * wrap___fxstat64(int ver, int fd, struct stat64 *buf) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ int save_errno;
+
+ rc = real___fxstat64(ver, fd, buf);
+ save_errno = errno;
+ if (rc == -1) {
+ return rc;
+ }
+ if (ver != _STAT_VER) {
+ pseudo_debug(1, "version mismatch: got stat version %d, only supporting %d\n", ver, _STAT_VER);
+ errno = save_errno;
+ return rc;
+ }
+ msg = pseudo_client_op(OP_FSTAT, 0, fd, -1, 0, buf);
+ if (msg) {
+ if (msg->result == RESULT_SUCCEED)
+ pseudo_stat_msg(buf, msg);
+ }
+
+ errno = save_errno;
+/* return rc;
+ * }
+ */
diff --git a/guts/__fxstatat.c b/guts/__fxstatat.c
new file mode 100644
index 0000000..4bc2454
--- /dev/null
+++ b/guts/__fxstatat.c
@@ -0,0 +1,29 @@
+/*
+ * static int
+ * wrap___fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags) {
+ * int rc = -1;
+ */
+
+ struct stat64 buf64;
+ /* populate buffer with complete data */
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (flags & AT_SYMLINK_NOFOLLOW) {
+ rc = real___lxstat(ver, path, buf);
+ } else {
+ rc = real___xstat(ver, path, buf);
+ }
+#else
+ real___fxstatat(ver, dirfd, path, buf, flags);
+#endif
+ /* obtain fake data */
+ rc = wrap___fxstatat64(ver, dirfd, path, &buf64, flags);
+ /* overwrite */
+ pseudo_stat32_from64(buf, &buf64);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__fxstatat64.c b/guts/__fxstatat64.c
new file mode 100644
index 0000000..3edd8c7
--- /dev/null
+++ b/guts/__fxstatat64.c
@@ -0,0 +1,71 @@
+/*
+ * static int
+ * wrap___fxstatat64(int ver, int dirfd, const char *path, struct stat64 *buf, int flags) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ int save_errno;
+ mode_t save_mode = 0;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+#endif
+ /* If the file is actually a symlink, we grab the db values
+ * for the underlying target, then mask in the size and mode
+ * from the link. Otherwise, we just use the db values (if
+ * any).
+ */
+ if (flags & AT_SYMLINK_NOFOLLOW) {
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real___lxstat64(ver, path, buf);
+#else
+ rc = real___fxstatat64(ver, dirfd, path, buf, flags);
+#endif
+ if (rc == -1) {
+ return rc;
+ }
+ /* it's a symlink, stash its mode */
+ if (S_ISLNK(buf->st_mode)) {
+ save_mode = buf->st_mode;
+ }
+ } else {
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real___xstat64(ver, path, buf);
+#else
+ rc = real___fxstatat64(ver, dirfd, path, buf, flags);
+#endif
+ if (rc == -1) {
+ return rc;
+ }
+ }
+ /* if we got here: we have valid stat data, for either the symlink
+ * (if it is a symlink, and we have NOFOLLOW on) or the target.
+ */
+ save_errno = errno;
+
+ if (ver != _STAT_VER) {
+ pseudo_debug(1, "version mismatch: got stat version %d, only supporting %d\n", ver, _STAT_VER);
+ errno = save_errno;
+ return rc;
+ }
+
+ /* query database
+ * note that symlink canonicalizing is now automatic, so we
+ * don't need to check for a symlink on this end
+ */
+ msg = pseudo_client_op(OP_STAT, flags, -1, dirfd, path, buf);
+ if (msg) {
+ pseudo_stat_msg(buf, msg);
+ if (save_mode) {
+ buf->st_mode = save_mode;
+ }
+ }
+
+ errno = save_errno;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__lxstat.c b/guts/__lxstat.c
new file mode 100644
index 0000000..4678f28
--- /dev/null
+++ b/guts/__lxstat.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap___lxstat(int ver, const char *path, struct stat *buf) {
+ * int rc = -1;
+ */
+
+ rc = wrap___fxstatat(ver, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
+
+/*
+ * }
+ */
diff --git a/guts/__lxstat64.c b/guts/__lxstat64.c
new file mode 100644
index 0000000..36ac18b
--- /dev/null
+++ b/guts/__lxstat64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap___lxstat64(int ver, const char *path, struct stat64 *buf) {
+ * int rc = -1;
+ */
+
+ rc = wrap___fxstatat64(ver, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
+
+/*
+ * }
+ */
diff --git a/guts/__openat64_2.c b/guts/__openat64_2.c
new file mode 100644
index 0000000..85b950b
--- /dev/null
+++ b/guts/__openat64_2.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap___openat64_2(int dirfd, const char *path, int flags) {
+ * int rc = -1;
+ */
+
+ rc = wrap_openat(dirfd, path, flags, O_LARGEFILE);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__openat_2.c b/guts/__openat_2.c
new file mode 100644
index 0000000..ef8d7ad
--- /dev/null
+++ b/guts/__openat_2.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap___openat_2(int dirfd, const char *path, int flags) {
+ * int rc = -1;
+ */
+
+ rc = wrap_openat(dirfd, path, flags, 0);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__xmknod.c b/guts/__xmknod.c
new file mode 100644
index 0000000..5fb395d
--- /dev/null
+++ b/guts/__xmknod.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap___xmknod(int ver, const char *path, mode_t mode, dev_t *dev) {
+ * int rc = -1;
+ */
+
+ rc = wrap___xmknodat(ver, AT_FDCWD, path, mode, dev);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__xmknodat.c b/guts/__xmknodat.c
new file mode 100644
index 0000000..a86d6aa
--- /dev/null
+++ b/guts/__xmknodat.c
@@ -0,0 +1,68 @@
+/*
+ * static int
+ * wrap___xmknodat(int ver, int dirfd, const char *path, mode_t mode, dev_t *dev) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ struct stat64 buf;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ rc = real___xstat64(_STAT_VER, path, &buf);
+#else
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, AT_SYMLINK_NOFOLLOW);
+#endif
+ if (rc != -1) {
+ /* if we can stat the file, you can't mknod it */
+ errno = EEXIST;
+ return -1;
+ }
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real_open(path, O_CREAT | O_WRONLY | O_EXCL, PSEUDO_FS_MODE(mode));
+#else
+ rc = real_openat(dirfd, path, O_CREAT | O_WRONLY | O_EXCL,
+ PSEUDO_FS_MODE(mode));
+#endif
+ if (rc == -1)
+ return -1;
+ real___fxstat64(_STAT_VER, rc, &buf);
+ /* mknod does not really open the file. We don't have
+ * to use wrap_close because we've never exposed this file
+ * descriptor to the client code.
+ */
+ real_close(rc);
+
+ /* mask in the mode type bits again */
+ buf.st_mode = (PSEUDO_DB_MODE(buf.st_mode, mode) & 07777) |
+ (mode & ~07777);
+ buf.st_rdev = *dev;
+ msg = pseudo_client_op(OP_MKNOD, AT_SYMLINK_NOFOLLOW, -1, dirfd, path, &buf);
+ if (!msg) {
+ errno = ENOSYS;
+ rc = -1;
+ } else if (msg->result != RESULT_SUCCEED) {
+ errno = msg->xerrno;
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+ if (rc == -1) {
+ int save_errno = errno;
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ real_unlink(path);
+#else
+ real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW);
+#endif
+ errno = save_errno;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__xstat.c b/guts/__xstat.c
new file mode 100644
index 0000000..ef2e363
--- /dev/null
+++ b/guts/__xstat.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap___xstat(int ver, const char *path, struct stat *buf) {
+ * int rc = -1;
+ */
+
+ rc = wrap___fxstatat(ver, AT_FDCWD, path, buf, 0);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/__xstat64.c b/guts/__xstat64.c
new file mode 100644
index 0000000..f02358b
--- /dev/null
+++ b/guts/__xstat64.c
@@ -0,0 +1,10 @@
+/*
+ * static int
+ * wrap___xstat64(int ver, const char *path, struct stat64 *buf) {
+ * int rc = -1;
+ */
+ rc = wrap___fxstatat64(ver, AT_FDCWD, path, buf, 0);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/chdir.c b/guts/chdir.c
new file mode 100644
index 0000000..b910060
--- /dev/null
+++ b/guts/chdir.c
@@ -0,0 +1,15 @@
+/*
+ * static int
+ * wrap_chdir(const char *path) {
+ * int rc = -1;
+ */
+ pseudo_debug(3, "chdir: %s\n", path ? path : "<nil>");
+ rc = real_chdir(path);
+
+ if (rc != -1) {
+ pseudo_client_op(OP_CHDIR, 0, -1, -1, path, 0);
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/chmod.c b/guts/chmod.c
new file mode 100644
index 0000000..2d5de91
--- /dev/null
+++ b/guts/chmod.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_chmod(const char *path, mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_fchmodat(AT_FDCWD, path, mode, 0);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/chown.c b/guts/chown.c
new file mode 100644
index 0000000..0a82989
--- /dev/null
+++ b/guts/chown.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_chown(const char *path, uid_t owner, gid_t group) {
+ * int rc = -1;
+ */
+
+ rc = wrap_fchownat(AT_FDCWD, path, owner, group, 0);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/close.c b/guts/close.c
new file mode 100644
index 0000000..8edbee9
--- /dev/null
+++ b/guts/close.c
@@ -0,0 +1,14 @@
+/*
+ * static int
+ * wrap_close(int fd) {
+ * int rc = -1;
+ */
+ /* this cleans up an internal table, and shouldn't even
+ * make it to the server.
+ */
+ pseudo_client_op(OP_CLOSE, 0, fd, -1, 0, 0);
+ rc = real_close(fd);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/creat.c b/guts/creat.c
new file mode 100644
index 0000000..e4c0b60
--- /dev/null
+++ b/guts/creat.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_creat(const char *path, mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_openat(AT_FDCWD, path, O_CREAT|O_WRONLY|O_TRUNC, mode);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/creat64.c b/guts/creat64.c
new file mode 100644
index 0000000..655c166
--- /dev/null
+++ b/guts/creat64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_creat64(const char *path, ...mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_openat(AT_FDCWD, path, O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE, mode);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/dup.c b/guts/dup.c
new file mode 100644
index 0000000..941a5d9
--- /dev/null
+++ b/guts/dup.c
@@ -0,0 +1,16 @@
+/*
+ * static int
+ * wrap_dup(int fd) {
+ * int rc = -1;
+ */
+ int save_errno;
+
+ rc = real_dup(fd);
+ save_errno = errno;
+ pseudo_debug(2, "dup: %d->%d\n", fd, rc);
+ pseudo_client_op(OP_DUP, 0, fd, rc, 0, 0);
+
+ errno = save_errno;
+/* return rc;
+ * }
+ */
diff --git a/guts/dup2.c b/guts/dup2.c
new file mode 100644
index 0000000..360c3ad
--- /dev/null
+++ b/guts/dup2.c
@@ -0,0 +1,19 @@
+/*
+ * static int
+ * wrap_dup2(int oldfd, int newfd) {
+ * int rc = -1;
+ */
+ int save_errno;
+
+ /* close existing one first - this also causes the socket to the
+ * server to get moved around if someone tries to overwrite it. */
+ pseudo_debug(2, "dup2: %d->%d\n", oldfd, newfd);
+ pseudo_client_op(OP_CLOSE, 0, newfd, -1, 0, 0);
+ rc = real_dup2(oldfd, newfd);
+ save_errno = errno;
+ pseudo_client_op(OP_DUP, 0, oldfd, newfd, 0, 0);
+ errno = save_errno;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fchdir.c b/guts/fchdir.c
new file mode 100644
index 0000000..5289f4c
--- /dev/null
+++ b/guts/fchdir.c
@@ -0,0 +1,15 @@
+/*
+ * static int
+ * wrap_fchdir(int dirfd) {
+ * int rc = -1;
+ */
+
+ rc = real_fchdir(dirfd);
+
+ if (rc != -1) {
+ pseudo_client_op(OP_CHDIR, 0, -1, dirfd, 0, 0);
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fchmod.c b/guts/fchmod.c
new file mode 100644
index 0000000..df2f9e9
--- /dev/null
+++ b/guts/fchmod.c
@@ -0,0 +1,30 @@
+/*
+ * static int
+ * wrap_fchmod(int fd, mode_t mode) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ struct stat64 buf;
+ int save_errno = errno;
+
+ if (real___fxstat64(_STAT_VER, fd, &buf) == -1) {
+ /* can't stat it, can't chmod it */
+ return -1;
+ }
+ buf.st_mode = (buf.st_mode & ~07777) | (mode & 07777);
+ msg = pseudo_client_op(OP_FCHMOD, 0, fd, -1, 0, &buf);
+ real_fchmod(fd, PSEUDO_FS_MODE(mode));
+ if (!msg) {
+ errno = ENOSYS;
+ rc = -1;
+ } else if (msg->result != RESULT_SUCCEED) {
+ errno = msg->xerrno;
+ rc = -1;
+ } else {
+ errno = save_errno;
+ rc = 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fchmodat.c b/guts/fchmodat.c
new file mode 100644
index 0000000..36bd4d1
--- /dev/null
+++ b/guts/fchmodat.c
@@ -0,0 +1,70 @@
+/*
+ * static int
+ * wrap_fchmodat(int dirfd, const char *path, mode_t mode, int flags) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ struct stat64 buf;
+ int save_errno;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (flags & AT_SYMLINK_NOFOLLOW) {
+ rc = real___lxstat64(_STAT_VER, path, &buf);
+ } else {
+ rc = real___xstat64(_STAT_VER, path, &buf);
+ }
+#else
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, flags);
+#endif
+ if (rc == -1) {
+ return rc;
+ }
+ if (S_ISLNK(buf.st_mode)) {
+ /* we don't really support chmod of a symlink */
+ errno = ENOSYS;
+ return -1;
+ }
+ save_errno = errno;
+
+ /* purely for debugging purposes: check whether file
+ * is already in database.
+ */
+ msg = pseudo_client_op(OP_STAT, flags, -1, -1, path, &buf);
+ if (!msg || msg->result != RESULT_SUCCEED) {
+ pseudo_debug(2, "chmodat to 0%o on %d/%s, ino %llu, new file.\n",
+ mode, dirfd, path, (unsigned long long) buf.st_ino);
+
+ }
+
+ /* user bits added so "root" can always access files. */
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ /* note: if path was a symlink, and AT_NOFOLLOW_SYMLINKS was
+ * specified, we already bailed previously. */
+ real_chmod(path, PSEUDO_FS_MODE(mode));
+#else
+ real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode), flags);
+#endif
+ /* we ignore a failure from underlying fchmod, because pseudo
+ * may believe you are permitted to change modes that the filesystem
+ * doesn't.
+ */
+
+ buf.st_mode = (buf.st_mode & ~07777) | (mode & 07777);
+ msg = pseudo_client_op(OP_CHMOD, flags, -1, dirfd, path, &buf);
+ if (!msg) {
+ errno = ENOSYS;
+ rc = -1;
+ } else if (msg->result != RESULT_SUCCEED) {
+ errno = msg->xerrno;
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fchown.c b/guts/fchown.c
new file mode 100644
index 0000000..4d420c8
--- /dev/null
+++ b/guts/fchown.c
@@ -0,0 +1,54 @@
+/*
+ * static int
+ * wrap_fchown(int fd, uid_t owner, gid_t group) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ struct stat64 buf;
+ int save_errno;
+
+ if (real___fxstat64(_STAT_VER, fd, &buf) == -1) {
+ save_errno = errno;
+ pseudo_debug(2, "fchown failing because fxstat failed: %s\n",
+ strerror(errno));
+ errno = save_errno;
+ return -1;
+ }
+ if (owner == -1 || group == -1) {
+ msg = pseudo_client_op(OP_STAT, 0, fd, -1, NULL, &buf);
+ /* copy in any existing values... */
+ if (msg) {
+ if (msg->result == RESULT_SUCCEED) {
+ pseudo_stat_msg(&buf, msg);
+ } else {
+ pseudo_debug(2, "fchown fd %d, ino %llu, unknown file.\n",
+ fd, (unsigned long long) buf.st_ino);
+ }
+ } else {
+ pseudo_diag("stat within chown of fd %d [%llu] failed.\n",
+ fd, (unsigned long long) buf.st_ino);
+ }
+ }
+ /* now override with arguments */
+ if (owner != -1) {
+ buf.st_uid = owner;
+ }
+ if (group != -1) {
+ buf.st_gid = group;
+ }
+ pseudo_debug(2, "fchown, fd %d: %d:%d -> %d:%d\n",
+ fd, owner, group, buf.st_uid, buf.st_gid);
+ msg = pseudo_client_op(OP_FCHOWN, 0, fd, -1, 0, &buf);
+ if (!msg) {
+ errno = ENOSYS;
+ rc = -1;
+ } else if (msg->result != RESULT_SUCCEED) {
+ errno = msg->xerrno;
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fchownat.c b/guts/fchownat.c
new file mode 100644
index 0000000..8d24d3e
--- /dev/null
+++ b/guts/fchownat.c
@@ -0,0 +1,64 @@
+/*
+ * static int
+ * wrap_fchownat(int dirfd, const char *path, uid_t owner, gid_t group, int flags) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ struct stat64 buf;
+ int save_errno;
+ int doing_link = 0;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (flags & AT_SYMLINK_NOFOLLOW) {
+ rc = real___lxstat64(_STAT_VER, path, &buf);
+ } else {
+ rc = real___xstat64(_STAT_VER, path, &buf);
+ }
+#else
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, flags);
+#endif
+ if (rc == -1) {
+ return rc;
+ }
+ /* pseudo won't track the ownership, here */
+ if (S_ISLNK(buf.st_mode)) {
+ doing_link = 1;
+ }
+ save_errno = errno;
+
+ msg = pseudo_client_op(OP_STAT, flags, -1, -1, path, &buf);
+ /* copy in any existing values... */
+ if (msg) {
+ if (msg->result == RESULT_SUCCEED) {
+ pseudo_stat_msg(&buf, msg);
+ } else {
+ pseudo_debug(2, "chownat to %d:%d on %d/%s, ino %llu, new file.\n",
+ owner, group, dirfd, path,
+ (unsigned long long) buf.st_ino);
+ }
+ }
+ /* now override with arguments */
+ if (owner != -1) {
+ buf.st_uid = owner;
+ }
+ if (group != -1) {
+ buf.st_gid = group;
+ }
+ msg = pseudo_client_op(OP_CHOWN, flags, -1, dirfd, path, &buf);
+ if (!msg) {
+ errno = ENOSYS;
+ rc = -1;
+ } else if (msg->result != RESULT_SUCCEED) {
+ errno = msg->xerrno;
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fclose.c b/guts/fclose.c
new file mode 100644
index 0000000..e0c5681
--- /dev/null
+++ b/guts/fclose.c
@@ -0,0 +1,17 @@
+/*
+ * static int
+ * wrap_fclose(FILE *fp) {
+ * int rc = -1;
+ */
+
+ if (!fp) {
+ errno = EFAULT;
+ return -1;
+ }
+ int fd = fileno(fp);
+ pseudo_client_op(OP_CLOSE, 0, fd, -1, 0, 0);
+ rc = real_fclose(fp);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fcntl.c b/guts/fcntl.c
new file mode 100644
index 0000000..d03d40c
--- /dev/null
+++ b/guts/fcntl.c
@@ -0,0 +1,68 @@
+/*
+ * static int
+ * wrap_fcntl(int fd, int cmd, ...struct flock *lock) {
+ * int rc = -1;
+ */
+ long arg;
+ int save_errno;
+
+ /* we don't know whether we need lock or arg; grab both, which
+ * should be safe enough on Linuxy systems. */
+ va_start(ap, cmd);
+ arg = va_arg(ap, long);
+ va_end(ap);
+
+ switch (cmd) {
+ case F_DUPFD:
+#ifdef F_DUPFD_CLOEXEC
+ case F_DUPFD_CLOEXEC:
+#endif
+ /* actually do something */
+ rc = real_fcntl(fd, cmd, arg);
+ save_errno = errno;
+ if (rc != -1) {
+ pseudo_debug(2, "fcntl_dup: %d->%d\n", fd, rc);
+ pseudo_client_op(OP_DUP, 0, fd, rc, 0, 0);
+ }
+ errno = save_errno;
+ break;
+ /* no argument: */
+ case F_GETFD:
+ case F_GETFL:
+ case F_GETOWN:
+ case F_GETSIG:
+ case F_GETLEASE:
+ rc = real_fcntl(fd, cmd);
+ break;
+ /* long argument */
+ case F_SETFD:
+ case F_SETFL:
+ case F_SETOWN:
+ case F_SETSIG:
+ case F_SETLEASE:
+ case F_NOTIFY:
+ rc = real_fcntl(fd, cmd, arg);
+ break;
+ /* struct flock * argument */
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+ rc = real_fcntl(fd, cmd, lock);
+ break;
+#if defined(F_GETLK64) && (F_GETLK64 != F_GETLK)
+ /* the cast is safe, all struct pointers must smell the same */
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+ rc = real_fcntl(fd, cmd, (struct flock64 *) lock);
+ break;
+#endif
+ default:
+ pseudo_diag("unknown fcntl argument %d, assuming long argument.\n",
+ cmd);
+ rc = real_fcntl(fd, cmd, arg);
+ break;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/fopen.c b/guts/fopen.c
new file mode 100644
index 0000000..2aae54c
--- /dev/null
+++ b/guts/fopen.c
@@ -0,0 +1,32 @@
+/*
+ * static FILE *
+ * wrap_fopen(const char *path, const char *mode) {
+ * FILE * rc = 0;
+ */
+ struct stat64 buf;
+ int save_errno;
+ int existed = (real___xstat64(_STAT_VER, path, &buf) != -1);
+
+ rc = real_fopen(path, mode);
+ save_errno = errno;
+
+ if (rc) {
+ int fd = fileno(rc);
+
+ pseudo_debug(2, "fopen '%s': fd %d\n", path, fd);
+ if (real___fxstat64(_STAT_VER, fd, &buf) != -1) {
+ if (!existed) {
+ pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ }
+ pseudo_client_op(OP_OPEN, 0, fd, -1, path, &buf);
+ } else {
+ pseudo_debug(1, "fopen (fd %d) succeeded, but fstat failed (%s).\n",
+ fd, strerror(errno));
+ pseudo_client_op(OP_OPEN, 0, fd, -1, path, 0);
+ }
+ errno = save_errno;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fopen64.c b/guts/fopen64.c
new file mode 100644
index 0000000..0b0ab58
--- /dev/null
+++ b/guts/fopen64.c
@@ -0,0 +1,32 @@
+/*
+ * static FILE *
+ * wrap_fopen64(const char *path, const char *mode) {
+ * FILE * rc = 0;
+ */
+ struct stat64 buf;
+ int save_errno;
+ int existed = (real___xstat64(_STAT_VER, path, &buf) != -1);
+
+ rc = real_fopen64(path, mode);
+ save_errno = errno;
+
+ if (rc) {
+ int fd = fileno(rc);
+
+ pseudo_debug(2, "fopen64 '%s': fd %d\n", path, fd);
+ if (real___fxstat64(_STAT_VER, fd, &buf) != -1) {
+ if (!existed) {
+ pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ }
+ pseudo_client_op(OP_OPEN, 0, fd, -1, path, &buf);
+ } else {
+ pseudo_debug(1, "fopen64 (fd %d) succeeded, but fstat failed (%s).\n",
+ fd, strerror(errno));
+ pseudo_client_op(OP_OPEN, 0, fd, -1, path, 0);
+ }
+ errno = save_errno;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fork.c b/guts/fork.c
new file mode 100644
index 0000000..a49694f
--- /dev/null
+++ b/guts/fork.c
@@ -0,0 +1,13 @@
+/*
+ * static int
+ * wrap_fork(void) {
+ * int rc = -1;
+ */
+
+ rc = real_fork();
+ if (rc == 0)
+ pseudo_client_reset();
+
+/* return rc;
+ * }
+ */
diff --git a/guts/freopen.c b/guts/freopen.c
new file mode 100644
index 0000000..312bc0a
--- /dev/null
+++ b/guts/freopen.c
@@ -0,0 +1,32 @@
+/*
+ * static FILE *
+ * wrap_freopen(const char *path, const char *mode, FILE *stream) {
+ * FILE * rc = NULL;
+ */
+ struct stat64 buf;
+ int save_errno;
+ int existed = (real___xstat64(_STAT_VER, path, &buf) != -1);
+
+ rc = real_freopen(path, mode, stream);
+ save_errno = errno;
+
+ if (rc) {
+ int fd = fileno(rc);
+
+ pseudo_debug(2, "freopen '%s': fd %d\n", path, fd);
+ if (real___fxstat64(_STAT_VER, fd, &buf) != -1) {
+ if (!existed) {
+ pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf);
+ }
+ pseudo_client_op(OP_OPEN, 0, fd, -1, path, &buf);
+ } else {
+ pseudo_debug(1, "fopen (fd %d) succeeded, but stat failed (%s).\n",
+ fd, strerror(errno));
+ pseudo_client_op(OP_OPEN, 0, fd, -1, path, 0);
+ }
+ errno = save_errno;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/getegid.c b/guts/getegid.c
new file mode 100644
index 0000000..4a3c929
--- /dev/null
+++ b/guts/getegid.c
@@ -0,0 +1,11 @@
+/*
+ * static gid_t
+ * wrap_getegid(void) {
+ * gid_t rc = 0;
+ */
+
+ rc = pseudo_egid;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/geteuid.c b/guts/geteuid.c
new file mode 100644
index 0000000..508cc83
--- /dev/null
+++ b/guts/geteuid.c
@@ -0,0 +1,11 @@
+/*
+ * static uid_t
+ * wrap_geteuid(void) {
+ * uid_t rc = 0;
+ */
+
+ rc = pseudo_euid;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/getgid.c b/guts/getgid.c
new file mode 100644
index 0000000..415e3f0
--- /dev/null
+++ b/guts/getgid.c
@@ -0,0 +1,11 @@
+/*
+ * static gid_t
+ * wrap_getgid(void) {
+ * gid_t rc = 0;
+ */
+
+ rc = pseudo_rgid;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/getresgid.c b/guts/getresgid.c
new file mode 100644
index 0000000..3eb4fac
--- /dev/null
+++ b/guts/getresgid.c
@@ -0,0 +1,20 @@
+/*
+ * static int
+ * wrap_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) {
+ * int rc = -1;
+ */
+ if (rgid)
+ *rgid = pseudo_rgid;
+ if (egid)
+ *egid = pseudo_egid;
+ if (sgid)
+ *sgid = pseudo_sgid;
+ if (rgid && egid && sgid) {
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EFAULT;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/getresuid.c b/guts/getresuid.c
new file mode 100644
index 0000000..2976f78
--- /dev/null
+++ b/guts/getresuid.c
@@ -0,0 +1,20 @@
+/*
+ * static int
+ * wrap_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) {
+ * int rc = -1;
+ */
+ if (ruid)
+ *ruid = pseudo_ruid;
+ if (euid)
+ *euid = pseudo_euid;
+ if (suid)
+ *suid = pseudo_suid;
+ if (ruid && euid && suid) {
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EFAULT;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/getuid.c b/guts/getuid.c
new file mode 100644
index 0000000..48294e2
--- /dev/null
+++ b/guts/getuid.c
@@ -0,0 +1,11 @@
+/*
+ * static uid_t
+ * wrap_getuid(void) {
+ * uid_t rc = 0;
+ */
+
+ rc = pseudo_ruid;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/lchown.c b/guts/lchown.c
new file mode 100644
index 0000000..6f7e53a
--- /dev/null
+++ b/guts/lchown.c
@@ -0,0 +1,48 @@
+/*
+ * static int
+ * wrap_lchown(const char *path, uid_t owner, gid_t group) {
+ */
+ pseudo_msg_t *msg;
+ struct stat64 buf;
+
+ pseudo_debug(2, "lchown(%s, %d, %d)\n",
+ path ? path : "<null>", owner, group);
+ if (real___lxstat64(_STAT_VER, path, &buf) == -1) {
+ return -1;
+ }
+ if (owner == -1 || group == -1) {
+ msg = pseudo_client_op(OP_STAT, AT_SYMLINK_NOFOLLOW, -1, -1, path, &buf);
+ /* copy in any existing values... */
+ if (msg) {
+ if (msg->result == RESULT_SUCCEED) {
+ pseudo_stat_msg(&buf, msg);
+ } else {
+ pseudo_debug(2, "lchown to %d:%d on %s, ino %llu, new file.\n",
+ owner, group, path,
+ (unsigned long long) buf.st_ino);
+ }
+ } else {
+ pseudo_diag("stat within lchown of '%s' [%llu] failed.\n",
+ path, (unsigned long long) buf.st_ino);
+ }
+ }
+ if (owner != -1) {
+ buf.st_uid = owner;
+ }
+ if (group != -1) {
+ buf.st_gid = group;
+ }
+ msg = pseudo_client_op(OP_CHOWN, AT_SYMLINK_NOFOLLOW, -1, -1, path, &buf);
+ if (!msg) {
+ errno = ENOSYS;
+ rc = -1;
+ } else if (msg->result != RESULT_SUCCEED) {
+ errno = msg->xerrno;
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/link.c b/guts/link.c
new file mode 100644
index 0000000..278edd7
--- /dev/null
+++ b/guts/link.c
@@ -0,0 +1,29 @@
+/*
+ * static int
+ * wrap_link(const char *oldpath, const char *newpath) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ struct stat64 buf;
+
+ rc = real_link(oldpath, newpath);
+ if (rc == 0) {
+ /* link(2) will not overwrite; if it succeeded, we know
+ * that there was no previous file with this name, so we
+ * shove it into the database.
+ */
+ real___xstat64(_STAT_VER, oldpath, &buf);
+ /* a link should copy the existing database entry, if
+ * there is one. OP_LINK is also used to insert unseen
+ * files, though, so it can't be implicit.
+ */
+ msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, &buf);
+ if (msg) {
+ pseudo_stat_msg(&buf, msg);
+ }
+ pseudo_client_op(OP_LINK, 0, -1, -1, newpath, &buf);
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/mkdir.c b/guts/mkdir.c
new file mode 100644
index 0000000..3177e4f
--- /dev/null
+++ b/guts/mkdir.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_mkdir(const char *path, mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_mkdirat(AT_FDCWD, path, mode);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/mkdirat.c b/guts/mkdirat.c
new file mode 100644
index 0000000..a5ae5d8
--- /dev/null
+++ b/guts/mkdirat.c
@@ -0,0 +1,34 @@
+/*
+ * static int
+ * wrap_mkdirat(int dirfd, const char *path, mode_t mode) {
+ * int rc = -1;
+ */
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ rc = real_mkdir(path, PSEUDO_FS_MODE(mode));
+#else
+ rc = real_mkdirat(dirfd, path, PSEUDO_FS_MODE(mode));
+#endif
+ if (rc != -1) {
+ struct stat64 buf;
+ int stat_rc;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ stat_rc = real___lxstat64(_STAT_VER, path, &buf);
+#else
+ stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, AT_SYMLINK_NOFOLLOW);
+#endif
+ if (stat_rc != -1) {
+ pseudo_client_op(OP_MKDIR, AT_SYMLINK_NOFOLLOW, -1, dirfd, path, &buf);
+ } else {
+ pseudo_debug(1, "mkdir of %s succeeded, but stat failed: %s\n",
+ path, strerror(errno));
+ }
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/mkstemp.c b/guts/mkstemp.c
new file mode 100644
index 0000000..b339b5c
--- /dev/null
+++ b/guts/mkstemp.c
@@ -0,0 +1,25 @@
+/*
+ * static int
+ * wrap_mkstemp(char *template) {
+ * int rc = -1;
+ */
+ struct stat64 buf;
+ int save_errno;
+
+ rc = real_mkstemp(template);
+
+ if (rc != -1) {
+ save_errno = errno;
+ if (real___fxstat64(_STAT_VER, rc, &buf) != -1) {
+ pseudo_client_op(OP_CREAT, 0, -1, -1, template, &buf);
+ pseudo_client_op(OP_OPEN, 0, rc, -1, template, &buf);
+ } else {
+ pseudo_debug(1, "mkstemp (fd %d) succeeded, but fstat failed (%s).\n",
+ rc, strerror(errno));
+ pseudo_client_op(OP_OPEN, 0, rc, -1, template, 0);
+ }
+ errno = save_errno;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/open.c b/guts/open.c
new file mode 100644
index 0000000..7348daf
--- /dev/null
+++ b/guts/open.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_open(const char *path, int flags, ...mode_t mode) {
+ * int rc = -1;
+ */
+
+ return wrap_openat(AT_FDCWD, path, flags, mode);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/open64.c b/guts/open64.c
new file mode 100644
index 0000000..0898c78
--- /dev/null
+++ b/guts/open64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_open64(const char *path, int flags, ...mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_openat(AT_FDCWD, path, flags, mode | O_LARGEFILE);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/openat.c b/guts/openat.c
new file mode 100644
index 0000000..9475aeb
--- /dev/null
+++ b/guts/openat.c
@@ -0,0 +1,64 @@
+/*
+ * static int
+ * wrap_openat(int dirfd, const char *path, int flags, ...mode_t mode) {
+ * int rc = -1;
+ */
+ struct stat64 buf;
+ int existed = 1;
+ int save_errno;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+#endif
+ /* if a creation has been requested, check whether file exists */
+ if (flags & O_CREAT) {
+ save_errno = errno;
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real___xstat64(_STAT_VER, path, &buf);
+#else
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, 0);
+#endif
+ existed = (rc != -1);
+ if (!existed)
+ pseudo_debug(2, "openat_creat: %s -> 0%o\n", path, mode);
+ errno = save_errno;
+ }
+
+ /* because we are not actually root, secretly mask in 0700 to the
+ * underlying mode
+ */
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real_open(path, flags, PSEUDO_FS_MODE(mode));
+#else
+ rc = real_openat(dirfd, path, flags, PSEUDO_FS_MODE(mode));
+#endif
+ save_errno = errno;
+
+ if (rc != -1) {
+ int stat_rc;
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ stat_rc = real___xstat64(_STAT_VER, path, &buf);
+#else
+ stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, 0);
+#endif
+
+ if (stat_rc != -1) {
+ buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode);
+ if (!existed) {
+ pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf);
+ }
+ pseudo_client_op(OP_OPEN, 0, rc, dirfd, path, &buf);
+ } else {
+ pseudo_debug(1, "openat (fd %d, path %d/%s, flags %d) succeeded, but stat failed (%s).\n",
+ rc, dirfd, path, flags, strerror(errno));
+ pseudo_client_op(OP_OPEN, 0, rc, dirfd, path, 0);
+ }
+ errno = save_errno;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/openat64.c b/guts/openat64.c
new file mode 100644
index 0000000..926d9c8
--- /dev/null
+++ b/guts/openat64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_openat64(int dirfd, const char *path, int flags, ...mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_openat(dirfd, path, flags, mode | O_LARGEFILE);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/rename.c b/guts/rename.c
new file mode 100644
index 0000000..9dd6d99
--- /dev/null
+++ b/guts/rename.c
@@ -0,0 +1,77 @@
+/*
+ * static int
+ * wrap_rename(const char *oldpath, const char *newpath) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ struct stat64 oldbuf, newbuf;
+ int oldrc, newrc;
+ int save_errno;
+
+ pseudo_debug(1, "rename: %s->%s\n",
+ oldpath ? oldpath : "<nil>",
+ newpath ? newpath : "<nil>");
+
+ if (!oldpath || !newpath) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ save_errno = errno;
+
+ newrc = real___lxstat64(_STAT_VER, newpath, &newbuf);
+ oldrc = real___lxstat64(_STAT_VER, oldpath, &oldbuf);
+
+ errno = save_errno;
+
+ rc = real_rename(oldpath, newpath);
+ if (rc == -1) {
+ /* we failed, and we don't care why */
+ return rc;
+ }
+ save_errno = errno;
+ /* nothing to do for a "rename" of a link to itself */
+ if (newrc != -1 && oldrc != -1 &&
+ newbuf.st_dev == oldbuf.st_dev &&
+ newbuf.st_ino == oldbuf.st_ino) {
+ return rc;
+ }
+
+ /* rename(3) is not mv(1). rename(file, dir) fails; you must provide
+ * the corrected path yourself. You can rename over a directory only
+ * if the source is a directory. Symlinks are simply removed.
+ *
+ * If we got here, the real rename call succeeded. That means newpath
+ * has been unlinked and oldpath has been linked to it.
+ *
+ * There are a ton of special cases to error check. I don't check
+ * for any of them, because in every such case, the underlying rename
+ * failed, and there is nothing to do.
+ * The only tricky part is that, because we used to ignore symlinks,
+ * we may have to rename or remove directory trees even though in
+ * theory rename can never destroy a directory tree.
+ */
+
+ /* newpath must be removed. */
+ pseudo_client_op(OP_UNLINK, AT_SYMLINK_NOFOLLOW, -1, -1, newpath, &newbuf);
+
+ /* fill in "correct" details from server */
+ msg = pseudo_client_op(OP_STAT, AT_SYMLINK_NOFOLLOW, -1, -1, oldpath, &oldbuf);
+ if (msg && msg->result == RESULT_SUCCEED) {
+ pseudo_stat_msg(&oldbuf, msg);
+ pseudo_debug(1, "renaming %s, got old mode of 0%o\n", oldpath, (int) msg->mode);
+ } else {
+ /* create an entry under the old name, which will then be
+ * renamed; this way, children would get renamed too, if there
+ * were any.
+ */
+ pseudo_debug(1, "renaming new '%s' [%llu]\n",
+ oldpath, (unsigned long long) oldbuf.st_ino);
+ pseudo_client_op(OP_LINK, AT_SYMLINK_NOFOLLOW, -1, -1, oldpath, &oldbuf);
+ }
+ pseudo_client_op(OP_RENAME, AT_SYMLINK_NOFOLLOW, -1, -1, newpath, &oldbuf, oldpath);
+
+ errno = save_errno;
+/* return rc;
+ * }
+ */
diff --git a/guts/renameat.c b/guts/renameat.c
new file mode 100644
index 0000000..c5d295f
--- /dev/null
+++ b/guts/renameat.c
@@ -0,0 +1,12 @@
+/*
+ * static int
+ * wrap_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ * int rc = -1;
+ */
+
+ pseudo_diag("help! unimplemented renameat [%s -> %s].\n", oldpath, newpath);
+ rc = real_renameat(olddirfd, oldpath, newdirfd, newpath);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/rmdir.c b/guts/rmdir.c
new file mode 100644
index 0000000..029e5a2
--- /dev/null
+++ b/guts/rmdir.c
@@ -0,0 +1,22 @@
+/*
+ * static int
+ * wrap_rmdir(const char *path) {
+ * int rc = -1;
+ */
+ struct stat64 buf;
+ int save_errno;
+
+ rc = real___lxstat64(_STAT_VER, path, &buf);
+ if (rc == -1) {
+ return rc;
+ }
+ rc = real_rmdir(path);
+ save_errno = errno;
+ if (rc != -1) {
+ pseudo_client_op(OP_UNLINK, AT_SYMLINK_NOFOLLOW, -1, -1, path, &buf);
+ }
+
+ errno = save_errno;
+/* return rc;
+ * }
+ */
diff --git a/guts/setegid.c b/guts/setegid.c
new file mode 100644
index 0000000..a24be76
--- /dev/null
+++ b/guts/setegid.c
@@ -0,0 +1,17 @@
+/*
+ * static int
+ * wrap_setegid(gid_t egid) {
+ * int rc = -1;
+ */
+ if (pseudo_euid == 0 || egid == pseudo_egid || egid == pseudo_rgid || egid == pseudo_sgid) {
+ pseudo_egid = egid;
+ pseudo_fgid = egid;
+ pseudo_client_touchgid();
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EPERM;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/seteuid.c b/guts/seteuid.c
new file mode 100644
index 0000000..42cb3db
--- /dev/null
+++ b/guts/seteuid.c
@@ -0,0 +1,17 @@
+/*
+ * static int
+ * wrap_seteuid(uid_t euid) {
+ * int rc = -1;
+ */
+ if (pseudo_euid == 0 || euid == pseudo_euid || euid == pseudo_ruid || euid == pseudo_suid) {
+ pseudo_euid = euid;
+ pseudo_fuid = euid;
+ pseudo_client_touchuid();
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EPERM;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setfsgid.c b/guts/setfsgid.c
new file mode 100644
index 0000000..b046c6f
--- /dev/null
+++ b/guts/setfsgid.c
@@ -0,0 +1,16 @@
+/*
+ * static int
+ * wrap_setfsgid(gid_t fsgid) {
+ * int rc = -1;
+ */
+ if (pseudo_euid == 0 ||
+ pseudo_egid == fsgid || pseudo_rgid == fsgid || pseudo_sgid == fsgid) {
+ pseudo_fgid = fsgid;
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EPERM;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setfsuid.c b/guts/setfsuid.c
new file mode 100644
index 0000000..1b58ce8
--- /dev/null
+++ b/guts/setfsuid.c
@@ -0,0 +1,16 @@
+/*
+ * static int
+ * wrap_setfsuid(uid_t fsuid) {
+ * int rc = -1;
+ */
+ if (pseudo_euid == 0 ||
+ pseudo_euid == fsuid || pseudo_ruid == fsuid || pseudo_suid == fsuid) {
+ pseudo_fuid = fsuid;
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EPERM;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setgid.c b/guts/setgid.c
new file mode 100644
index 0000000..0bb5f27
--- /dev/null
+++ b/guts/setgid.c
@@ -0,0 +1,24 @@
+/*
+ * static int
+ * wrap_setgid(gid_t gid) {
+ * int rc = -1;
+ */
+ if (pseudo_euid == 0) {
+ pseudo_rgid = gid;
+ pseudo_egid = gid;
+ pseudo_sgid = gid;
+ pseudo_fgid = gid;
+ pseudo_client_touchgid();
+ rc = 0;
+ } else if (pseudo_egid == gid || pseudo_sgid == gid || pseudo_rgid == gid) {
+ pseudo_egid = gid;
+ pseudo_fgid = gid;
+ pseudo_client_touchgid();
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EPERM;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setgroups.c b/guts/setgroups.c
new file mode 100644
index 0000000..d6856f2
--- /dev/null
+++ b/guts/setgroups.c
@@ -0,0 +1,12 @@
+/*
+ * static int
+ * wrap_setgroups(size_t size, const gid_t *list) {
+ * int rc = -1;
+ */
+
+ /* you always have all group privileges. we're like magic! */
+ rc = 0;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/setregid.c b/guts/setregid.c
new file mode 100644
index 0000000..2444390
--- /dev/null
+++ b/guts/setregid.c
@@ -0,0 +1,27 @@
+/*
+ * static int
+ * wrap_setregid(gid_t rgid, gid_t egid) {
+ * int rc = -1;
+ */
+ rc = 0;
+ if (pseudo_euid != 0 && rgid != -1 &&
+ rgid != pseudo_egid && rgid != pseudo_rgid && rgid != pseudo_sgid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (pseudo_euid != 0 && egid != -1 &&
+ egid != pseudo_egid && egid != pseudo_rgid && egid != pseudo_sgid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (rc != -1) {
+ if (rgid != -1)
+ pseudo_rgid = rgid;
+ if (egid != -1)
+ pseudo_egid = egid;
+ pseudo_fgid = pseudo_egid;
+ pseudo_client_touchuid();
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setresgid.c b/guts/setresgid.c
new file mode 100644
index 0000000..455fe62
--- /dev/null
+++ b/guts/setresgid.c
@@ -0,0 +1,34 @@
+/*
+ * static int
+ * wrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid) {
+ * int rc = -1;
+ */
+ rc = 0;
+ if (pseudo_euid != 0 && rgid != -1 &&
+ rgid != pseudo_egid && rgid != pseudo_rgid && rgid != pseudo_sgid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (pseudo_euid != 0 && egid != -1 &&
+ egid != pseudo_egid && egid != pseudo_rgid && egid != pseudo_sgid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (pseudo_euid != 0 && sgid != -1 &&
+ sgid != pseudo_egid && sgid != pseudo_rgid && sgid != pseudo_sgid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (rc != -1) {
+ if (rgid != -1)
+ pseudo_rgid = rgid;
+ if (egid != -1)
+ pseudo_egid = egid;
+ if (sgid != -1)
+ pseudo_sgid = sgid;
+ pseudo_fgid = pseudo_egid;
+ pseudo_client_touchuid();
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setresuid.c b/guts/setresuid.c
new file mode 100644
index 0000000..41dd81d
--- /dev/null
+++ b/guts/setresuid.c
@@ -0,0 +1,34 @@
+/*
+ * static int
+ * wrap_setresuid(uid_t ruid, uid_t euid, uid_t suid) {
+ * int rc = -1;
+ */
+ rc = 0;
+ if (pseudo_euid != 0 && ruid != -1 &&
+ ruid != pseudo_euid && ruid != pseudo_ruid && ruid != pseudo_suid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (pseudo_euid != 0 && euid != -1 &&
+ euid != pseudo_euid && euid != pseudo_ruid && euid != pseudo_suid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (pseudo_euid != 0 && suid != -1 &&
+ suid != pseudo_euid && suid != pseudo_ruid && suid != pseudo_suid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (rc != -1) {
+ if (ruid != -1)
+ pseudo_ruid = ruid;
+ if (euid != -1)
+ pseudo_euid = euid;
+ if (suid != -1)
+ pseudo_suid = suid;
+ pseudo_fuid = pseudo_euid;
+ pseudo_client_touchuid();
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setreuid.c b/guts/setreuid.c
new file mode 100644
index 0000000..3669581
--- /dev/null
+++ b/guts/setreuid.c
@@ -0,0 +1,27 @@
+/*
+ * static int
+ * wrap_setreuid(uid_t ruid, uid_t euid) {
+ * int rc = -1;
+ */
+ rc = 0;
+ if (pseudo_euid != 0 && ruid != -1 &&
+ ruid != pseudo_euid && ruid != pseudo_ruid && ruid != pseudo_suid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (pseudo_euid != 0 && euid != -1 &&
+ euid != pseudo_euid && euid != pseudo_ruid && euid != pseudo_suid) {
+ rc = -1;
+ errno = EPERM;
+ }
+ if (rc != -1) {
+ if (ruid != -1)
+ pseudo_ruid = ruid;
+ if (euid != -1)
+ pseudo_euid = euid;
+ pseudo_fuid = pseudo_euid;
+ pseudo_client_touchuid();
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/setuid.c b/guts/setuid.c
new file mode 100644
index 0000000..bedebb6
--- /dev/null
+++ b/guts/setuid.c
@@ -0,0 +1,24 @@
+/*
+ * static int
+ * wrap_setuid(uid_t uid) {
+ * int rc = -1;
+ */
+ if (pseudo_euid == 0) {
+ pseudo_ruid = uid;
+ pseudo_euid = uid;
+ pseudo_suid = uid;
+ pseudo_fuid = uid;
+ pseudo_client_touchuid();
+ rc = 0;
+ } else if (pseudo_euid == uid || pseudo_suid == uid || pseudo_ruid == uid) {
+ pseudo_euid = uid;
+ pseudo_fuid = uid;
+ pseudo_client_touchuid();
+ rc = 0;
+ } else {
+ rc = -1;
+ errno = EPERM;
+ }
+/* return rc;
+ * }
+ */
diff --git a/guts/symlink.c b/guts/symlink.c
new file mode 100644
index 0000000..bc1c4be
--- /dev/null
+++ b/guts/symlink.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_symlink(const char *oldpath, const char *newpath) {
+ * int rc = -1;
+ */
+
+ rc = wrap_symlinkat(oldpath, AT_FDCWD, newpath);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/symlinkat.c b/guts/symlinkat.c
new file mode 100644
index 0000000..5f1cc59
--- /dev/null
+++ b/guts/symlinkat.c
@@ -0,0 +1,38 @@
+/*
+ * static int
+ * wrap_symlinkat(const char *oldpath, int dirfd, const char *newpath) {
+ * int rc = -1;
+ */
+ struct stat64 buf;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ rc = real_symlink(oldpath, newpath);
+#else
+ rc = real_symlinkat(oldpath, dirfd, newpath);
+#endif
+
+ if (rc == -1) {
+ return rc;
+ }
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real___lxstat64(_STAT_VER, newpath, &buf);
+#else
+ rc = real___fxstatat64(_STAT_VER, dirfd, newpath, &buf, AT_SYMLINK_NOFOLLOW);
+#endif
+ if (rc == -1) {
+ int save_errno = errno;
+ pseudo_diag("symlinkat: couldn't stat '%s' even though symlink creation succeeded (%s).\n",
+ newpath, strerror(errno));
+ errno = save_errno;
+ return rc;
+ }
+ /* just record the entry */
+ pseudo_client_op(OP_SYMLINK, AT_SYMLINK_NOFOLLOW, -1, dirfd, newpath, &buf);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/unlink.c b/guts/unlink.c
new file mode 100644
index 0000000..369089b
--- /dev/null
+++ b/guts/unlink.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_unlink(const char *path) {
+ * int rc = -1;
+ */
+
+ rc = wrap_unlinkat(AT_FDCWD, path, 0);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/unlinkat.c b/guts/unlinkat.c
new file mode 100644
index 0000000..cfa71e7
--- /dev/null
+++ b/guts/unlinkat.c
@@ -0,0 +1,44 @@
+/*
+ * static int
+ * wrap_unlinkat(int dirfd, const char *path, int flags) {
+ * int rc = -1;
+ */
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (flags) {
+ /* the only supported flag is AT_REMOVEDIR. We'd never call
+ * with that flag unless the real AT functions exist, so
+ * something must have gone horribly wrong....
+ */
+ pseudo_diag("wrap_unlinkat called with flags (0x%x), path '%s'\n",
+ flags, path ? path : "<nil>");
+ errno = ENOSYS;
+ return -1;
+ }
+#endif
+
+ struct stat64 buf;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real___lxstat64(_STAT_VER, path, &buf);
+#else
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, AT_SYMLINK_NOFOLLOW);
+#endif
+ if (rc == -1) {
+ return rc;
+ }
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ rc = real_unlink(path);
+#else
+ rc = real_unlinkat(dirfd, path, flags);
+#endif
+ if (rc != -1) {
+ pseudo_client_op(OP_UNLINK, AT_SYMLINK_NOFOLLOW, -1, dirfd, path, &buf);
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/vfork.c b/guts/vfork.c
new file mode 100644
index 0000000..7d234da
--- /dev/null
+++ b/guts/vfork.c
@@ -0,0 +1,14 @@
+/*
+ * static int
+ * wrap_vfork(void) {
+ * int rc = -1;
+ */
+
+ /* like fakeroot, we really can't handle vfork's implications */
+ rc = real_fork();
+ if (rc == 0)
+ pseudo_client_reset();
+
+/* return rc;
+ * }
+ */