diff --git a/Makefile.in b/Makefile.in
index b43d2d6..6eff522 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -42,7 +42,7 @@ LOCALSTATE=var/pseudo
BINDIR=$(PREFIX)/$(BIN)
LOCALSTATEDIR=$(PREFIX)/$(LOCALSTATE)
-CFLAGS_BASE=-pipe -std=gnu99 -Wall -W -Wextra
+CFLAGS_BASE=-pipe -std=gnu99 -Wall -W -Wextra -Wno-deprecated-declarations
CFLAGS_CODE=-fPIC -D_LARGEFILE64_SOURCE -D_ATFILE_SOURCE $(ARCH_FLAGS)
CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' -DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' -DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"' $(SQLITE_MEMORY) $(FORCE_ASYNC) -DPSEUDO_PASSWD_FALLBACK='$(PASSWD_FALLBACK)' $(OPTDEFS) $(EPOLL)
CFLAGS_DEBUG=-O2 -g
@@ -75,6 +75,7 @@ TABLES=table_templates/pseudo_tables.c table_templates/pseudo_tables.h
all: $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) $(PSEUDO_PROFILE)
test: all | $(BIN) $(LIB) $(LOCALSTATE)
+ $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o test/test-rename-fstat test/test-rename-fstat.c
@./run_tests.sh -v
install-lib: $(LIBPSEUDO)
diff --git a/enums/res.in b/enums/res.in
index 435338f..b0096b4 100644
--- a/enums/res.in
+++ b/enums/res.in
@@ -2,3 +2,4 @@ res: RESULT
succeed
fail
error
+abort
diff --git a/maketables b/maketables
index a211772..52285e2 100755
--- a/maketables
+++ b/maketables
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (c) 2008-2010, 2013 Wind River Systems, Inc.
#
diff --git a/makewrappers b/makewrappers
index e84607d..6681e11 100755
--- a/makewrappers
+++ b/makewrappers
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (c) 2008-2011,2013 Wind River Systems, Inc.
#
@@ -11,6 +11,7 @@ import glob
import sys
import re
import os.path
+import platform
import string
import subprocess
from templatefile import TemplateFile
@@ -228,6 +229,8 @@ class Function:
self.real_func = None
self.paths_to_munge = []
self.specific_dirfds = {}
+ self.fd_arg = False
+ self.noignore_path = False
self.hand_wrapped = None
self.async_skip = None
# used for the copyright date when creating stub functions
@@ -267,6 +270,11 @@ class Function:
self.flags = '(flags & AT_SYMLINK_NOFOLLOW)'
elif arg.name.endswith('path'):
self.paths_to_munge.append(arg.name)
+ elif arg.name == 'fd':
+ self.fd_arg = "fd"
+ elif arg.name == 'filedes':
+ self.fd_arg = "filedes"
+
# pick default values
if self.type == 'void':
@@ -283,10 +291,18 @@ class Function:
# handle special comments, such as flags=AT_SYMLINK_NOFOLLOW
if self.comments:
- modifiers = self.comments.split(', ')
- for mod in modifiers:
- key, value = mod.split('=')
- value = value.rstrip()
+ # Build a dictionary of key=value, key=value pairs
+ modifiers = dict(mod.split("=") for mod in self.comments.split(','))
+ # Strip all leading/trailing whitespace
+ modifiers = {k.strip():v.strip() for k, v in modifiers.items()}
+
+ arch = "-" + platform.machine()
+ # Sorted so that versions-foo appear after versions, so overrides are easy
+ for key in sorted(modifiers):
+ value = modifiers[key]
+ # If the key is version-arm64 and we're on arm64 then rename this to version
+ if key.endswith(arch):
+ key = key.replace(arch, "")
setattr(self, key, value)
def maybe_inode64(self):
@@ -361,6 +377,25 @@ class Function:
(path, prefix, self.dirfd, path, self.flags))
return "\n\t\t".join(fix_paths)
+ def ignore_paths(self):
+ if self.noignore_path:
+ return "0"
+
+ mainpath = None
+ if "oldpath" in self.paths_to_munge:
+ mainpath = "oldpath"
+ elif "newpath" in self.paths_to_munge:
+ mainpath = "newpath"
+ elif "path" in self.paths_to_munge:
+ mainpath = "path"
+
+ if mainpath:
+ return "pseudo_client_ignore_path(%s)" % mainpath
+ if self.fd_arg:
+ return "pseudo_client_ignore_fd(%s)" % self.fd_arg
+
+ return "0"
+
def real_predecl(self):
if self.real_func:
return self.decl().replace(self.name, self.real_func, 1) + ";"
@@ -567,7 +602,7 @@ def process_wrapfuncs(port):
func.directory = directory
funcs[func.name] = func
sys.stdout.write(".")
- except Exception(e):
+ except Exception as e:
print("Parsing failed:", e)
exit(1)
funclist.close()
diff --git a/ports/linux/guts/fcntl.c b/ports/linux/guts/fcntl.c
index 4dd9796..434c7f3 100644
--- a/ports/linux/guts/fcntl.c
+++ b/ports/linux/guts/fcntl.c
@@ -52,6 +52,11 @@
case F_GETLK:
case F_SETLK:
case F_SETLKW:
+#ifdef F_OFD_GETLK
+ case F_OFD_GETLK:
+ case F_OFD_SETLK:
+ case F_OFD_SETLKW:
+#endif
rc = real_fcntl(fd, cmd, lock);
break;
#if defined(F_GETLK64) && (F_GETLK64 != F_GETLK)
diff --git a/ports/linux/guts/lchmod.c b/ports/linux/guts/lchmod.c
new file mode 100644
index 0000000..da330a7
--- /dev/null
+++ b/ports/linux/guts/lchmod.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2021 Linux Foundation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static int
+ * wrap_lchmod(const char *path, mode_t mode) {
+ */
+
+ rc = wrap_fchmodat(AT_FDCWD, path, mode, AT_SYMLINK_NOFOLLOW);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/guts/mkostemp64.c b/ports/linux/guts/mkostemp64.c
new file mode 100644
index 0000000..502211b
--- /dev/null
+++ b/ports/linux/guts/mkostemp64.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static int
+ * wrap_mkstemp64(char *template, int oflags) {
+ * int rc = -1;
+ */
+ struct stat64 buf;
+ int save_errno;
+ size_t len;
+ char *tmp_template;
+
+ if (!template) {
+ errno = EFAULT;
+ return 0;
+ }
+
+ len = strlen(template);
+ tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW);
+
+ if (!tmp_template) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* mkstemp64 wrapper uses this code and mkostemp64 not present in some glibc versions */
+ if (oflags == 0)
+ rc = real_mkstemp64(tmp_template);
+ else
+ rc = real_mkostemp64(tmp_template, oflags);
+
+ if (rc != -1) {
+ save_errno = errno;
+
+ if (real___fxstat64(_STAT_VER, rc, &buf) != -1) {
+ real_fchmod(rc, PSEUDO_FS_MODE(0600, 0));
+ pseudo_client_op(OP_CREAT, 0, -1, -1, tmp_template, &buf);
+ pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, &buf);
+ } else {
+ pseudo_debug(PDBGF_CONSISTENCY, "mkstemp (fd %d) succeeded, but fstat failed (%s).\n",
+ rc, strerror(errno));
+ pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, 0);
+ }
+ errno = save_errno;
+ }
+ /* mkstemp only changes the XXXXXX at the end. */
+ memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6);
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/guts/mkstemp64.c b/ports/linux/guts/mkstemp64.c
index aa7bb58..487f256 100644
--- a/ports/linux/guts/mkstemp64.c
+++ b/ports/linux/guts/mkstemp64.c
@@ -8,42 +8,9 @@
* wrap_mkstemp64(char *template) {
* int rc = -1;
*/
- struct stat64 buf;
- int save_errno;
- size_t len;
- char *tmp_template;
+ /* mkstemp64() is just like mkostemp64() with no flags */
+ rc = wrap_mkostemp64(template, 0);
- if (!template) {
- errno = EFAULT;
- return 0;
- }
-
- len = strlen(template);
- tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW);
-
- if (!tmp_template) {
- errno = ENOENT;
- return -1;
- }
-
- rc = real_mkstemp64(tmp_template);
-
- if (rc != -1) {
- save_errno = errno;
-
- if (real___fxstat64(_STAT_VER, rc, &buf) != -1) {
- real_fchmod(rc, PSEUDO_FS_MODE(0600, 0));
- pseudo_client_op(OP_CREAT, 0, -1, -1, tmp_template, &buf);
- pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, &buf);
- } else {
- pseudo_debug(PDBGF_CONSISTENCY, "mkstemp (fd %d) succeeded, but fstat failed (%s).\n",
- rc, strerror(errno));
- pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, 0);
- }
- errno = save_errno;
- }
- /* mkstemp only changes the XXXXXX at the end. */
- memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6);
/* return rc;
* }
*/
diff --git a/ports/linux/portdefs.h b/ports/linux/portdefs.h
index d419365..9545550 100644
--- a/ports/linux/portdefs.h
+++ b/ports/linux/portdefs.h
@@ -32,3 +32,24 @@ GLIBC_COMPAT_SYMBOL(memcpy,2.0);
#include <linux/capability.h>
#include <sys/syscall.h>
+#include <sys/prctl.h>
+#include <linux/seccomp.h>
+
+#ifndef _STAT_VER
+#if defined (__aarch64__)
+#define _STAT_VER 0
+#elif defined (__x86_64__)
+#define _STAT_VER 1
+#else
+#define _STAT_VER 3
+#endif
+#endif
+#ifndef _MKNOD_VER
+#if defined (__aarch64__)
+#define _MKNOD_VER 0
+#elif defined (__x86_64__)
+#define _MKNOD_VER 0
+#else
+#define _MKNOD_VER 1
+#endif
+#endif
diff --git a/ports/linux/pseudo_wrappers.c b/ports/linux/pseudo_wrappers.c
index cd7e173..ed34115 100644
--- a/ports/linux/pseudo_wrappers.c
+++ b/ports/linux/pseudo_wrappers.c
@@ -57,6 +57,7 @@ int pseudo_capset(cap_user_header_t hdrp, const cap_user_data_t datap) {
long
syscall(long number, ...) {
long rc = -1;
+ va_list ap;
if (!pseudo_check_wrappers() || !real_syscall) {
/* rc was initialized to the "failure" value */
@@ -77,6 +78,20 @@ syscall(long number, ...) {
(void) number;
#endif
+#ifdef SYS_seccomp
+ /* pseudo and seccomp are incompatible as pseudo uses different syscalls
+ * so pretend to enable seccomp but really do nothing */
+ if (number == SYS_seccomp) {
+ unsigned long cmd;
+ va_start(ap, number);
+ cmd = va_arg(ap, unsigned long);
+ va_end(ap);
+ if (cmd == SECCOMP_SET_MODE_FILTER) {
+ return 0;
+ }
+ }
+#endif
+
/* gcc magic to attempt to just pass these args to syscall. we have to
* guess about the number of args; the docs discuss calling conventions
* up to 7, so let's try that?
@@ -92,3 +107,44 @@ static long wrap_syscall(long nr, va_list ap) {
(void) ap;
return -1;
}
+
+int
+prctl(int option, ...) {
+ int rc = -1;
+ va_list ap;
+
+ if (!pseudo_check_wrappers() || !real_prctl) {
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("prctl");
+ return rc;
+ }
+
+#ifdef SECCOMP_SET_MODE_FILTER
+ /* pseudo and seccomp are incompatible as pseudo uses different syscalls
+ * so pretend to enable seccomp but really do nothing */
+ if (option == PR_SET_SECCOMP) {
+ unsigned long cmd;
+ va_start(ap, option);
+ cmd = va_arg(ap, unsigned long);
+ va_end(ap);
+ if (cmd == SECCOMP_SET_MODE_FILTER) {
+ return 0;
+ }
+ }
+#endif
+
+ /* gcc magic to attempt to just pass these args to prctl. we have to
+ * guess about the number of args; the docs discuss calling conventions
+ * up to 5, so let's try that?
+ */
+ void *res = __builtin_apply((void (*)()) real_prctl, __builtin_apply_args(), sizeof(long) * 5);
+ __builtin_return(res);
+}
+
+/* unused.
+ */
+static int wrap_prctl(int option, va_list ap) {
+ (void) option;
+ (void) ap;
+ return -1;
+}
diff --git a/ports/linux/statx/guts/statx.c b/ports/linux/statx/guts/statx.c
new file mode 100644
index 0000000..42aebe5
--- /dev/null
+++ b/ports/linux/statx/guts/statx.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Linux Foundation
+ * Author: Richard Purdie
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * int
+ * statx(int dirfd, const char *path, int flags, unsigned int mask, struct statx *statxbuf) {
+ * int rc = -1;
+ */
+ pseudo_msg_t *msg;
+ PSEUDO_STATBUF buf;
+ int save_errno;
+
+ rc = real_statx(dirfd, path, flags, mask, statxbuf);
+ save_errno = errno;
+ if (rc == -1) {
+ return rc;
+ }
+
+ buf.st_uid = statxbuf->stx_uid;
+ buf.st_gid = statxbuf->stx_gid;
+ buf.st_dev = makedev(statxbuf->stx_dev_major, statxbuf->stx_dev_minor);
+ buf.st_ino = statxbuf->stx_ino;
+ buf.st_mode = statxbuf->stx_mode;
+ buf.st_rdev = makedev(statxbuf->stx_rdev_major, statxbuf->stx_rdev_minor);
+ buf.st_nlink = statxbuf->stx_nlink;
+ msg = pseudo_client_op(OP_STAT, 0, -1, dirfd, path, &buf);
+ if (msg && msg->result == RESULT_SUCCEED) {
+ pseudo_debug(PDBGF_FILE, "statx(path %s), flags %o, stat rc %d, stat uid %o\n", path, flags, rc, statxbuf->stx_uid);
+ statxbuf->stx_uid = msg->uid;
+ statxbuf->stx_gid = msg->gid;
+ statxbuf->stx_mode = msg->mode;
+ statxbuf->stx_rdev_major = major(msg->rdev);
+ statxbuf->stx_rdev_minor = minor(msg->rdev);
+ } else {
+ pseudo_debug(PDBGF_FILE, "statx(path %s) failed, flags %o, stat rc %d, stat uid %o\n", path, flags, rc, statxbuf->stx_uid);
+ }
+ errno = save_errno;
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/statx/portdefs.h b/ports/linux/statx/portdefs.h
new file mode 100644
index 0000000..bf934dc
--- /dev/null
+++ b/ports/linux/statx/portdefs.h
@@ -0,0 +1,6 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
diff --git a/ports/linux/statx/wrapfuncs.in b/ports/linux/statx/wrapfuncs.in
new file mode 100644
index 0000000..168234f
--- /dev/null
+++ b/ports/linux/statx/wrapfuncs.in
@@ -0,0 +1 @@
+int statx(int dirfd, const char *path, int flags, unsigned int mask, struct statx *statxbuf);
diff --git a/ports/linux/subports b/ports/linux/subports
index a29044a..740ec83 100755
--- a/ports/linux/subports
+++ b/ports/linux/subports
@@ -29,11 +29,12 @@ fi
if $port_xattr; then
cat > dummy.c <<EOF
#include <sys/types.h>
-#include <attr/xattr.h>
+#include <sys/xattr.h>
+#include <attr/attributes.h>
int i;
EOF
if ! ${CC} -c -o dummy.o dummy.c >/dev/null 2>&1; then
- echo >&2 "Warning: Can't compile trivial program using <attr/xattr.h>".
+ echo >&2 "Warning: Can't compile trivial program using <attr/attributes.h>".
echo >&2 " xattr support will require that header."
fi
echo "linux/xattr"
@@ -54,3 +55,13 @@ else
fi
rm -f dummy.c dummy.o
+cat > dummy.c <<EOF
+#define _GNU_SOURCE
+#include <sys/stat.h>
+struct statx x;
+EOF
+if ${CC} -c -o dummy.o dummy.c >/dev/null 2>&1; then
+ echo "linux/statx"
+fi
+rm -f dummy.c dummy.o
+
diff --git a/ports/linux/wrapfuncs.in b/ports/linux/wrapfuncs.in
index c5ea7c3..80221fc 100644
--- a/ports/linux/wrapfuncs.in
+++ b/ports/linux/wrapfuncs.in
@@ -1,23 +1,24 @@
-int open(const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW */
+int open(const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
char *get_current_dir_name(void);
int __xstat(int ver, const char *path, struct stat *buf);
int __lxstat(int ver, const char *path, struct stat *buf); /* flags=AT_SYMLINK_NOFOLLOW */
int __fxstat(int ver, int fd, struct stat *buf);
+int lchmod(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */
int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */
int __fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags);
-int openat(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW */
-int __openat_2(int dirfd, const char *path, int flags); /* flags=flags&O_NOFOLLOW */
+int openat(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
+int __openat_2(int dirfd, const char *path, int flags); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
int mknod(const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknod */
int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknodat */
int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW */
int __xmknodat(int ver, int dirfd, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW */
-int fcntl(int fd, int cmd, ...{struct flock *lock});
+int fcntl(int fd, int cmd, ...{struct flock *lock}); /* noignore_path=1 */
# just so we know the inums of symlinks
char *canonicalize_file_name(const char *filename);
int eaccess(const char *path, int mode);
-int open64(const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW */
-int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW */
-int __openat64_2(int dirfd, const char *path, int flags); /* flags=flags&O_NOFOLLOW */
+int open64(const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
+int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
+int __openat64_2(int dirfd, const char *path, int flags); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
int creat64(const char *path, mode_t mode);
int stat(const char *path, struct stat *buf); /* real_func=pseudo_stat */
int lstat(const char *path, struct stat *buf); /* real_func=pseudo_lstat, flags=AT_SYMLINK_NOFOLLOW */
@@ -29,13 +30,14 @@ int __xstat64(int ver, const char *path, struct stat64 *buf);
int __lxstat64(int ver, const char *path, struct stat64 *buf); /* flags=AT_SYMLINK_NOFOLLOW */
int __fxstat64(int ver, int fd, struct stat64 *buf);
int __fxstatat64(int ver, int dirfd, const char *path, struct stat64 *buf, int flags);
-FILE *fopen64(const char *path, const char *mode);
-int nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag);
-FILE *freopen64(const char *path, const char *mode, FILE *stream);
+FILE *fopen64(const char *path, const char *mode); /* noignore_path=1 */
+int nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag); /* noignore_path=1 */
+FILE *freopen64(const char *path, const char *mode, FILE *stream); /* noignore_path=1 */
int ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd);
int glob64(const char *pattern, int flags, int (*errfunc)(const char *, int), glob64_t *pglob);
int scandir64(const char *path, struct dirent64 ***namelist, int (*filter)(const struct dirent64 *), int (*compar)());
int truncate64(const char *path, off64_t length);
+int mkostemp64(char *template, int oflags); /* flags=AT_SYMLINK_NOFOLLOW */
int mkstemp64(char *template); /* flags=AT_SYMLINK_NOFOLLOW */
int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups);
int setgroups(size_t size, const gid_t *list);
@@ -56,3 +58,4 @@ int getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbuf
int capset(cap_user_header_t hdrp, const cap_user_data_t datap); /* real_func=pseudo_capset */
long syscall(long nr, ...); /* hand_wrapped=1 */
int renameat2(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags); /* flags=AT_SYMLINK_NOFOLLOW */
+int prctl(int option, ...); /* hand_wrapped=1 */
diff --git a/ports/linux/xattr/portdefs.h b/ports/linux/xattr/portdefs.h
index 56cd3ca..068d39a 100644
--- a/ports/linux/xattr/portdefs.h
+++ b/ports/linux/xattr/portdefs.h
@@ -2,5 +2,6 @@
* SPDX-License-Identifier: LGPL-2.1-only
*
*/
-#include <attr/xattr.h>
+#include <sys/xattr.h>
+#include <attr/attributes.h>
#include <stdint.h>
diff --git a/ports/linux/xattr/pseudo_wrappers.c b/ports/linux/xattr/pseudo_wrappers.c
index 590af30..0b65920 100644
--- a/ports/linux/xattr/pseudo_wrappers.c
+++ b/ports/linux/xattr/pseudo_wrappers.c
@@ -134,7 +134,7 @@ static ssize_t shared_getxattr(const char *path, int fd, const char *name, void
pseudo_debug(PDBGF_XATTR, "getxattr(%s [fd %d], %s)\n",
path ? path : "<no path>", fd, name);
pseudo_msg_t *result = pseudo_client_op(OP_GET_XATTR, 0, fd, -1, path, &buf, name);
- if (result->result != RESULT_SUCCEED) {
+ if (!result || result->result != RESULT_SUCCEED) {
errno = ENOATTR;
return -1;
}
@@ -197,7 +197,7 @@ static int shared_setxattr(const char *path, int fd, const char *name, const voi
mode |= get_special_bits(path, fd);
pseudo_debug(PDBGF_XATTR, "posix_acl_access translated to mode %04o. Remaining attribute(s): %d.\n",
mode, extra);
- buf.st_mode = mode;
+
/* we want to actually issue a corresponding chmod,
* as well, or else the file ends up 0600 on the
* host. Using the slightly-less-efficient wrap_chmod
@@ -254,7 +254,7 @@ static int shared_setxattr(const char *path, int fd, const char *name, const voi
static ssize_t shared_listxattr(const char *path, int fd, char *list, size_t size) {
RC_AND_BUF
pseudo_msg_t *result = pseudo_client_op(OP_LIST_XATTR, 0, fd, -1, path, &buf);
- if (result->result != RESULT_SUCCEED) {
+ if (!result || result->result != RESULT_SUCCEED) {
pseudo_debug(PDBGF_XATTR, "listxattr: no success.\n");
errno = ENOATTR;
return -1;
@@ -276,7 +276,7 @@ static int shared_removexattr(const char *path, int fd, const char *name) {
RC_AND_BUF
pseudo_msg_t *result = pseudo_client_op(OP_REMOVE_XATTR, 0, fd, -1, path, &buf, name);
- if (result->result != RESULT_SUCCEED) {
+ if (!result || result->result != RESULT_SUCCEED) {
/* docs say ENOATTR, but I don't have one */
errno = ENOENT;
return -1;
diff --git a/ports/linux/xattr/wrapfuncs.in b/ports/linux/xattr/wrapfuncs.in
index c37f78a..09eba23 100644
--- a/ports/linux/xattr/wrapfuncs.in
+++ b/ports/linux/xattr/wrapfuncs.in
@@ -1,12 +1,12 @@
-ssize_t getxattr(const char *path, const char *name, void *value, size_t size); /* flags=0 */
-ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); /* flags=AT_SYMLINK_NOFOLLOW */
-ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size);
-int setxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=0 */
-int lsetxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=AT_SYMLINK_NOFOLLOW */
-int fsetxattr(int filedes, const char *name, const void *value, size_t size, int xflags);
-ssize_t listxattr(const char *path, char *list, size_t size); /* flags=0 */
-ssize_t llistxattr(const char *path, char *list, size_t size); /* flags=AT_SYMLINK_NOFOLLOW */
-ssize_t flistxattr(int filedes, char *list, size_t size);
-int removexattr(const char *path, const char *name); /* flags=0 */
-int lremovexattr(const char *path, const char *name); /* flags=AT_SYMLINK_NOFOLLOW */
-int fremovexattr(int filedes, const char *name);
+ssize_t getxattr(const char *path, const char *name, void *value, size_t size); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+int setxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+int lsetxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+int fsetxattr(int filedes, const char *name, const void *value, size_t size, int xflags); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+ssize_t listxattr(const char *path, char *list, size_t size); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+ssize_t llistxattr(const char *path, char *list, size_t size); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+ssize_t flistxattr(int filedes, char *list, size_t size); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+int removexattr(const char *path, const char *name); /* flags=0, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+int lremovexattr(const char *path, const char *name); /* flags=AT_SYMLINK_NOFOLLOW, version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
+int fremovexattr(int filedes, const char *name); /* version="GLIBC_2.3", version-aarch64="GLIBC_2.17" */
diff --git a/ports/unix/guts/fchmodat.c b/ports/unix/guts/fchmodat.c
index 55dbd35..d1ac7bb 100644
--- a/ports/unix/guts/fchmodat.c
+++ b/ports/unix/guts/fchmodat.c
@@ -11,16 +11,16 @@
PSEUDO_STATBUF buf;
int save_errno = errno;
- if (flags & AT_SYMLINK_NOFOLLOW) {
- errno = ENOTSUP;
- return -1;
- }
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (dirfd != AT_FDCWD) {
errno = ENOSYS;
return -1;
}
- rc = base_stat(path, &buf);
+ if (flags & AT_SYMLINK_NOFOLLOW) {
+ rc = base_lstat(path, &buf);
+ } else {
+ rc = base_stat(path, &buf);
+ }
#else
rc = base_fstatat(dirfd, path, &buf, flags);
#endif
diff --git a/ports/unix/guts/linkat.c b/ports/unix/guts/linkat.c
index 381f9d0..7d8dff4 100644
--- a/ports/unix/guts/linkat.c
+++ b/ports/unix/guts/linkat.c
@@ -116,6 +116,7 @@
* if the thing linked is a symlink.
*/
pseudo_client_op(OP_LINK, 0, -1, -1, newpath, &buf);
+ pseudo_client_linked_paths(oldpath, newpath);
errno = save_errno;
diff --git a/ports/unix/guts/realpath.c b/ports/unix/guts/realpath.c
index 085d2cb..8d8118b 100644
--- a/ports/unix/guts/realpath.c
+++ b/ports/unix/guts/realpath.c
@@ -14,7 +14,14 @@
errno = ENAMETOOLONG;
return NULL;
}
- if ((len = strlen(rname)) >= pseudo_sys_path_max()) {
+ len = strlen(rname);
+ char *ep = rname + len - 1;
+ while (ep > rname && *ep == '/') {
+ --len;
+ *(ep--) = '\0';
+ }
+
+ if (len >= pseudo_sys_path_max()) {
errno = ENAMETOOLONG;
return NULL;
}
diff --git a/ports/unix/guts/rename.c b/ports/unix/guts/rename.c
index 5073c71..7e4a499 100644
--- a/ports/unix/guts/rename.c
+++ b/ports/unix/guts/rename.c
@@ -29,6 +29,14 @@
newrc = base_lstat(newpath, &newbuf);
oldrc = base_lstat(oldpath, &oldbuf);
+ /* 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) {
+ pseudo_debug(PDBGF_OP, "rename: paths are the same\n");
+ return real_rename(oldpath, newpath);
+ }
+
errno = save_errno;
/* newpath must be removed. */
@@ -58,12 +66,6 @@
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
diff --git a/ports/unix/guts/renameat.c b/ports/unix/guts/renameat.c
index 735a60a..8064818 100644
--- a/ports/unix/guts/renameat.c
+++ b/ports/unix/guts/renameat.c
@@ -41,6 +41,14 @@
newrc = base_fstatat(newdirfd, newpath, &newbuf, AT_SYMLINK_NOFOLLOW);
#endif
+ /* 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) {
+ pseudo_debug(PDBGF_OP, "renameat: paths are the same\n");
+ return real_renameat(olddirfd, oldpath, newdirfd, newpath);
+ }
+
errno = save_errno;
/* newpath must be removed. */
@@ -71,12 +79,6 @@
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
diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in
index 3910fae..bd2706f 100644
--- a/ports/unix/wrapfuncs.in
+++ b/ports/unix/wrapfuncs.in
@@ -1,14 +1,14 @@
int creat(const char *path, mode_t mode);
char *getcwd(char *buf, size_t size);
char *getwd(char *buf);
-int close(int fd);
+int close(int fd); /* noignore_path=1 */
int fchmod(int fd, mode_t mode);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */
-int dup2(int oldfd, int newfd);
-int dup(int fd);
-int chdir(const char *path);
-int fchdir(int dirfd);
+int dup2(int oldfd, int newfd); /* noignore_path=1 */
+int dup(int fd); /* noignore_path=1 */
+int chdir(const char *path); /* noignore_path=1 */
+int fchdir(int dirfd); /* noignore_path=1 */
int access(const char *path, int mode);
FTS *fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)); /* inode64=1 */
int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd);
@@ -20,18 +20,18 @@ char *mktemp(char *template);
long pathconf(const char *path, int name);
char *realpath(const char *name, char *resolved_name); /* version="GLIBC_2.3" */
int remove(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */
-DIR *opendir(const char *path);
-int closedir(DIR *dirp);
+DIR *opendir(const char *path); /* noignore_path=1 */
+int closedir(DIR *dirp); /* noignore_path=1 */
char *tempnam(const char *template, const char *pfx);
char *tmpnam(char *s);
int truncate(const char *path, off_t length);
int utime(const char *path, const struct utimbuf *buf);
int utimes(const char *path, const struct timeval *times);
# needed because libc stdio does horrible things with inline asm syscalls
-FILE *fopen(const char *path, const char *mode);
-int fclose(FILE *fp);
-FILE *freopen(const char *path, const char *mode, FILE *stream);
-int chroot(const char *path);
+FILE *fopen(const char *path, const char *mode); /* noignore_path=1 */
+int fclose(FILE *fp); /* noignore_path=1 */
+FILE *freopen(const char *path, const char *mode, FILE *stream); /* noignore_path=1 */
+int chroot(const char *path); /* noignore_path=1 */
int acct(const char *path);
int chmod(const char *path, mode_t mode);
int chown(const char *path, uid_t owner, gid_t group);
diff --git a/pseudo.c b/pseudo.c
index 0f5850e..f2e2f87 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -245,10 +245,12 @@ main(int argc, char *argv[]) {
/* Options are processed, preserve them... */
pseudo_set_value("PSEUDO_OPTS", opts);
- if (!pseudo_get_prefix(argv[0])) {
+ s = pseudo_get_prefix(argv[0]);
+ if (!s) {
pseudo_diag("Can't figure out prefix. Set PSEUDO_PREFIX or invoke with full path.\n");
exit(PSEUDO_EXIT_PSEUDO_PREFIX);
}
+ free(s);
/* move database */
if (opt_m || opt_M) {
@@ -695,16 +697,15 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **respon
msg->path);
pdb_did_unlink_file(path_by_ino, &by_ino, by_ino.deleting);
} else {
- int flags = 0;
- if (msg->nlink > 1) {
- flags = PDBGF_FILE | PDBGF_VERBOSE;
- }
- pseudo_debug(flags, "path mismatch [%d link%s]: ino %llu db '%s' req '%s'.\n",
+ pseudo_diag("path mismatch [%d link%s]: ino %llu db '%s' req '%s'.\n",
msg->nlink,
msg->nlink == 1 ? "" : "s",
(unsigned long long) msg_header.ino,
path_by_ino ? path_by_ino : "no path",
msg->path);
+ found_ino = 0;
+ msg->result = RESULT_ABORT;
+ goto op_exit;
}
}
} else {
@@ -1024,6 +1025,24 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **respon
break;
}
+ switch (msg->op) {
+ case OP_FCHOWN: /* FALLTHROUGH */
+ case OP_FCHMOD: /* FALLTHROUGH */
+ case OP_FSTAT:
+ if (!found_path && !found_ino && (msg->nlink == 0)) {
+ /* If nlink is 0 for an fchown/fchmod/fstat, we probably have an fd which is
+ * unlinked and we don't want to do inode/path matching against it. Marking it
+ * as may unlink gives the right hints in the database to ensure we
+ * handle correctly whilst maintaining the permissions whilst the
+ * file exists for the fd. */
+ pdb_may_unlink_file(msg, msg->client);
+ }
+ break;
+ default:
+ break;
+ }
+
+op_exit:
/* in the case of an exact match, we just used the pointer
* rather than allocating space.
*/
@@ -1087,9 +1106,15 @@ pseudo_db_check(int fix) {
int fixup_needed = 0;
pseudo_debug(PDBGF_DB, "Checking <%s>\n", m->path);
if (lstat(m->path, &buf)) {
- errors = EXIT_FAILURE;
- pseudo_diag("can't stat <%s>\n", m->path);
- continue;
+ if (!fix) {
+ pseudo_diag("can't stat <%s>\n", m->path);
+ errors = EXIT_FAILURE;
+ continue;
+ } else {
+ pseudo_debug(PDBGF_DB, "can't stat <%s>\n", m->path);
+ fixup_needed = 2;
+ goto do_fixup;
+ }
}
/* can't check for device type mismatches, uid/gid, or
* permissions, because those are the very things we
@@ -1125,6 +1150,7 @@ pseudo_db_check(int fix) {
S_ISDIR(m->mode));
fixup_needed = 2;
}
+ do_fixup:
if (fixup_needed) {
/* in fixup mode, either delete (mismatches) or
* correct (dev/ino).
diff --git a/pseudo_client.c b/pseudo_client.c
index 478e450..f846d54 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -70,6 +70,8 @@ int pseudo_umask = 022;
static char **fd_paths = NULL;
static int nfds = 0;
+static char **linked_fd_paths = NULL;
+static int linked_nfds = 0;
static const char **passwd_paths = NULL;
static int npasswd_paths = 0;
#ifdef PSEUDO_PROFILING
@@ -429,6 +431,7 @@ pseudo_profile_report(void) {
void
pseudo_init_client(void) {
char *env;
+ int need_free = 0;
pseudo_antimagic();
pseudo_new_pid();
@@ -448,9 +451,11 @@ pseudo_init_client(void) {
* or it may have gone away, in which case we'd enable
* pseudo (and cause it to reinit the defaults).
*/
+ need_free = 0;
env = getenv("PSEUDO_DISABLED");
if (!env) {
env = pseudo_get_value("PSEUDO_DISABLED");
+ need_free = 1;
}
if (env) {
int actually_disabled = 1;
@@ -485,15 +490,19 @@ pseudo_init_client(void) {
} else {
pseudo_set_value("PSEUDO_DISABLED", "0");
}
+ if (need_free)
+ free(env);
/* ALLOW_FSYNC is here because some crazy hosts will otherwise
* report incorrect values for st_size/st_blocks. I can sort of
* understand st_blocks, but bogus values for st_size? Not cool,
* dudes, not cool.
*/
+ need_free = 0;
env = getenv("PSEUDO_ALLOW_FSYNC");
if (!env) {
env = pseudo_get_value("PSEUDO_ALLOW_FSYNC");
+ need_free = 1;
} else {
pseudo_set_value("PSEUDO_ALLOW_FSYNC", env);
}
@@ -502,6 +511,8 @@ pseudo_init_client(void) {
} else {
pseudo_allow_fsync = 0;
}
+ if (need_free)
+ free(env);
/* in child processes, PSEUDO_UNLOAD may become set to
* some truthy value, in which case we're being asked to
@@ -822,6 +833,8 @@ pseudo_client_chroot(const char *path) {
}
memcpy(pseudo_chroot, path, pseudo_chroot_len + 1);
pseudo_set_value("PSEUDO_CHROOT", pseudo_chroot);
+ /* Rebuild passwd paths since we've done a chroot */
+ build_passwd_paths();
return 0;
}
@@ -889,32 +902,73 @@ fd_path(int fd) {
}
static void
-pseudo_client_path(int fd, const char *path) {
+pseudo_client_path_set(int fd, const char *path, char ***patharray, int *len) {
if (fd < 0)
return;
- if (fd >= nfds) {
+ if (fd >= *len) {
int i;
pseudo_debug(PDBGF_CLIENT, "expanding fds from %d to %d\n",
- nfds, fd + 1);
- fd_paths = realloc(fd_paths, (fd + 1) * sizeof(char *));
- for (i = nfds; i < fd + 1; ++i)
- fd_paths[i] = 0;
- nfds = fd + 1;
+ *len, fd + 1);
+ (*patharray) = realloc((*patharray), (fd + 1) * sizeof(char *));
+ for (i = *len; i < fd + 1; ++i)
+ (*patharray)[i] = 0;
+ *len = fd + 1;
} else {
- if (fd_paths[fd]) {
+ if ((*patharray)[fd]) {
pseudo_debug(PDBGF_CLIENT, "reopening fd %d [%s] -- didn't see close\n",
- fd, fd_paths[fd]);
+ fd, (*patharray)[fd]);
}
/* yes, it is safe to free null pointers. yay for C89 */
- free(fd_paths[fd]);
- fd_paths[fd] = 0;
+ free((*patharray)[fd]);
+ (*patharray)[fd] = 0;
}
if (path) {
- fd_paths[fd] = strdup(path);
+ (*patharray)[fd] = strdup(path);
+ }
+}
+
+static void
+pseudo_client_path(int fd, const char *path) {
+ pseudo_client_path_set(fd, path, &fd_paths, &nfds);
+}
+
+void
+pseudo_client_linked_paths(const char *oldpath, const char *newpath) {
+ int fd;
+ for (fd = 3; fd < nfds; ++fd) {
+ if (fd_paths[fd] && !strcmp(oldpath, fd_paths[fd])) {
+ pseudo_client_path_set(fd, newpath, &linked_fd_paths, &linked_nfds);
+ }
+ }
+}
+
+static void
+pseudo_client_rename_path(const char *oldpath, const char *newpath) {
+ int fd;
+ for (fd = 3; fd < nfds; ++fd) {
+ if (fd_paths[fd] && !strcmp(oldpath, fd_paths[fd])) {
+ pseudo_client_path(fd, newpath);
+ }
+ }
+ for (fd = 0; fd < linked_nfds; ++fd) {
+ if (linked_fd_paths[fd] && fd_paths[fd] && !strcmp(oldpath, linked_fd_paths[fd])) {
+ pseudo_client_path_set(fd, newpath, &linked_fd_paths, &linked_nfds);
+ }
+ }
+}
+
+static void
+pseudo_client_unlinked_path(const char *path) {
+ int fd;
+ for (fd = 0; fd < linked_nfds; ++fd) {
+ if (linked_fd_paths[fd] && fd_paths[fd] && !strcmp(path, fd_paths[fd])) {
+ pseudo_client_path(fd, linked_fd_paths[fd]);
+ }
}
}
+
static void
pseudo_client_close(int fd) {
if (fd < 0 || fd >= nfds)
@@ -922,6 +976,11 @@ pseudo_client_close(int fd) {
free(fd_paths[fd]);
fd_paths[fd] = 0;
+
+ if (fd < linked_nfds) {
+ free(linked_fd_paths[fd]);
+ linked_fd_paths[fd] = 0;
+ }
}
/* spawn server */
@@ -1271,7 +1330,7 @@ pseudo_client_setup(void) {
}
}
-#define PSEUDO_RETRIES 20
+#define PSEUDO_RETRIES 250
static pseudo_msg_t *
pseudo_client_request(pseudo_msg_t *msg, size_t len, const char *path) {
pseudo_msg_t *response = 0;
@@ -1436,8 +1495,12 @@ base_path(int dirfd, const char *path, int leave_last) {
if (!path)
return NULL;
- if (!*path)
+
+ if (!*path) {
+ if (dirfd != -1 && dirfd != AT_FDCWD)
+ return fd_path(dirfd);
return "";
+ }
if (path[0] != '/') {
if (dirfd != -1 && dirfd != AT_FDCWD) {
@@ -1482,6 +1545,47 @@ base_path(int dirfd, const char *path, int leave_last) {
return newpath;
}
+int pseudo_client_ignore_fd(int fd) {
+ if (fd >= 0 && fd <= nfds)
+ return pseudo_client_ignore_path(fd_path(fd));
+ return 0;
+}
+
+int pseudo_client_ignore_path_chroot(const char *path, int ignore_chroot) {
+ char *env;
+
+ if (!path)
+ return 0;
+
+ if (ignore_chroot && pseudo_chroot && strncmp(path, pseudo_chroot, pseudo_chroot_len) == 0)
+ return 0;
+
+ env = pseudo_get_value("PSEUDO_IGNORE_PATHS");
+ if (!env)
+ return 0;
+
+ int ret = 0;
+ char *p = env;
+ while (p) {
+ char *next = strchr(p, ',');
+ if (next)
+ *next++ = '\0';
+ if (*p && !strncmp(path, p, strlen(p))) {
+ pseudo_debug(PDBGF_PATH | PDBGF_VERBOSE, "ignoring path: '%s'\n", path);
+ ret = 1;
+ break;
+ }
+ p = next;
+ }
+ free(env);
+
+ return ret;
+}
+
+int pseudo_client_ignore_path(const char *path) {
+ return pseudo_client_ignore_path_chroot(path, 1);
+}
+
pseudo_msg_t *
pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path, const PSEUDO_STATBUF *buf, ...) {
pseudo_msg_t *result = 0;
@@ -1522,6 +1626,16 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
}
}
+ if (op != OP_CHROOT && op != OP_CHDIR && op != OP_CLOSE && op != OP_DUP
+ && pseudo_client_ignore_path_chroot(path, 0)) {
+ if (op == OP_OPEN) {
+ pseudo_client_path(fd, path);
+ }
+ /* reenable wrappers */
+ pseudo_magic();
+ return result;
+ }
+
#ifdef PSEUDO_XATTRDB
if (buf) {
struct stat64 bufcopy = *buf;
@@ -1772,7 +1886,8 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
break;
case OP_OPEN:
pseudo_client_path(fd, path);
- case OP_EXEC: /* fallthrough */
+ /* fallthrough */
+ case OP_EXEC:
do_request = pseudo_client_logging;
break;
case OP_CLOSE:
@@ -1813,6 +1928,12 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
dirfd);
pseudo_client_path(dirfd, fd_path(fd));
break;
+ case OP_UNLINK:
+ case OP_DID_UNLINK:
+ if (path)
+ pseudo_client_unlinked_path(path);
+ do_request = 1;
+ break;
/* operations for which we should use the magic uid/gid */
case OP_CHMOD:
case OP_CREAT:
@@ -1833,10 +1954,7 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
case OP_FCHOWN:
case OP_FSTAT:
case OP_LINK:
- case OP_RENAME:
case OP_STAT:
- case OP_UNLINK:
- case OP_DID_UNLINK:
case OP_CANCEL_UNLINK:
case OP_MAY_UNLINK:
case OP_GET_XATTR:
@@ -1845,6 +1963,10 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
case OP_REMOVE_XATTR:
do_request = 1;
break;
+ case OP_RENAME:
+ pseudo_client_rename_path(path_extra_1, path);
+ do_request = 1;
+ break;
default:
pseudo_diag("error: unknown or unimplemented operator %d (%s)", op, pseudo_op_name(op));
break;
@@ -1876,6 +1998,12 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
#endif
if (result) {
pseudo_debug(PDBGF_OP, "(%d) %s", getpid(), pseudo_res_name(result->result));
+ if (result->result == RESULT_ABORT) {
+ char *local_state_dir = pseudo_get_value("PSEUDO_LOCALSTATEDIR");
+ pseudo_diag("abort()ing pseudo client by server request. See https://wiki.yoctoproject.org/wiki/Pseudo_Abort for more details on this.\n"
+ "Check logfile: %s/%s\n", local_state_dir, PSEUDO_LOGFILE);
+ abort();
+ }
if (op == OP_STAT || op == OP_FSTAT) {
pseudo_debug(PDBGF_OP, " mode 0%o uid %d:%d",
(int) result->mode,
diff --git a/pseudo_client.h b/pseudo_client.h
index 457b095..d7944ce 100644
--- a/pseudo_client.h
+++ b/pseudo_client.h
@@ -7,6 +7,9 @@
*
*/
extern pseudo_msg_t *pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path, const PSEUDO_STATBUF *buf, ...);
+extern int pseudo_client_ignore_path(const char *path);
+extern int pseudo_client_ignore_fd(int fd);
+extern void pseudo_client_linked_paths(const char *oldpath, const char *newpath);
#if PSEUDO_STATBUF_64
#define base_lstat real_lstat64
#define base_fstat real_fstat64
diff --git a/pseudo_db.c b/pseudo_db.c
index 92e4f50..ebf6e08 100644
--- a/pseudo_db.c
+++ b/pseudo_db.c
@@ -158,11 +158,24 @@ static struct sql_index {
static char *file_pragmas[] = {
"PRAGMA legacy_file_format = OFF;",
- "PRAGMA journal_mode = OFF;",
+#ifdef USE_MEMORY_DB
/* the default page size produces painfully bad behavior
* for memory databases with some versions of sqlite.
*/
"PRAGMA page_size = 8192;",
+ "PRAGMA journal_mode = OFF;",
+#else
+ /* Use WAL mode when using the on-disk database. If user care about
+ * performance, they can use the in-memory database, but if they care
+ * more about resilience, they can disable it and WAL mode will prevent
+ * corruption of the on-disk database (for a slight performance
+ * penalty). Note that the database still keeps synchronous to OFF,
+ * meaning its resilient to the pseudo process crashing or being killed
+ * unexpectedly, but not to the OS crashing and losing buffered disk
+ * state
+ */
+ "PRAGMA journal_mode = WAL;",
+#endif
"PRAGMA locking_mode = EXCLUSIVE;",
/* Setting this to NORMAL makes pseudo noticably slower
* than fakeroot, but is perhaps more secure. However,
diff --git a/pseudo_ipc.h b/pseudo_ipc.h
index caeae5c..d945257 100644
--- a/pseudo_ipc.h
+++ b/pseudo_ipc.h
@@ -29,7 +29,7 @@ typedef struct {
char path[];
} pseudo_msg_t;
-enum {
+typedef enum {
PSA_EXEC = 1,
PSA_WRITE = (PSA_EXEC << 1),
PSA_READ = (PSA_WRITE << 1),
diff --git a/pseudo_server.c b/pseudo_server.c
index 898aab4..84cef05 100644
--- a/pseudo_server.c
+++ b/pseudo_server.c
@@ -792,6 +792,7 @@ pseudo_server_loop(void) {
struct sigaction eat_usr2 = {
.sa_handler = set_do_list_clients
};
+ int hitmaxfiles;
clients = malloc(16 * sizeof(*clients));
@@ -810,6 +811,7 @@ pseudo_server_loop(void) {
active_clients = 1;
max_clients = 16;
highest_client = 0;
+ hitmaxfiles = 0;
pseudo_debug(PDBGF_SERVER, "server loop started.\n");
if (listen_fd < 0) {
@@ -868,10 +870,15 @@ pseudo_server_loop(void) {
} else {
serve_client(i);
}
+ } else if (hitmaxfiles) {
+ /* Only close one per loop iteration in the interests of caution */
+ close_client(i);
+ hitmaxfiles = 0;
}
if (die_forcefully)
break;
}
+ hitmaxfiles = 0;
if (!die_forcefully &&
(FD_ISSET(clients[0].fd, &events) ||
FD_ISSET(clients[0].fd, &reads))) {
@@ -893,6 +900,9 @@ pseudo_server_loop(void) {
*/
pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT;
die_peacefully = 0;
+ } else if (errno == EMFILE) {
+ hitmaxfiles = 1;
+ pseudo_debug(PDBGF_SERVER, "Hit max open files, dropping a client.\n");
}
}
pseudo_debug(PDBGF_SERVER, "server loop complete [%d clients left]\n", active_clients);
diff --git a/pseudo_util.c b/pseudo_util.c
index c867ed6..b6980c2 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -43,6 +43,7 @@ static struct pseudo_variables pseudo_env[] = {
{ "PSEUDO_BINDIR", 13, NULL },
{ "PSEUDO_LIBDIR", 13, NULL },
{ "PSEUDO_LOCALSTATEDIR", 20, NULL },
+ { "PSEUDO_IGNORE_PATHS", 19, NULL },
{ "PSEUDO_PASSWD", 13, NULL },
{ "PSEUDO_CHROOT", 13, NULL },
{ "PSEUDO_UIDS", 11, NULL },
@@ -158,7 +159,7 @@ pseudo_get_value(const char *key) {
if (pseudo_util_initted == -1)
pseudo_init_util();
- for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++)
+ for (i = 0; pseudo_env[i].key && strcmp(pseudo_env[i].key, key); i++)
;
/* Check if the environment has it and we don't ...
@@ -187,7 +188,7 @@ pseudo_set_value(const char *key, const char *value) {
if (pseudo_util_initted == -1)
pseudo_init_util();
- for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++)
+ for (i = 0; pseudo_env[i].key && strcmp(pseudo_env[i].key, key); i++)
;
if (pseudo_env[i].key) {
@@ -966,6 +967,7 @@ pseudo_setupenv() {
}
snprintf(newenv, len, "%s:%s64", libdir_path, libdir_path);
SETENV(PRELINK_PATH, newenv, 1);
+ free(newenv);
} else if (!strstr(ld_library_path, libdir_path)) {
size_t len = strlen(ld_library_path) + 1 + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1;
char *newenv = malloc(len);
@@ -974,6 +976,7 @@ pseudo_setupenv() {
}
snprintf(newenv, len, "%s:%s:%s64", ld_library_path, libdir_path, libdir_path);
SETENV(PRELINK_PATH, newenv, 1);
+ free(newenv);
} else {
/* nothing to do, ld_library_path exists and contains
* our preferred path */
diff --git a/templates/wrapfuncs.c b/templates/wrapfuncs.c
index 3859183..93bb671 100644
--- a/templates/wrapfuncs.c
+++ b/templates/wrapfuncs.c
@@ -60,9 +60,15 @@ ${maybe_async_skip}
${rc_assign} (*real_${name})(${call_args});
} else {
${fix_paths}
- /* exec*() use this to restore the sig mask */
- pseudo_saved_sigmask = saved;
- ${rc_assign} wrap_$name(${call_args});
+ if (${ignore_paths}) {
+ /* call the real syscall */
+ pseudo_debug(PDBGF_SYSCALL, "${name} ignored path, calling real syscall.\n");
+ ${rc_assign} (*real_${name})(${call_args});
+ } else {
+ /* exec*() use this to restore the sig mask */
+ pseudo_saved_sigmask = saved;
+ ${rc_assign} wrap_$name(${call_args});
+ }
}
${variadic_end}
save_errno = errno;
diff --git a/test/test-acl.sh b/test/test-acl.sh
new file mode 100755
index 0000000..fb7d5ec
--- /dev/null
+++ b/test/test-acl.sh
@@ -0,0 +1,188 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+# Return vals: 2 - Unable to run ACL commands, assertion failure
+# 1 - Invalid return value
+# 0 - Pass
+
+# NOTE: these test exclusively test setfacl -m
+
+set -u
+
+check_owner () {
+ local file="$1"
+ local expected="$2"
+ local msg="$3"
+ local actual=$(stat -c "%U" "$file")
+ if [ "$actual" != "$expected" ]
+ then
+ echo "$msg" "Fail, '$file' unexpected owner '$actual'"
+ exit 2
+ fi
+}
+
+check_group () {
+ local file="$1"
+ local expected="$2"
+ local msg="$3"
+ local actual=$(stat -c "%G" "$file")
+ if [ "$actual" != "$expected" ]
+ then
+ echo "$msg" "Fail, '$file' unexpected group '$actual'"
+ exit 2
+ fi
+}
+
+check_acl_contains () {
+ local file="$1"
+ local acl="$2"
+ local msg="$3"
+ IFS=',' read -ra acls <<< "$acl"
+ for pattern in "${acls[@]}"; do
+ result=$(getfacl -c "$file" | grep -o "^$pattern")
+ if [ "$result" != "$pattern" ]
+ then
+ echo "$msg" "Fail, did not find desired acl '$pattern' in '$file'"
+ exit 2
+ fi
+ done
+}
+
+check_acl_minimal () {
+ local file="$1"
+ local msg="${2:-''}"
+ local acls
+ acls=$(getfacl -c "${file}" | grep -v "::")
+ if [ -n "$acls" ]
+ then
+ echo "$msg" "Fail, '$file' unexpected getfacl result '$acls'"
+ exit 1
+ fi
+}
+
+test_modify_once () {
+ local file="$1"
+ local acl="$2"
+ local msg="${3:-''}"
+ # ensure that file is pristine
+ check_acl_minimal "$file" "$msg precondition:"
+ check_owner "$file" root "$msg precondition:"
+ check_group "$file" root "$msg precondition:"
+ if ! setfacl -m "$acl" "$file"
+ then
+ echo "$msg" "Fail, unable to call setfacl"
+ exit 2
+ fi
+ check_acl_contains "$file" "$acl" "$msg: acl not set:"
+ check_owner "$file" root "$msg owner corrupted:"
+ check_group "$file" root "$msg group corrupted:"
+}
+
+
+trap "rm -rf testdir" EXIT
+mkdir testdir || exit 1
+
+
+# user
+touch testdir/f1 || exit 1
+mkdir testdir/d1 || exit 1
+# regular file
+test_modify_once testdir/f1 "user:root:r" "$LINENO:"
+# directory
+test_modify_once testdir/d1 "user:root:r" "$LINENO:"
+rm -rf testdir/f1 testdir/d1
+
+#group
+rm -rf testdir/f1 testdir/d1
+touch testdir/f1 || exit 1
+mkdir testdir/d1 || exit 1
+# regular file
+test_modify_once testdir/f1 "group:root:r" "$LINENO:"
+# directory
+test_modify_once testdir/d1 "group:root:r" "$LINENO:"
+rm -rf testdir/f1 testdir/d1
+
+# multiple users
+touch testdir/f1 || exit 1
+mkdir testdir/d1 || exit 1
+# regular file
+test_modify_once testdir/f1 "user:root:r,group:root:r,user:bin:rw" "$LINENO:"
+# directory
+test_modify_once testdir/d1 "user:root:r,group:root:r,user:bin:rw" "$LINENO:"
+rm -rf testdir/f1 testdir/d1
+
+
+# setfacl default acls
+mkdir testdir/d1 || exit 1
+test_modify_once testdir/d1 "default:user:root:r,user:root:r" "$LINENO:"
+rm -rf testdir/d1
+
+
+# multiple calls to setfacl -m on same file
+touch testdir/f1 || exit 1
+mkdir testdir/d1 || exit 1
+check_owner testdir/f1 root "$LINENO: precondition:"
+check_group testdir/f1 root "$LINENO: precondition:"
+check_acl_minimal testdir/f1 "$LINENO: precondition:"
+
+acl1="user:root:r"
+acl2="user:bin:rw"
+
+if ! setfacl -m "$acl1" testdir/f1 # first setfacl
+then
+ echo "$LINENO:" "Fail, unable to call setfacl"
+ exit 2
+fi
+check_acl_contains testdir/f1 "$acl1" "$LINENO: acl1 not set:"
+check_owner testdir/f1 root "$LINENO: owner corrupted:"
+check_group testdir/f1 root "$LINENO: group corrupted:"
+
+if ! setfacl -m "$acl2" testdir/f1 # second setfacl
+then
+ echo "$LINENO:" "Fail, unable to call setfacl"
+ exit 2
+fi
+
+check_acl_contains testdir/f1 "$acl1" "$LINENO: acl1 not set:"
+check_acl_contains testdir/f1 "$acl2" "$LINENO: acl2 not set:"
+check_owner testdir/f1 root "$LINENO: owner corrupted:"
+check_group testdir/f1 root "$LINENO: group corrupted:"
+rm -rf testdir/f1 testdir/d1
+
+# setfacl recursive
+test_modify_recursive () {
+ local root_dir="$1"
+ local acl="$2"
+ local msg="${3:-''}"
+
+ find "$root_dir" | while read -r file; do
+ check_owner "$file" root "$msg precondition:"
+ check_group "$file" root "$msg precondition:"
+ check_acl_minimal "$file" "$msg precondition:"
+ done
+ if ! setfacl -R -m "$acl" "$root_dir"
+ then
+ echo "$msg" "Fail, unable to call setfacl"
+ exit 2
+ fi
+ find "$root_dir" | while read -r file; do
+ check_owner "$file" root "$msg owner corrupted:"
+ check_group "$file" root "$msg group corrupted:"
+ check_acl_contains "$file" "$acl" "$msg acl not set:"
+ done
+}
+
+mkdir -p testdir/d1/d2 || exit 1
+touch testdir/d1/d2/f1 || exit 1
+test_modify_recursive testdir/d1 "user:root:r,group:root:r,user:bin:rw" "$LINENO:"
+rm -rf testdir/d1
+
+mkdir -p testdir/d1/d2 || exit 1
+mkdir -p testdir/d1/d3 || exit 1
+test_modify_recursive testdir/d1 "default:user:root:rwx,user:root:r,group:root:r,user:bin:rw" "$LINENO:"
+rm -rf testdir/d1
+
+#echo "Passed."
+exit 0
diff --git a/test/test-rename-fstat.c b/test/test-rename-fstat.c
new file mode 100644
index 0000000..fb47c05
--- /dev/null
+++ b/test/test-rename-fstat.c
@@ -0,0 +1,23 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Test we can rename a file whilst holding an open fd which we fstat after renaming
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int main()
+{
+ struct stat buf;
+ int err;
+ int fd = open("test-rename-fstat1", O_RDONLY);
+ err = rename("test-rename-fstat1", "test-rename-fstat1");
+ if (err)
+ return err;
+ return fstat(fd, &buf);
+}
diff --git a/test/test-rename-fstat.sh b/test/test-rename-fstat.sh
new file mode 100755
index 0000000..4ac89b8
--- /dev/null
+++ b/test/test-rename-fstat.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+rm -f test-rename-fstat1 test-rename-fstat2
+touch test-rename-fstat1
+# Will abort if it fails
+./test/test-rename-fstat
+ecode=$?
+rm -f test-rename-fstat1 test-rename-fstat2
+exit $ecode