diff options
33 files changed, 574 insertions, 33 deletions
@@ -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 @@ -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 |