aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--ChangeLog.txt4
-rw-r--r--Makefile.in19
-rw-r--r--README33
-rw-r--r--SECURITY.md13
-rw-r--r--enums/op.in1
-rw-r--r--enums/res.in1
-rwxr-xr-xmaketables2
-rwxr-xr-xmakewrappers56
-rw-r--r--ports/linux/guts/close_range.c20
-rw-r--r--ports/linux/guts/closefrom.c16
-rw-r--r--ports/linux/guts/fcntl.c26
-rw-r--r--ports/linux/guts/fcntl64.c99
-rw-r--r--ports/linux/guts/fstatat.c15
-rw-r--r--ports/linux/guts/fstatat64.c15
-rw-r--r--ports/linux/guts/lchmod.c14
-rw-r--r--ports/linux/guts/mkostemp64.c53
-rw-r--r--ports/linux/guts/mkstemp64.c37
-rw-r--r--ports/linux/guts/openat.c26
-rw-r--r--ports/linux/nostatx/portdefs.h38
-rw-r--r--ports/linux/portdefs.h21
-rw-r--r--ports/linux/pseudo_wrappers.c58
-rw-r--r--ports/linux/statvfs/guts/statvfs64.c15
-rw-r--r--ports/linux/statvfs/wrapfuncs.in1
-rw-r--r--ports/linux/statx/guts/statx.c42
-rw-r--r--ports/linux/statx/portdefs.h6
-rw-r--r--ports/linux/statx/wrapfuncs.in1
-rwxr-xr-xports/linux/subports20
-rw-r--r--ports/linux/wrapfuncs.in28
-rw-r--r--ports/linux/xattr/portdefs.h6
-rw-r--r--ports/linux/xattr/pseudo_wrappers.c8
-rw-r--r--ports/linux/xattr/wrapfuncs.in24
-rw-r--r--ports/unix/guts/access.c2
-rw-r--r--ports/unix/guts/faccessat.c15
-rw-r--r--ports/unix/guts/faccessat2.c46
-rw-r--r--ports/unix/guts/fchmodat.c15
-rw-r--r--ports/unix/guts/linkat.c1
-rw-r--r--ports/unix/guts/realpath.c9
-rw-r--r--ports/unix/guts/rename.c49
-rw-r--r--ports/unix/guts/renameat.c48
-rwxr-xr-xports/unix/subports2
-rw-r--r--ports/unix/wrapfuncs.in24
-rw-r--r--pseudo.12
-rw-r--r--pseudo.c58
-rw-r--r--pseudo_client.c224
-rw-r--r--pseudo_client.h3
-rw-r--r--pseudo_db.c21
-rw-r--r--pseudo_db.h1
-rw-r--r--pseudo_ipc.h2
-rw-r--r--pseudo_server.c11
-rw-r--r--pseudo_util.c43
-rw-r--r--pseudo_wrappers.c2
-rw-r--r--pseudolog.c5
-rw-r--r--templates/wrapfuncs.c12
-rw-r--r--templates/wrapper_table8
-rwxr-xr-xtest/test-acl.sh188
-rw-r--r--test/test-chroot-symlink.c28
-rwxr-xr-xtest/test-chroot-symlink.sh19
-rw-r--r--test/test-fcntl.c58
-rwxr-xr-xtest/test-fcntl.sh5
-rw-r--r--test/test-fstat.c29
-rwxr-xr-xtest/test-fstat.sh8
-rw-r--r--test/test-openat.c58
-rwxr-xr-xtest/test-openat.sh7
-rwxr-xr-xtest/test-relative-from-root.sh14
-rw-r--r--test/test-rename-fstat.c23
-rwxr-xr-xtest/test-rename-fstat.sh12
-rw-r--r--test/test-statx.c20
-rwxr-xr-xtest/test-statx.sh6
-rwxr-xr-xtest/test-umask.sh1
70 files changed, 1575 insertions, 225 deletions
diff --git a/.gitignore b/.gitignore
index e6e11d9..bc4cbe8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
*.o
+*.pyc
Makefile
libpseudo.so
pseudo_wrapfuncs.*
@@ -14,3 +15,5 @@ pseudo_ports.h
templatefile.pyc
func_deps.mk
port_deps.mk
+test/test-*
+!test/test-*.*
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 329b262..f681596 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,7 @@
+2019-08-02:
+ * (seebs) Pass flags & O_NOFOLLOW, also use that to influence
+ stat types. Note. &. Not |.
+
2019-08-01:
* (seebs) Pass flags|O_NOFOLLOW on when resolving paths with openat.
diff --git a/Makefile.in b/Makefile.in
index b43d2d6..48fdbd2 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
@@ -55,6 +55,7 @@ GUTS=$(filter-out "$(GLOB_PATTERN)",$(wildcard $(GLOB_PATTERN)))
SOURCES=$(wildcard *.c)
OBJS=$(subst .c,.o,$(SOURCES))
+TESTS=$(patsubst %.c,%,$(wildcard test/*.c))
SHOBJS=pseudo_tables.o pseudo_util.o
DBOBJS=pseudo_db.o
@@ -74,8 +75,11 @@ TABLES=table_templates/pseudo_tables.c table_templates/pseudo_tables.h
all: $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) $(PSEUDO_PROFILE)
-test: all | $(BIN) $(LIB) $(LOCALSTATE)
- @./run_tests.sh -v
+test: all $(TESTS) | $(BIN) $(LIB)
+ ./run_tests.sh -v
+
+test/%: test/%.c
+ $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o $@ $<
install-lib: $(LIBPSEUDO)
mkdir -p $(DESTDIR)$(LIBDIR)
@@ -91,7 +95,7 @@ install-data:
install: all install-lib install-bin install-data
-$(BIN) $(LIB) $(LOCALSTATE):
+$(BIN) $(LIB):
mkdir -p $@
pseudo: $(PSEUDO)
@@ -165,7 +169,8 @@ pseudo_profile: Makefile pseudo_profile.c tables wrappers
$(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o pseudo_profile pseudo_profile.c
clean:
- rm -f *.o *.so $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) \
+ rm -f *.o $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) \
+ $(TESTS) \
pseudo_wrapfuncs.h pseudo_wrapfuncs.c \
pseudo_wrapper_table.c \
pseudo_tables.c pseudo_tables.h \
@@ -176,8 +181,8 @@ clean:
touch port_deps.mk func_deps.mk
distclean: clean
- rm -f Makefile
- rm -rf ./$(BIN) ./$(LIB) ./$(LOCALSTATE)
+ rm -f Makefile port_deps.mk func_deps.mk
+ rm -rf ./$(BIN) ./$(LIB) ./$(LOCALSTATE) ./__pycache__
@echo "WARNING: Makefile has been removed. You must reconfigure to do anything else."
nuke: distclean
diff --git a/README b/README
index 468cf66..c4cb997 100644
--- a/README
+++ b/README
@@ -1,16 +1,8 @@
-pseudo -- an analogue to sudo
+pseudo -- an analogue to sudo and an alternative to fakeroot
-IMPORTANT NOTE:
+The official home for pseudo's git repository is:
-As of this writing, the official home for pseudo's git repository has
-changed to:
- git://git.yoctoproject.org/pseudo
-
-The old site at:
- https://github.com/wrpseudo/pseudo
-
-is no longer the "real" master, although I'll probably try to keep it in
-sync.
+ https://git.yoctoproject.org/pseudo
OVERVIEW:
@@ -78,6 +70,7 @@ commentary.
ACKNOWLEDGEMENTS:
+Peter Seebach:
My various coworkers, both engineering and management, made this possible.
While I did most of the actual typing, this code has benefitted greatly
from detailed code reviews, excellent reproducers for bugs, and the
@@ -86,9 +79,19 @@ deal of fun, and I'm pretty happy that we're finally ready to make it
available for other people to look at.
+MAINTAINERS:
+
+Richard Purdie
+Mark Hatle
+
+
CONTACT:
-Discussions and patches should be directed at the openembedded-core mailing
-list at openembedded-core at lists.openembedded.org. More information at
-https://www.openembedded.org/wiki/Mailing_lists. Bugs should be filed with
-the Yocto project at https://bugzilla.yoctoproject.org/.
+Patches should be directed at the yocto-patches mailing list at yocto-patches
+at lists.yoctoproject.org with [pseudo] in the subject line. Bugs should be
+filed with the Yocto project at https://bugzilla.yoctoproject.org/.
+
+The repository can be configured to do this correctly with:
+
+ git config format.subjectprefix "pseudo] [PATCH"
+ git config sendemail.to yocto-patches@lists.yoctoproject.org
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..7ccecc1
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,13 @@
+How to Report a Potential Vulnerability?
+========================================
+
+If you would like to report a public issue (for example, one with a released
+CVE number), please report it using the
+[https://bugzilla.yoctoproject.org/enter_bug.cgi?product=Security Security Bugzilla].
+If you have a patch ready, submit it following the same procedure as any other
+patch as described in README.md.
+
+If you are dealing with a not-yet released or urgent issue, please send a
+message to security AT yoctoproject DOT org, including as many details as
+possible: the layer or software module affected, the recipe and its version,
+and any example code, if available.
diff --git a/enums/op.in b/enums/op.in
index 61ee666..5b5e21b 100644
--- a/enums/op.in
+++ b/enums/op.in
@@ -27,3 +27,4 @@ remove-xattr, 1
set-xattr, 0
create-xattr, 1
replace-xattr, 1
+closefrom, 0
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..cf5ad60 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):
@@ -356,11 +372,39 @@ class Function:
prefix = path[:-4]
if prefix not in self.specific_dirfds:
prefix = ''
+ if self.dirfd != "AT_FDCWD" and "flags" in self.flags \
+ and "AT_SYMLINK_NOFOLLOW" in self.flags:
+ fix_paths.append(
+ # Reference the variable inside a noop inline-asm to stop
+ # the compiler from eliminating the null pointer check
+ # on parameters marked nonnull
+ "asm(\"\" : \"+r\"(%s));"
+ "if (%s && !*%s && (flags & AT_EMPTY_PATH))\n"
+ "\t\t\tflags |= AT_SYMLINK_NOFOLLOW;" % (path, path, path))
fix_paths.append(
"%s = pseudo_root_path(__func__, __LINE__, %s%s, %s, %s);" %
(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 +611,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/close_range.c b/ports/linux/guts/close_range.c
new file mode 100644
index 0000000..4bd2fe1
--- /dev/null
+++ b/ports/linux/guts/close_range.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2021 Richard Purdie
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * int close_range(unsigned int lowfd, unsigned int maxfd, int flags)
+ * int rc = -1;
+ */
+
+ (void) lowfd;
+ (void) maxfd;
+ (void) flags;
+ /* for now pretend the kernel doesn't support it regardless
+ which users are supposed to be able to handle */
+ errno = ENOSYS;
+ rc = -1;
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/guts/closefrom.c b/ports/linux/guts/closefrom.c
new file mode 100644
index 0000000..1350506
--- /dev/null
+++ b/ports/linux/guts/closefrom.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2021 Richard Purdie
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * void closefrom(int fd)
+ */
+ pseudo_msg_t *msg;
+ /* this cleans up internal tables, and shouldn't make it to the server. Avoids pseudo's internal fds */
+ msg = pseudo_client_op(OP_CLOSEFROM, 0, fd, -1, 0, 0);
+ /* fds between fd and msg->fd are closed within the above function avoiding pseudo's own fds */
+ real_closefrom(msg->fd);
+
+/* return;
+ * }
+ */
diff --git a/ports/linux/guts/fcntl.c b/ports/linux/guts/fcntl.c
index 4dd9796..ffb50be 100644
--- a/ports/linux/guts/fcntl.c
+++ b/ports/linux/guts/fcntl.c
@@ -8,6 +8,22 @@
* wrap_fcntl(int fd, int cmd, ...struct flock *lock) {
* int rc = -1;
*/
+#if !defined(F_GETPIPE_SZ)
+#define F_GETPIPE_SZ (1032)
+#endif
+
+#if F_GETPIPE_SZ != 1032
+#error System F_GETPIPE_SZ has unexpected value
+#endif
+
+#if !defined(F_SETPIPE_SZ)
+#define F_SETPIPE_SZ (1031)
+#endif
+
+#if F_SETPIPE_SZ != 1031
+#error System F_SETPIPE_SZ has unexpected value
+#endif
+
long arg;
int save_errno;
@@ -31,12 +47,17 @@
}
errno = save_errno;
break;
+ case F_SETPIPE_SZ:
+ /* actually do something */
+ rc = real_fcntl(fd, cmd, arg);
+ break;
/* no argument: */
case F_GETFD:
case F_GETFL:
case F_GETOWN:
case F_GETSIG:
case F_GETLEASE:
+ case F_GETPIPE_SZ:
rc = real_fcntl(fd, cmd);
break;
/* long argument */
@@ -52,6 +73,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/fcntl64.c b/ports/linux/guts/fcntl64.c
new file mode 100644
index 0000000..99de43d
--- /dev/null
+++ b/ports/linux/guts/fcntl64.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2008-2010 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static int
+ * wrap_fcntl64(int fd, int cmd, ...struct flock *lock) {
+ * int rc = -1;
+ */
+#if !defined(F_GETPIPE_SZ)
+#define F_GETPIPE_SZ (1032)
+#endif
+
+#if F_GETPIPE_SZ != 1032
+#error System F_GETPIPE_SZ has unexpected value
+#endif
+
+#if !defined(F_SETPIPE_SZ)
+#define F_SETPIPE_SZ (1031)
+#endif
+
+#if F_SETPIPE_SZ != 1031
+#error System F_SETPIPE_SZ has unexpected value
+#endif
+
+ 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_fcntl64(fd, cmd, arg);
+ save_errno = errno;
+ if (rc != -1) {
+ pseudo_debug(PDBGF_OP, "fcntl64_dup: %d->%d\n", fd, rc);
+ pseudo_client_op(OP_DUP, 0, fd, rc, 0, 0);
+ }
+ errno = save_errno;
+ break;
+ case F_SETPIPE_SZ:
+ /* actually do something */
+ rc = real_fcntl64(fd, cmd, arg);
+ break;
+ /* no argument: */
+ case F_GETFD:
+ case F_GETFL:
+ case F_GETOWN:
+ case F_GETSIG:
+ case F_GETLEASE:
+ case F_GETPIPE_SZ:
+ rc = real_fcntl64(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_fcntl64(fd, cmd, arg);
+ break;
+ /* struct flock * argument */
+ 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_fcntl64(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_fcntl64(fd, cmd, (struct flock64 *) lock);
+ break;
+#endif
+ default:
+ pseudo_diag("unknown fcntl64 argument %d, assuming long argument.\n",
+ cmd);
+ rc = real_fcntl64(fd, cmd, arg);
+ break;
+ }
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/guts/fstatat.c b/ports/linux/guts/fstatat.c
new file mode 100644
index 0000000..3267641
--- /dev/null
+++ b/ports/linux/guts/fstatat.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2021 Linux Foundation; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * int fstatat(int dirfd, const char *path, struct stat *buf, int flags)
+ * int rc = -1;
+ */
+
+ rc = wrap___fxstatat(_STAT_VER, dirfd, path, buf, flags);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/guts/fstatat64.c b/ports/linux/guts/fstatat64.c
new file mode 100644
index 0000000..c981e14
--- /dev/null
+++ b/ports/linux/guts/fstatat64.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2021 Linux Foundation; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * int fstatat64(int dirfd, const char *path, struct stat64 *buf, int flags)
+ * int rc = -1;
+ */
+
+ rc = wrap___fxstatat64(_STAT_VER, dirfd, path, buf, flags);
+
+/* return rc;
+ * }
+ */
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/guts/openat.c b/ports/linux/guts/openat.c
index 673ea6e..656ac2b 100644
--- a/ports/linux/guts/openat.c
+++ b/ports/linux/guts/openat.c
@@ -55,9 +55,13 @@
if (flags & O_CREAT) {
save_errno = errno;
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
- rc = real___xstat64(_STAT_VER, path, &buf);
+ if (flags & O_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, 0);
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
#endif
existed = (rc != -1);
if (!existed)
@@ -72,9 +76,13 @@
if (!(flags & O_NONBLOCK) && ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_RDWR)) {
save_errno = errno;
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
- rc = real___xstat64(_STAT_VER, path, &buf);
+ if (flags & O_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, 0);
+ rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
#endif
if (rc != -1 && S_ISFIFO(buf.st_mode)) {
overly_magic_nonblocking = 1;
@@ -126,11 +134,17 @@
}
#endif
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
- stat_rc = real___xstat64(_STAT_VER, path, &buf);
+ if (flags & O_NOFOLLOW) {
+ stat_rc = real___lxstat64(_STAT_VER, path, &buf);
+ } else {
+ stat_rc = real___xstat64(_STAT_VER, path, &buf);
+ }
#else
- stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, 0);
+ stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, (flags & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
#endif
+ pseudo_debug(PDBGF_FILE, "openat(path %s), flags %o, stat rc %d, stat mode %o\n",
+ path, flags, stat_rc, buf.st_mode);
if (stat_rc != -1) {
buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode);
if (!existed) {
diff --git a/ports/linux/nostatx/portdefs.h b/ports/linux/nostatx/portdefs.h
new file mode 100644
index 0000000..ded8ff9
--- /dev/null
+++ b/ports/linux/nostatx/portdefs.h
@@ -0,0 +1,38 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copy of the statx struct to allow a pseudo built on a system without
+ * statx to work on one with statx and hence work with OE's uninative
+ */
+
+struct statx_timestamp
+{
+ __int64_t tv_sec;
+ __uint32_t tv_nsec;
+ __int32_t __statx_timestamp_pad1[1];
+};
+
+struct statx
+{
+ __uint32_t stx_mask;
+ __uint32_t stx_blksize;
+ __uint64_t stx_attributes;
+ __uint32_t stx_nlink;
+ __uint32_t stx_uid;
+ __uint32_t stx_gid;
+ __uint16_t stx_mode;
+ __uint16_t __statx_pad1[1];
+ __uint64_t stx_ino;
+ __uint64_t stx_size;
+ __uint64_t stx_blocks;
+ __uint64_t stx_attributes_mask;
+ struct statx_timestamp stx_atime;
+ struct statx_timestamp stx_btime;
+ struct statx_timestamp stx_ctime;
+ struct statx_timestamp stx_mtime;
+ __uint32_t stx_rdev_major;
+ __uint32_t stx_rdev_minor;
+ __uint32_t stx_dev_major;
+ __uint32_t stx_dev_minor;
+ __uint64_t __statx_pad2[14];
+};
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..7659897 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,11 +78,25 @@ 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?
*/
- void *res = __builtin_apply((void (*)()) real_syscall, __builtin_apply_args(), sizeof(long) * 7);
+ void *res = __builtin_apply((void (*)(void)) real_syscall, __builtin_apply_args(), sizeof(long) * 7);
__builtin_return(res);
}
@@ -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 (*)(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/statvfs/guts/statvfs64.c b/ports/linux/statvfs/guts/statvfs64.c
new file mode 100644
index 0000000..856d3db
--- /dev/null
+++ b/ports/linux/statvfs/guts/statvfs64.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * int statvfs64(const char *path, struct statvfs64 *buf)
+ * int rc = -1;
+ */
+
+ rc = real_statvfs64(path, buf);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/linux/statvfs/wrapfuncs.in b/ports/linux/statvfs/wrapfuncs.in
index 1afb64d..6a59660 100644
--- a/ports/linux/statvfs/wrapfuncs.in
+++ b/ports/linux/statvfs/wrapfuncs.in
@@ -1 +1,2 @@
int statvfs(const char *path, struct statvfs *buf);
+int statvfs64(const char *path, struct statvfs64 *buf);
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..099ea59 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,18 @@ else
fi
rm -f dummy.c dummy.o
+# For statx, we want a pseudo which can work with OE's uninative, i.e. build on a system without
+# statx but work on one with it. We have a header in nostatx to allow this.
+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"
+else
+ echo "linux/nostatx"
+ echo "linux/statx"
+fi
+rm -f dummy.c dummy.o
+
diff --git a/ports/linux/wrapfuncs.in b/ports/linux/wrapfuncs.in
index 4cdbc9c..97b16c2 100644
--- a/ports/linux/wrapfuncs.in
+++ b/ports/linux/wrapfuncs.in
@@ -1,41 +1,46 @@
-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 */
+int fcntl64(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 */
int fstat(int fd, struct stat *buf); /* real_func=pseudo_fstat */
+int fstatat(int dirfd, const char *path, struct stat *buf, int flags);
int stat64(const char *path, struct stat64 *buf); /* real_func=pseudo_stat64 */
int lstat64(const char *path, struct stat64 *buf); /* real_func=pseudo_lstat64, flags=AT_SYMLINK_NOFOLLOW */
int fstat64(int fd, struct stat64 *buf); /* real_func=pseudo_fstat64 */
+int fstatat64(int dirfd, const char *path, struct stat64 *buf, int flags);
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 +61,6 @@ 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 */
+int close_range(unsigned int lowfd, unsigned int maxfd, int flags);
+void closefrom(int fd);
diff --git a/ports/linux/xattr/portdefs.h b/ports/linux/xattr/portdefs.h
index 56cd3ca..beab7d0 100644
--- a/ports/linux/xattr/portdefs.h
+++ b/ports/linux/xattr/portdefs.h
@@ -2,5 +2,9 @@
* SPDX-License-Identifier: LGPL-2.1-only
*
*/
-#include <attr/xattr.h>
+#include <sys/xattr.h>
#include <stdint.h>
+
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
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/access.c b/ports/unix/guts/access.c
index 1cc8d58..4725f49 100644
--- a/ports/unix/guts/access.c
+++ b/ports/unix/guts/access.c
@@ -21,7 +21,7 @@
if (buf.st_mode & 0111) {
return 0;
} else {
- errno = EPERM;
+ errno = EACCES;
return -1;
}
} else {
diff --git a/ports/unix/guts/faccessat.c b/ports/unix/guts/faccessat.c
new file mode 100644
index 0000000..02515ee
--- /dev/null
+++ b/ports/unix/guts/faccessat.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2021, Linux Foundation; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static int
+ * wrap_fsaccessat(int dirfd, const char *path, int mode, int flags) {
+ * int rc = -1;
+ */
+ rc = wrap_faccessat2(dirfd, path, mode, flags);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/unix/guts/faccessat2.c b/ports/unix/guts/faccessat2.c
new file mode 100644
index 0000000..1283cc6
--- /dev/null
+++ b/ports/unix/guts/faccessat2.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021, Linux Foundation; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static int
+ * wrap_fsaccessat2(int dirfd, const char *path, int mode, int flags) {
+ * int rc = -1;
+ */
+ PSEUDO_STATBUF buf;
+
+#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
+ if (dirfd != AT_FDCWD) {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (flags & AT_SYMLINK_NOFOLLOW) {
+ rc = base_lstat(path, &buf);
+ } else {
+ rc = base_stat(path, &buf);
+ }
+#else
+ rc = base_fstatat(dirfd, path, &buf, flags & AT_SYMLINK_NOFOLLOW);
+#endif
+ if (rc == -1)
+ return rc;
+
+ /* note: no attempt to handle the case where user isn't
+ * root.
+ */
+
+ if (mode & X_OK) {
+ if (buf.st_mode & 0111) {
+ return 0;
+ } else {
+ errno = EACCES;
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/ports/unix/guts/fchmodat.c b/ports/unix/guts/fchmodat.c
index 55dbd35..5a31151 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
@@ -28,9 +28,8 @@
return rc;
}
if (S_ISLNK(buf.st_mode)) {
- /* we don't really support chmod of a symlink */
- errno = ENOSYS;
- return -1;
+ /* according to docs, "chmod on a symbolic link always succeeds and has no effect" */
+ return 0;
}
save_errno = errno;
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..80bbf41 100644
--- a/ports/unix/guts/rename.c
+++ b/ports/unix/guts/rename.c
@@ -13,7 +13,8 @@
int oldrc, newrc;
int save_errno;
int old_db_entry = 0;
- int may_unlinked = 0;
+ int may_unlink_new = 0;
+ int may_unlink_old = 0;
pseudo_debug(PDBGF_OP, "rename: %s->%s\n",
oldpath ? oldpath : "<nil>",
@@ -29,41 +30,51 @@
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. */
/* as with unlink, we have to mark that the file may get deleted */
msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, -1, newpath, newrc ? NULL : &newbuf);
if (msg && msg->result == RESULT_SUCCEED)
- may_unlinked = 1;
+ may_unlink_new = 1;
+ /* oldpath is also likely to disappear. Something could call stat() after
+ real_rename so we need to mark as MAY_UNLINK too */
+ msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, -1, oldpath, oldrc ? NULL : &oldbuf);
+ if (msg && msg->result == RESULT_SUCCEED)
+ may_unlink_old = 1;
+
msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, oldrc ? NULL : &oldbuf);
if (msg && msg->result == RESULT_SUCCEED)
old_db_entry = 1;
rc = real_rename(oldpath, newpath);
save_errno = errno;
- if (may_unlinked) {
- if (rc == -1) {
- /* since we failed, that wasn't really unlinked -- put
- * it back.
- */
- pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, newpath, &newbuf);
- } else {
- /* confirm that the file was removed */
- pseudo_client_op(OP_DID_UNLINK, 0, -1, -1, newpath, &newbuf);
- }
- }
+
if (rc == -1) {
+ /* since we failed, that wasn't really unlinked -- put
+ * it back.
+ */
+ if (may_unlink_new)
+ pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, newpath, &newbuf);
+ if (may_unlink_old)
+ pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, oldpath, &oldbuf);
/* and we're done. */
errno = save_errno;
return rc;
}
+
+ /* confirm that the file was removed */
+ if (may_unlink_new)
+ pseudo_client_op(OP_DID_UNLINK, 0, -1, -1, newpath, &newbuf);
+ /* OP_DID_UNLINK for oldpath is handled by the server */
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..5ac63f9 100644
--- a/ports/unix/guts/renameat.c
+++ b/ports/unix/guts/renameat.c
@@ -13,7 +13,8 @@
int oldrc, newrc;
int save_errno;
int old_db_entry = 0;
- int may_unlinked = 0;
+ int may_unlink_new = 0;
+ int may_unlink_old = 0;
pseudo_debug(PDBGF_FILE, "renameat: %d,%s->%d,%s\n",
olddirfd, oldpath ? oldpath : "<nil>",
@@ -41,42 +42,51 @@
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. */
/* as with unlink, we have to mark that the file may get deleted */
msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, newdirfd, newpath, newrc ? NULL : &newbuf);
if (msg && msg->result == RESULT_SUCCEED)
- may_unlinked = 1;
+ may_unlink_new = 1;
+ /* oldpath is also likely to disappear. Something could call stat() after
+ real_rename so we need to mark as MAY_UNLINK too */
+ msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, olddirfd, oldpath, oldrc ? NULL : &oldbuf);
+ if (msg && msg->result == RESULT_SUCCEED)
+ may_unlink_old = 1;
+
msg = pseudo_client_op(OP_STAT, 0, -1, olddirfd, oldpath, oldrc ? NULL : &oldbuf);
if (msg && msg->result == RESULT_SUCCEED)
old_db_entry = 1;
rc = real_renameat(olddirfd, oldpath, newdirfd, newpath);
save_errno = errno;
- if (may_unlinked) {
- if (rc == -1) {
- /* since we failed, that wasn't really unlinked -- put
- * it back.
- */
- pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
- } else {
- /* confirm that the file was removed */
- pseudo_client_op(OP_DID_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
- }
- }
if (rc == -1) {
+ /* since we failed, that wasn't really unlinked -- put
+ * it back.
+ */
+ if (may_unlink_new)
+ pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
+ if (may_unlink_old)
+ pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, olddirfd, oldpath, &oldbuf);
/* and we're done. */
errno = save_errno;
return rc;
}
+
+ /* confirm that the file was removed */
+ if (may_unlink_new)
+ pseudo_client_op(OP_DID_UNLINK, 0, -1, newdirfd, newpath, &newbuf);
+ /* OP_DID_UNLINK for oldpath is handled by the server */
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/subports b/ports/unix/subports
index e41b036..bd5a2f6 100755
--- a/ports/unix/subports
+++ b/ports/unix/subports
@@ -1,11 +1,13 @@
#!/bin/sh
cat > dummy.c <<EOF
+#define _GNU_SOURCE
#include <unistd.h>
int main(void) {
syncfs(0);
return 0;
}
EOF
+
if ${CC} -o dummy dummy.c > /dev/null 2>&1; then
echo "unix/syncfs"
fi
diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in
index 3910fae..7724fc7 100644
--- a/ports/unix/wrapfuncs.in
+++ b/ports/unix/wrapfuncs.in
@@ -1,15 +1,17 @@
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);
+int faccessat(int dirfd, const char *path, int mode, int flags);
+int faccessat2(int dirfd, const char *path, int mode, int flags);
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);
int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag);
@@ -20,18 +22,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.1 b/pseudo.1
index 63a6782..40429aa 100644
--- a/pseudo.1
+++ b/pseudo.1
@@ -295,7 +295,7 @@ options increase the debugging level.
.TP 8
.BI \-x\ (debug)
-Set specific deugging flags (the
+Set specific debugging flags (the
.I pseudo
utility's help message lists them). This is equivalent to the string
form of the
diff --git a/pseudo.c b/pseudo.c
index 0f5850e..e98e5d6 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) {
@@ -680,31 +682,30 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **respon
}
break;
default:
- if (strcmp(msg->path, path_by_ino)) {
+ /* Ignore NAMELESS FILE entries since those could be created by other threads on new files */
+ if (strcmp(msg->path, path_by_ino) && strcmp(path_by_ino, "NAMELESS FILE")) {
mismatch = 1;
}
break;
}
if (mismatch) {
- /* a mismatch, but we were planning to delete
- * the file, so it must have gotten deleted
- * already.
- */
if (by_ino.deleting != 0) {
- pseudo_debug(PDBGF_FILE, "inode mismatch for '%s' -- old one was marked for deletion, deleting.\n",
+ /* a mismatch, likely a race but we were planning to
+ * delete the file, or are in the middle of a rename
+ * so continue, need the old entry for a rename.
+ */
+ pseudo_debug(PDBGF_FILE, "inode mismatch for '%s' -- old one was marked for deletion.\n",
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..a03d6b1 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;
}
@@ -833,7 +846,7 @@ pseudo_root_path(const char *func, int line, int dirfd, const char *path, int le
pseudo_magic();
if (!rc) {
pseudo_diag("couldn't allocate absolute path for '%s'.\n",
- path);
+ path ? path : "null");
}
pseudo_debug(PDBGF_CHROOT, "root_path [%s, %d]: '%s' from '%s'\n",
func, line,
@@ -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,28 @@ 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;
+ }
+}
+
+static void
+pseudo_client_closefrom(int fd) {
+ int i;
+ if (fd < 0 || fd >= nfds)
+ return;
+
+ for (i = fd; i < nfds; ++i) {
+ free(fd_paths[i]);
+ fd_paths[i] = 0;
+
+ if (i < linked_nfds) {
+ free(linked_fd_paths[i]);
+ linked_fd_paths[i] = 0;
+ }
+ }
}
/* spawn server */
@@ -1271,7 +1347,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 +1512,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,10 +1562,52 @@ 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;
- pseudo_msg_t msg = { .type = PSEUDO_MSG_OP };
+ static pseudo_msg_t msg;
+ msg = (pseudo_msg_t) { .type = PSEUDO_MSG_OP };
size_t pathlen = -1;
int do_request = 0;
char *path_extra_1 = 0;
@@ -1495,6 +1617,7 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
static char *alloced_path = 0;
static size_t alloced_len = 0;
int strip_slash;
+ int startfd, i;
#ifdef PSEUDO_PROFILING
struct timeval tv1_op, tv2_op;
@@ -1522,6 +1645,25 @@ 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_CLOSEFROM && op != OP_DUP
+ && pseudo_client_ignore_path_chroot(path, 0)) {
+ if (op == OP_OPEN) {
+ /* Sanitise the path to have no trailing slash as this is convention in the database */
+ char *stripped_path;
+ pathlen = strlen(path);
+ if (pathlen > 2 && (path[pathlen - 1]) == '/') {
+ stripped_path = strndup(path, pathlen-1);
+ pseudo_client_path(fd, stripped_path);
+ free(stripped_path);
+ } else {
+ pseudo_client_path(fd, path);
+ }
+ }
+ /* reenable wrappers */
+ pseudo_magic();
+ return result;
+ }
+
#ifdef PSEUDO_XATTRDB
if (buf) {
struct stat64 bufcopy = *buf;
@@ -1772,9 +1914,36 @@ 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_CLOSEFROM:
+ /* no request needed */
+ startfd = fd;
+ if (pseudo_util_debug_fd > startfd)
+ startfd = pseudo_util_debug_fd + 1;
+ if (pseudo_localstate_dir_fd > startfd)
+ startfd = pseudo_localstate_dir_fd + 1;
+ if (pseudo_pwd_fd > startfd)
+ startfd = pseudo_pwd_fd + 1;
+ if (pseudo_grp_fd > startfd)
+ startfd = pseudo_grp_fd + 1;
+ if (connect_fd > startfd)
+ startfd = connect_fd + 1;
+ for (i = fd; i < startfd; ++i) {
+ if (i == pseudo_util_debug_fd || i == pseudo_localstate_dir_fd || i == pseudo_pwd_fd ||
+ i == pseudo_grp_fd || i == connect_fd)
+ continue;
+ pseudo_client_close(i);
+ close(i);
+ }
+ pseudo_client_closefrom(startfd);
+ /* tell the caller to close from startfd instead of fd */
+ result = &msg;
+ msg.fd = startfd;
+ do_request = 0;
+ break;
case OP_CLOSE:
/* no request needed */
if (fd >= 0) {
@@ -1813,6 +1982,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 +2008,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,12 +2017,16 @@ 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;
}
/* result can only be set when PSEUDO_XATTRDB resulted in a
- * successful store to or read from the local database.
+ * successful store to or read from the local database or for OP_CLOSEFROM.
*/
if (do_request && !result) {
#ifdef PSEUDO_PROFILING
@@ -1876,6 +2052,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..14bafcb 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,
@@ -386,7 +399,7 @@ signed_ino(ino_t ino) {
#ifdef USE_MEMORY_DB
-static void
+void
pdb_backup() {
sqlite3_backup *pBackup;
/* no point in doing this if we don't have a database to back up,
@@ -2007,8 +2020,8 @@ int
pdb_rename_file(const char *oldpath, pseudo_msg_t *msg) {
static sqlite3_stmt *update_exact, *update_sub;
int rc;
- char *sql_update_exact = "UPDATE files SET path = ? WHERE path = ?;";
- char *sql_update_sub = "UPDATE files SET path = replace(path, ?, ?) "
+ char *sql_update_exact = "UPDATE files SET path = ?, deleting = 0 WHERE path = ?;";
+ char *sql_update_sub = "UPDATE files SET path = replace(path, ?, ?), deleting = 0 "
"WHERE (path > (? || '/') AND path < (? || '0'));";
if (!file_db && get_dbs()) {
diff --git a/pseudo_db.h b/pseudo_db.h
index 5a4aa59..9d01232 100644
--- a/pseudo_db.h
+++ b/pseudo_db.h
@@ -26,6 +26,7 @@ typedef struct {
char *program;
} log_entry;
+extern void pdb_backup(void);
extern int pdb_maybe_backup(void);
extern int pdb_cancel_unlink_file(pseudo_msg_t *msg);
extern int pdb_did_unlink_file(char *path, pseudo_msg_t *msg, int deleting);
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..815c76b 100644
--- a/pseudo_server.c
+++ b/pseudo_server.c
@@ -568,6 +568,7 @@ serve_client(int i) {
}
in->pathlen = (s - response_path) + 1;
/* exit quickly once clients go away, though */
+ pdb_backup();
pseudo_server_timeout = 3;
} else {
in->type = PSEUDO_MSG_ACK;
@@ -792,6 +793,7 @@ pseudo_server_loop(void) {
struct sigaction eat_usr2 = {
.sa_handler = set_do_list_clients
};
+ int hitmaxfiles;
clients = malloc(16 * sizeof(*clients));
@@ -810,6 +812,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 +871,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 +901,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..b58036f 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) {
@@ -678,7 +679,7 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre
if (!leave_this && is_dir) {
int is_link = S_ISLNK(buf->st_mode);
if (link_recursion >= PSEUDO_MAX_LINK_RECURSION && is_link) {
- pseudo_diag("link recursion too deep, not expanding path '%s'.\n", newpath);
+ pseudo_debug(PDBGF_PATH, "link recursion too deep, not expanding path '%s'.\n", newpath);
is_link = 0;
}
if (is_link) {
@@ -688,14 +689,15 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre
linklen = readlink(newpath, linkbuf, pseudo_path_max());
if (linklen == -1) {
- pseudo_diag("uh-oh! '%s' seems to be a symlink, but I can't read it. Ignoring.", newpath);
+ pseudo_debug(PDBGF_PATH, "uh-oh! '%s' seems to be a symlink, but I can't read it. Ignoring.\n", newpath);
+ *pcurrent = current;
return 0;
}
/* null-terminate buffer */
linkbuf[linklen] = '\0';
- /* absolute symlink means start over! */
+ /* absolute symlink means go back to root */
if (*linkbuf == '/') {
- current = newpath;
+ current = root;
} else {
/* point back at the end of the previous path... */
current -= (elen + 1);
@@ -790,11 +792,9 @@ static char *pathbufs[PATHBUFS] = { 0 };
static int pathbuf = 0;
/* Canonicalize path. "base", if present, is an already-canonicalized
- * path of baselen characters, presumed not to end in a /. path is
- * the new path to be canonicalized. The tricky part is that path may
- * contain symlinks, which must be resolved.
- * if "path" starts with a /, then it is an absolute path, and
- * we ignore base.
+ * path of baselen characters, presumed not to end in a / unless it is
+ * just "/". path is the new path to be canonicalized. The tricky part
+ * is that path may contain symlinks, which must be resolved.
*/
char *
pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t baselen, size_t *lenp, int leave_last) {
@@ -808,13 +808,28 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel
pseudo_diag("can't fix empty path.\n");
return 0;
}
+ if (baselen == 1) {
+ baselen = 0;
+ }
+ if (rootlen == 1) {
+ rootlen = 0;
+ }
newpathlen = pseudo_path_max();
+ pathlen = strlen(path);
+ /* Crazy shell code (e.g. libtool) can pass in a command pipeline as a path which exceeds the max path
+ * length the system can support (6000+ chars). This will fail in libc or the syscall but if we don't
+ * do something here, we'd segfault before it can do that. Leave path unchanged and let libc deal
+ * with it.
+ */
+ if ((pathlen + baselen) >= newpathlen) {
+ return path;
+ }
if (!pathbufs[pathbuf]) {
pathbufs[pathbuf] = malloc(newpathlen);
}
newpath = pathbufs[pathbuf];
pathbuf = (pathbuf + 1) % PATHBUFS;
- pathlen = strlen(path);
+
/* a trailing slash has special meaning, but processing
* trailing slashes is expensive.
*/
@@ -966,6 +981,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 +990,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 */
@@ -1594,7 +1611,7 @@ pseudo_logfile(char *filename, char *defname, int prefer_fd) {
}
free(filename);
}
- fd = open(pseudo_path, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ fd = open(pseudo_path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
if (fd == -1) {
pseudo_diag("help: can't open log file %s: %s\n", pseudo_path, strerror(errno));
} else {
diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c
index 99aabff..9ae1200 100644
--- a/pseudo_wrappers.c
+++ b/pseudo_wrappers.c
@@ -140,7 +140,7 @@ pseudo_init_one_wrapper(pseudo_function *func) {
#endif
f = dlsym(RTLD_NEXT, func->name);
if (f) {
- *func->real = f;
+ *func->real = (void (*)(void)) f;
}
/* it turns out that in some cases, we get apparently-harmless
* errors if a function is missing, and that printing output
diff --git a/pseudolog.c b/pseudolog.c
index 1101f28..ad04753 100644
--- a/pseudolog.c
+++ b/pseudolog.c
@@ -8,7 +8,8 @@
*/
/* We need _XOPEN_SOURCE for strptime(), but if we define that,
* we then don't get S_IFSOCK... _GNU_SOURCE turns on everything. */
-#define _GNU_SOURCE
+#define _DEFAULT_SOURCE
+#define _XOPEN_SOURCE
#include <ctype.h>
#include <limits.h>
@@ -374,7 +375,7 @@ plog_trait(int opt, char *string) {
pseudo_diag("invalid empty string for -%c\n", opt);
return 0;
}
- new_trait = calloc(sizeof(*new_trait), 1);
+ new_trait = calloc(1, sizeof(*new_trait));
if (!new_trait) {
pseudo_diag("Couldn't allocate requested trait (for -%c %s)\n",
opt, string ? string : "<nil>");
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/templates/wrapper_table b/templates/wrapper_table
index bb30530..498ca81 100644
--- a/templates/wrapper_table
+++ b/templates/wrapper_table
@@ -6,8 +6,8 @@
* script if you want to modify this. */
typedef struct {
char *name; /* the name */
- int (**real)(void); /* the underlying syscall */
- int (*wrapper)(void); /* the wrapper from guts/name.c */
+ void (**real)(void); /* the underlying syscall */
+ void (*wrapper)(void); /* the wrapper from guts/name.c */
char *version; /* the version, if we know and care */
} pseudo_function;
@@ -15,8 +15,8 @@ static pseudo_function pseudo_functions[] = {
@body
{ /* ${comment}; */
"${name}${maybe_inode64}",
- (int (**)(void)) &real_${name},
- (int (*)(void)) wrap_${name},
+ (void (**)(void)) &real_${name},
+ (void (*)(void)) wrap_${name},
${version}
},
@footer
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-chroot-symlink.c b/test/test-chroot-symlink.c
new file mode 100644
index 0000000..469cb49
--- /dev/null
+++ b/test/test-chroot-symlink.c
@@ -0,0 +1,28 @@
+/*
+ * Test that stat'ing absolute/relative symlinks in a chroot environment works
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ struct stat buf;
+
+ if (argc != 2) {
+ perror("args");
+ return 2;
+ }
+ if (chroot(argv[1]) == -1) {
+ perror("chroot");
+ return 1;
+ }
+ if (stat("symlink", &buf) == -1) {
+ perror("stat symlink");
+ return 1;
+ }
+ return 0;
+}
diff --git a/test/test-chroot-symlink.sh b/test/test-chroot-symlink.sh
new file mode 100755
index 0000000..91092c1
--- /dev/null
+++ b/test/test-chroot-symlink.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Test that stat'ing absolute/relative symlinks in a chroot environment works
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+set -e
+
+touch symlink_target
+trap "rm -f symlink_target symlink" 0
+
+# Absolute symlink
+ln -s /symlink_target symlink
+./test/test-chroot-symlink `pwd`
+rm symlink
+
+# Relative symlink
+ln -s symlink_target symlink
+./test/test-chroot-symlink `pwd`
diff --git a/test/test-fcntl.c b/test/test-fcntl.c
new file mode 100644
index 0000000..b593d50
--- /dev/null
+++ b/test/test-fcntl.c
@@ -0,0 +1,58 @@
+/* fcntl-linux.h doesn't define F_GETPIPE_SZ and F_SETPIPE_SZ without
+ * this */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+
+int test_pipe_sz()
+{
+#if defined(F_GETPIPE_SZ) && defined(F_SETPIPE_SZ)
+ int pipefd[2];
+
+ if (pipe(pipefd) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ const int orig_size = fcntl(pipefd[0], F_GETPIPE_SZ);
+ if (orig_size < 0) {
+ perror("F_GETPIPE_SZ");
+ return 1;
+ }
+
+ const int new_size = orig_size * 2;
+
+ if (fcntl(pipefd[0], F_SETPIPE_SZ, new_size) < 0) {
+ perror("F_SETPIPE_SZ");
+ return 1;
+ }
+
+ const int final_size = fcntl(pipefd[0], F_GETPIPE_SZ);
+ if (final_size < 0) {
+ perror("Second F_GETPIPE_SZ");
+ return 1;
+ }
+
+ if (final_size < new_size) {
+ fprintf(stderr, "Unexpected final pipe size: %d\n", final_size);
+ return 1;
+ }
+#else
+ printf("Host too old for F_GETPIPE_SZ and F_SETPIPE_SZ tests\n");
+#endif
+ return 0;
+}
+
+int main()
+{
+ int result = 0;
+ result += test_pipe_sz();
+ return result;
+}
diff --git a/test/test-fcntl.sh b/test/test-fcntl.sh
new file mode 100755
index 0000000..7112620
--- /dev/null
+++ b/test/test-fcntl.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+./test/test-fcntl
diff --git a/test/test-fstat.c b/test/test-fstat.c
new file mode 100644
index 0000000..78f7013
--- /dev/null
+++ b/test/test-fstat.c
@@ -0,0 +1,29 @@
+/*
+ * Test that stat'ing a file descriptor of a symlink does not dereference the symlink
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+#define _GNU_SOURCE
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+int main(void) {
+ struct stat buf;
+ int fd = open("symlink", O_NOFOLLOW | O_PATH);
+ if (fd == -1) {
+ perror("open symlink");
+ return 1;
+ }
+ if (fstatat(fd, "", &buf, AT_EMPTY_PATH) == -1) {
+ perror("stat symlink");
+ return 1;
+ }
+ if (S_ISLNK(buf.st_mode) != 1) {
+ fprintf(stderr, "path is not a symlink\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/test/test-fstat.sh b/test/test-fstat.sh
new file mode 100755
index 0000000..88eab8b
--- /dev/null
+++ b/test/test-fstat.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+ln -s . symlink
+trap "rm symlink" 0
+./test/test-fstat
diff --git a/test/test-openat.c b/test/test-openat.c
new file mode 100644
index 0000000..df6655a
--- /dev/null
+++ b/test/test-openat.c
@@ -0,0 +1,58 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+
+char *path_of(int fd) {
+ ssize_t len;
+ char proc[PATH_MAX], real[PATH_MAX];
+ snprintf(proc, sizeof(proc), "/proc/self/fd/%d", fd);
+ len = readlink(proc, real, sizeof(real));
+ real[len] = '\0';
+ return strdup(real);
+}
+
+/*
+* Test that recusing up the tree with openat(fd, "../") handles slashes
+* correctly and doesn't end up opening the same directory instead of going up a
+* level.
+*/
+int main () {
+ int fd, dir_fd;
+ struct stat st;
+ ino_t ino;
+ dev_t dev;
+ char *path;
+
+ fd = openat(AT_FDCWD, ".", O_DIRECTORY, 0);
+ fstat(fd, &st);
+ ino = st.st_ino;
+ dev = st.st_dev;
+
+ while (1) {
+ path = path_of(fd);
+ //puts(path);
+
+ dir_fd = openat(fd, "../", O_DIRECTORY, 0);
+ fstat(dir_fd, &st);
+ if (st.st_ino == ino && st.st_dev == dev) {
+ if (strcmp(path, "/") == 0) {
+ //puts("Reached top of tree");
+ return 0;
+ } else {
+ //puts("Recursion failed!");
+ return 1;
+ }
+ }
+
+ free (path);
+ ino = st.st_ino;
+ dev = st.st_dev;
+ fd = dir_fd;
+ }
+ return 0;
+}
diff --git a/test/test-openat.sh b/test/test-openat.sh
new file mode 100755
index 0000000..0455586
--- /dev/null
+++ b/test/test-openat.sh
@@ -0,0 +1,7 @@
+#! /bin/sh
+
+# Test with and without paths being ignored. The bug was with paths being ignored.
+
+./test/test-openat
+
+PSEUDO_IGNORE_PATHS=/ ./test/test-openat
diff --git a/test/test-relative-from-root.sh b/test/test-relative-from-root.sh
new file mode 100755
index 0000000..e2c230e
--- /dev/null
+++ b/test/test-relative-from-root.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# pseudo had a bug that made it abort() when looking up a relative path from
+# base "/", such as by openat(dirfd_of_root, "foo/bar") or when cwd is /. It
+# tried to look up base+"/"+path = "//foo/bar", which is wrong.
+
+set -e
+
+touch f1
+relative_pwd=${PWD#/}
+
+cd /
+cat "$relative_pwd/f1"
+
+rm "$relative_pwd/f1"
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
diff --git a/test/test-statx.c b/test/test-statx.c
new file mode 100644
index 0000000..06d86af
--- /dev/null
+++ b/test/test-statx.c
@@ -0,0 +1,20 @@
+/*
+ * Test that passing NULL to a parameter marked as nonnull works correctly
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+#define _GNU_SOURCE
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// Passing a null pointer is the test scenario
+#pragma GCC diagnostic ignored "-Wnonnull"
+
+int main(void) {
+ if (statx(0, NULL, 0, 0, NULL) != -1) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/test/test-statx.sh b/test/test-statx.sh
new file mode 100755
index 0000000..77d0302
--- /dev/null
+++ b/test/test-statx.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+exec ./test/test-statx
diff --git a/test/test-umask.sh b/test/test-umask.sh
index e4e366b..e09fdbf 100755
--- a/test/test-umask.sh
+++ b/test/test-umask.sh
@@ -35,3 +35,4 @@ case $(mode b) in
*) exit 1;;
esac
+rm a b