aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile.in18
-rw-r--r--enums/op.in1
-rwxr-xr-xmakewrappers6
-rw-r--r--ports/linux/guts/close_range.c20
-rw-r--r--ports/linux/guts/closefrom.c16
-rw-r--r--ports/linux/guts/fcntl.c21
-rw-r--r--ports/linux/guts/fcntl64.c99
-rw-r--r--ports/linux/nostatx/portdefs.h38
-rw-r--r--ports/linux/pseudo_wrappers.c4
-rwxr-xr-xports/linux/subports5
-rw-r--r--ports/linux/wrapfuncs.in3
-rw-r--r--ports/linux/xattr/portdefs.h5
-rw-r--r--pseudo.c3
-rw-r--r--pseudo_client.c64
-rw-r--r--pseudo_db.c2
-rw-r--r--pseudo_db.h1
-rw-r--r--pseudo_server.c1
-rw-r--r--pseudo_util.c34
-rw-r--r--pseudo_wrappers.c2
-rw-r--r--templates/wrapper_table8
-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-statx.c20
-rwxr-xr-xtest/test-statx.sh6
-rwxr-xr-xtest/test-umask.sh1
33 files changed, 574 insertions, 33 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/Makefile.in b/Makefile.in
index 6eff522..48fdbd2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -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,9 +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)
- $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o test/test-rename-fstat test/test-rename-fstat.c
- @./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)
@@ -92,7 +95,7 @@ install-data:
install: all install-lib install-bin install-data
-$(BIN) $(LIB) $(LOCALSTATE):
+$(BIN) $(LIB):
mkdir -p $@
pseudo: $(PSEUDO)
@@ -166,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 \
@@ -177,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/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/makewrappers b/makewrappers
index 818834e..cf5ad60 100755
--- a/makewrappers
+++ b/makewrappers
@@ -375,8 +375,12 @@ class Function:
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))
+ "\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))
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 434c7f3..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 */
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/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/pseudo_wrappers.c b/ports/linux/pseudo_wrappers.c
index ed34115..7659897 100644
--- a/ports/linux/pseudo_wrappers.c
+++ b/ports/linux/pseudo_wrappers.c
@@ -96,7 +96,7 @@ syscall(long number, ...) {
* 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);
}
@@ -137,7 +137,7 @@ prctl(int option, ...) {
* 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);
+ void *res = __builtin_apply((void (*)(void)) real_prctl, __builtin_apply_args(), sizeof(long) * 5);
__builtin_return(res);
}
diff --git a/ports/linux/subports b/ports/linux/subports
index 740ec83..099ea59 100755
--- a/ports/linux/subports
+++ b/ports/linux/subports
@@ -55,6 +55,8 @@ 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>
@@ -62,6 +64,9 @@ 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 3824fd8..97b16c2 100644
--- a/ports/linux/wrapfuncs.in
+++ b/ports/linux/wrapfuncs.in
@@ -13,6 +13,7 @@ int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* real_func=p
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}); /* 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);
@@ -61,3 +62,5 @@ int capset(cap_user_header_t hdrp, const cap_user_data_t datap); /* real_func=ps
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 068d39a..beab7d0 100644
--- a/ports/linux/xattr/portdefs.h
+++ b/ports/linux/xattr/portdefs.h
@@ -3,5 +3,8 @@
*
*/
#include <sys/xattr.h>
-#include <attr/attributes.h>
#include <stdint.h>
+
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
diff --git a/pseudo.c b/pseudo.c
index 528fe1b..4f36786 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -682,7 +682,8 @@ 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;
diff --git a/pseudo_client.c b/pseudo_client.c
index f846d54..a03d6b1 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -846,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,
@@ -983,6 +983,23 @@ pseudo_client_close(int fd) {
}
}
+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 */
static int
client_spawn_server(void) {
@@ -1589,7 +1606,8 @@ int pseudo_client_ignore_path(const char *path) {
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;
@@ -1599,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;
@@ -1626,10 +1645,19 @@ 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
+ 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) {
- pseudo_client_path(fd, path);
+ /* 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();
@@ -1890,6 +1918,32 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
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) {
@@ -1972,7 +2026,7 @@ pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path
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
diff --git a/pseudo_db.c b/pseudo_db.c
index 6c48cc8..14bafcb 100644
--- a/pseudo_db.c
+++ b/pseudo_db.c
@@ -399,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,
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_server.c b/pseudo_server.c
index 84cef05..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;
diff --git a/pseudo_util.c b/pseudo_util.c
index b6980c2..64636b7 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -679,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) {
@@ -689,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);
@@ -791,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) {
@@ -809,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.
*/
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/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-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-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