@@ -54,6 +54,8 @@ other functions:
__xmknod: __xmknodat
__xstat64: __fxstatat64
__xstat: __fxstatat
+ ftw: nftw
+ ftw64: nftw64
The following functions are full implementations:
@@ -129,15 +131,11 @@ calling the underlying routine.
eaccess
euidaccess
fts_open
- ftw64
- ftw
glob64
glob
lutimes
mkdtemp
mktemp
- nftw64
- nftw
opendir
pathconf
readlinkat
deleted file mode 100644
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) 2010 Wind River Systems; see
- * guts/COPYRIGHT for information.
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * static int
- * wrap_ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd) {
- * int rc = -1;
- */
-
- rc = real_ftw64(path, fn, nopenfd);
-
-/* return rc;
- * }
- */
new file mode 100644
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static int
+ * wrap_ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd) {
+ * int rc = -1;
+ */
+ typedef int (*pseudo_nftw64_func_t) (const char *filename,
+ const struct stat64 *status, int flag,
+ struct FTW *info);
+
+ // 1. Set the flag argument to 0, just like glibc does.
+ // 2. The difference between ftw and nftw callback
+ // is only the last parameter: struct FTW is only used
+ // by nftw(), and it is missing from ftw().
+ // However since otherwise the stacklayout for the
+ // functions is the same, this cast should work just the
+ // way we want it. This is also borrowed from glibc.
+
+ // This, of course in turn will be captured by pseudo, and will be passed
+ // to the respective wrapper of nftw64()
+ rc = nftw64(path, (pseudo_nftw64_func_t)fn, nopenfd, 0);
+
+/* return rc;
+ * }
+ */
similarity index 57%
rename from ports/linux/guts/nftw64.c
rename to ports/linux/nftw64/guts/nftw64.c
@@ -9,7 +9,12 @@
* int rc = -1;
*/
- rc = real_nftw64(path, fn, nopenfd, flag);
+/***********************************
+ * This call has a custom wrapper
+ * where all the logic happens just
+ * around this single line.
+ ***********************************/
+ rc = real_nftw64(path, NFTW_CALLBACK_NAME, nopenfd, flag);
/* return rc;
* }
new file mode 100644
@@ -0,0 +1,45 @@
+#undef NFTW_NAME
+#undef NFTW_WRAP_NAME
+#undef NFTW_REAL_NAME
+#undef NFTW_STAT_STRUCT
+#undef NFTW_STAT_NAME
+#undef NFTW_LSTAT_NAME
+#undef NFTW_GUTS_INCLUDE
+#undef NFTW_CALLBACK_NAME
+#undef NFTW_STORAGE_STRUCT_NAME
+#undef NFTW_STORAGE_ARRAY_SIZE
+#undef NFTW_MUTEX_NAME
+#undef NFTW_STORAGE_ARRAY_NAME
+#undef NFTW_APPEND_FN_NAME
+#undef NFTW_DELETE_FN_NAME
+#undef NFTW_FIND_FN_NAME
+
+#define CONCAT_EXPANDED(prefix, value) prefix ## value
+#define CONCAT(prefix, value) CONCAT_EXPANDED(prefix, value)
+
+#define NFTW_NAME nftw64
+#define NFTW_WRAP_NAME CONCAT(wrap_, NFTW_NAME)
+#define NFTW_REAL_NAME CONCAT(real_, NFTW_NAME)
+#define NFTW_STAT_STRUCT stat64
+#define NFTW_STAT_NAME stat64
+#define NFTW_LSTAT_NAME lstat64
+
+// this file is in the same directory as this wrapper, but
+// this path is relative to the nftw_wrapper_base file, where
+// it gets included.
+#define NFTW_GUTS_INCLUDE "../../linux/nftw64/guts/nftw64.c"
+#define NFTW_CALLBACK_NAME CONCAT(wrap_callback_, NFTW_NAME)
+#define NFTW_STORAGE_STRUCT_NAME CONCAT(storage_struct_, NFTW_NAME)
+#define NFTW_STORAGE_ARRAY_SIZE CONCAT(storage_size_, NFTW_NAME)
+#define NFTW_MUTEX_NAME CONCAT(mutex_, NFTW_NAME)
+#define NFTW_STORAGE_ARRAY_NAME CONCAT(storage_array_, NFTW_NAME)
+#define NFTW_APPEND_FN_NAME CONCAT(append_to_array_, NFTW_NAME)
+#define NFTW_DELETE_FN_NAME CONCAT(delete_from_array_, NFTW_NAME)
+#define NFTW_FIND_FN_NAME CONCAT(find_in_array_, NFTW_NAME)
+
+// nftw64() is identical to nftw() in all regards, except
+// that it uses "stat64" structs instead of "stat", so
+// the whole codebase can be reused, accounting for naming
+// and type changes using the macros above.
+
+#include "../../unix/guts/nftw_wrapper_base.c"
new file mode 100644
@@ -0,0 +1,2 @@
+int nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag); /* noignore_path=1, hand_wrapped=1 */
+int ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd);
@@ -70,3 +70,17 @@ else
fi
rm -f dummy.c dummy.o
+# nftw64 (and its deprecated pair, ftw64) are only present in case large
+# file support is present.
+cat > dummy.c <<EOF
+#define _GNU_SOURCE
+#include <features.h>
+#ifndef __USE_LARGEFILE64
+#error "no large file support"
+#endif
+int i;
+EOF
+if ${CC} -c -o dummy.o dummy.c >/dev/null 2>&1; then
+ echo linux/nftw64
+fi
+rm -f dummy.c dummy.o
@@ -34,9 +34,7 @@ int __lxstat64(int ver, const char *path, struct stat64 *buf); /* flags=AT_SYMLI
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); /* 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);
@@ -9,7 +9,18 @@
* int rc = -1;
*/
- rc = real_ftw(path, fn, nopenfd);
+ typedef int (*pseudo_nftw_func_t) (const char *filename,
+ const struct stat *status, int flag,
+ struct FTW *info);
+
+ // 1. Set the flag argument to 0, just like glibc does.
+ // 2. The difference between ftw and nftw callback
+ // is only the last parameter: struct FTW is only used
+ // by nftw(), and it is missing from ftw().
+ // However since otherwise the stacklayout for the
+ // functions is the same, this cast should work just the
+ // way we want it. This is also borrowed from glibc.
+ rc = nftw(path, (pseudo_nftw_func_t)fn, nopenfd, 0);
/* return rc;
* }
@@ -9,7 +9,12 @@
* int rc = -1;
*/
- rc = real_nftw(path, fn, nopenfd, flag);
+/***********************************
+ * This call has a custom wrapper
+ * where all the logic happens just
+ * around this single line.
+ ***********************************/
+ rc = real_nftw(path, NFTW_CALLBACK_NAME, nopenfd, flag);
/* return rc;
* }
new file mode 100644
@@ -0,0 +1,211 @@
+int
+NFTW_NAME(const char *path, int (*fn)(const char *, const struct NFTW_STAT_STRUCT *, int, struct FTW *), int nopenfd, int flag) {
+ sigset_t saved;
+
+ int rc = -1;
+
+ if (!pseudo_check_wrappers() || !NFTW_REAL_NAME) {
+ /* rc was initialized to the "failure" value */
+ pseudo_enosys("nftw");
+ return rc;
+ }
+
+ if (pseudo_disabled) {
+ rc = (*NFTW_REAL_NAME)(path, fn, nopenfd, flag);
+
+ return rc;
+ }
+
+ pseudo_debug(PDBGF_WRAPPER, "wrapper called: %s\n", __func__);
+ pseudo_sigblock(&saved);
+ pseudo_debug(PDBGF_WRAPPER | PDBGF_VERBOSE, "%s - signals blocked, obtaining lock\n", __func__);
+ if (pseudo_getlock()) {
+ errno = EBUSY;
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(PDBGF_WRAPPER, "%s failed to get lock, giving EBUSY.\n", __func__);
+ return -1;
+ }
+
+ int save_errno;
+ if (antimagic > 0) {
+ /* call the real syscall */
+ pseudo_debug(PDBGF_SYSCALL, "%s calling real syscall.\n", __func__);
+ rc = (*NFTW_REAL_NAME)(path, fn, nopenfd, flag);
+ } else {
+ path = pseudo_root_path(__func__, __LINE__, AT_FDCWD, path, 0);
+ if (pseudo_client_ignore_path(path)) {
+ /* call the real syscall */
+ pseudo_debug(PDBGF_SYSCALL, "%s ignored path, calling real syscall.\n", __func__);
+ rc = (*NFTW_REAL_NAME)(path, fn, nopenfd, flag);
+ } else {
+ /* exec*() use this to restore the sig mask */
+ pseudo_saved_sigmask = saved;
+ rc = NFTW_WRAP_NAME(path, fn, nopenfd, flag);
+ }
+ }
+
+ save_errno = errno;
+ pseudo_droplock();
+ sigprocmask(SIG_SETMASK, &saved, NULL);
+ pseudo_debug(PDBGF_WRAPPER | PDBGF_VERBOSE, "%s - yielded lock, restored signals\n", __func__);
+ pseudo_debug(PDBGF_WRAPPER, "wrapper completed: %s returns %d (errno: %d)\n", __func__, rc, save_errno);
+ errno = save_errno;
+ return rc;
+}
+
+struct NFTW_STORAGE_STRUCT_NAME {
+ int (*callback)(const char *, const struct NFTW_STAT_STRUCT *, int, struct FTW *);
+ int flags;
+ pthread_t tid;
+};
+
+static struct NFTW_STORAGE_STRUCT_NAME *NFTW_STORAGE_ARRAY_NAME;
+size_t NFTW_STORAGE_ARRAY_SIZE = 0;
+static pthread_mutex_t NFTW_MUTEX_NAME = PTHREAD_MUTEX_INITIALIZER;
+
+static void NFTW_APPEND_FN_NAME(struct NFTW_STORAGE_STRUCT_NAME *data_to_append){
+ NFTW_STORAGE_ARRAY_NAME = realloc(NFTW_STORAGE_ARRAY_NAME, ++NFTW_STORAGE_ARRAY_SIZE * sizeof(*data_to_append));
+ memcpy(&NFTW_STORAGE_ARRAY_NAME[NFTW_STORAGE_ARRAY_SIZE - 1], data_to_append, sizeof(*data_to_append));
+}
+
+int NFTW_FIND_FN_NAME(struct NFTW_STORAGE_STRUCT_NAME* target) {
+ pthread_t tid = pthread_self();
+
+ // return the last one, not the first
+ for (ssize_t i = NFTW_STORAGE_ARRAY_SIZE - 1; i >= 0; --i){
+ if (NFTW_STORAGE_ARRAY_NAME[i].tid == tid){
+ // need to dereference it, as next time this array
+ // may be realloc'd, making the original pointer
+ // invalid
+ *target = NFTW_STORAGE_ARRAY_NAME[i];
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void NFTW_DELETE_FN_NAME() {
+ pthread_t tid = pthread_self();
+
+ if (NFTW_STORAGE_ARRAY_SIZE == 1) {
+ if (NFTW_STORAGE_ARRAY_NAME[0].tid == tid) {
+ free(NFTW_STORAGE_ARRAY_NAME);
+ NFTW_STORAGE_ARRAY_NAME = NULL;
+ --NFTW_STORAGE_ARRAY_SIZE;
+ } else {
+ pseudo_diag("%s: Invalid callback storage content, can't find corresponding data", __func__);
+ }
+ return;
+ }
+
+ int found_idx = -1;
+ for (ssize_t i = NFTW_STORAGE_ARRAY_SIZE - 1; i >= 0; --i) {
+ if (NFTW_STORAGE_ARRAY_NAME[i].tid == tid) {
+ found_idx = i;
+ break;
+ }
+ }
+
+ if (found_idx == -1) {
+ pseudo_diag("%s: Invalid callback storage content, can't find corresponding data", __func__);
+ return;
+ }
+
+ // delete the item we just found
+ for (size_t i = found_idx + 1; i < NFTW_STORAGE_ARRAY_SIZE; ++i)
+ NFTW_STORAGE_ARRAY_NAME[i - 1] = NFTW_STORAGE_ARRAY_NAME[i];
+
+ NFTW_STORAGE_ARRAY_NAME = realloc(NFTW_STORAGE_ARRAY_NAME, --NFTW_STORAGE_ARRAY_SIZE * sizeof(struct NFTW_STORAGE_STRUCT_NAME));
+
+}
+
+static int NFTW_CALLBACK_NAME(const char* fpath, const struct NFTW_STAT_STRUCT *sb, int typeflag, struct FTW *ftwbuf) {
+ int orig_cwd_fd = -1;
+ char *orig_cwd = NULL;
+ char *target_dir = NULL;
+ struct NFTW_STORAGE_STRUCT_NAME saved_details;
+
+ if (!NFTW_FIND_FN_NAME(&saved_details)) {
+ pseudo_diag("%s: Could not find corresponding callback!", __func__);
+ return -1;
+ }
+
+ // This flag is handled by nftw, however the actual directory change happens
+ // outside of pseudo, so it doesn't have any effect. To mitigate this, handle
+ // it here also explicitly.
+ //
+ // This is very similar to what glibc is doing: keep an open FD for the
+ // current working directory, process the entry (determine the flags, etc),
+ // call the callback, and then switch back to the original folder - in the same
+ // process. Glibc doesn't seem to take any further thread-safety measures nor
+ // other special steps.
+ // Error checking is not done here, as if real_nftw couldn't perform it,
+ // then it has already returned an error, and this part isn't reached. This
+ // just repeats the successful steps.
+ //
+ // See io/ftw.c in glibc source
+ if (saved_details.flags & FTW_CHDIR) {
+ orig_cwd_fd = open(".", O_RDONLY | O_DIRECTORY);
+ if (orig_cwd_fd == -1) {
+ orig_cwd = getcwd(NULL, 0);
+ }
+
+ // If it is a folder that's content has been already walked with the
+ // FTW_DEPTH flag, then switch into this folder, instead of the parent of
+ // it. This matches the behavior of the real nftw in this special case.
+ // This seems to be undocumented - it was derived by observing this behavior.
+ if (typeflag == FTW_DP) {
+ chdir(fpath);
+ } else {
+ target_dir = malloc(ftwbuf->base + 1);
+ memset(target_dir, 0, ftwbuf->base + 1);
+ strncpy(target_dir, fpath, ftwbuf->base);
+ chdir(target_dir);
+ }
+ }
+
+ // This is the main point of this call. Instead of the stat that
+ // came from real_nftw, use the stat returned by pseudo.
+ // If the target can't be stat'd (DNR), then just forward whatever
+ // is inside - no information can be retrieved of it anyway.
+ if (typeflag != FTW_DNR) {
+ (saved_details.flags & FTW_PHYS) ? NFTW_LSTAT_NAME(fpath, sb) : NFTW_STAT_NAME(fpath, sb);
+ }
+
+ int ret = saved_details.callback(fpath, sb, typeflag, ftwbuf);
+
+ if (saved_details.flags & FTW_CHDIR) {
+ if (orig_cwd_fd != -1) {
+ fchdir(orig_cwd_fd);
+ close(orig_cwd_fd);
+ } else if (orig_cwd != NULL) {
+ chdir(orig_cwd);
+ }
+ free(target_dir);
+ }
+
+ return ret;
+}
+
+static int
+NFTW_WRAP_NAME(const char *path, int (*fn)(const char *, const struct NFTW_STAT_STRUCT *, int, struct FTW *), int nopenfd, int flag) {
+ int rc = -1;
+
+ struct NFTW_STORAGE_STRUCT_NAME saved_details;
+
+ saved_details.tid = pthread_self();
+ saved_details.flags = flag;
+ saved_details.callback = fn;
+
+ pthread_mutex_lock(&NFTW_MUTEX_NAME);
+ NFTW_APPEND_FN_NAME(&saved_details);
+ pthread_mutex_unlock(&NFTW_MUTEX_NAME);
+
+#include NFTW_GUTS_INCLUDE
+
+ pthread_mutex_lock(&NFTW_MUTEX_NAME);
+ NFTW_DELETE_FN_NAME();
+ pthread_mutex_unlock(&NFTW_MUTEX_NAME);
+ return rc;
+}
@@ -2,6 +2,10 @@
* SPDX-License-Identifier: LGPL-2.1-only
*
*/
+
+/**********************************************
+ * POPEN
+ **********************************************/
FILE *
popen(const char *command, const char *mode) {
sigset_t saved;
@@ -52,3 +56,44 @@ wrap_popen(const char *command, const char *mode) {
return rc;
}
+
+/**********************************************
+ * NFTW
+ **********************************************/
+
+#define CONCAT_EXPANDED(prefix, value) prefix ## value
+#define CONCAT(prefix, value) CONCAT_EXPANDED(prefix, value)
+
+#undef NFTW_NAME
+#undef NFTW_WRAP_NAME
+#undef NFTW_REAL_NAME
+#undef NFTW_STAT_STRUCT
+#undef NFTW_STAT_NAME
+#undef NFTW_LSTAT_NAME
+#undef NFTW_GUTS_INCLUDE
+#undef NFTW_CALLBACK_NAME
+#undef NFTW_STORAGE_STRUCT_NAME
+#undef NFTW_STORAGE_ARRAY_SIZE
+#undef NFTW_MUTEX_NAME
+#undef NFTW_STORAGE_ARRAY_NAME
+#undef NFTW_APPEND_FN_NAME
+#undef NFTW_DELETE_FN_NAME
+#undef NFTW_FIND_FN_NAME
+
+#define NFTW_NAME nftw
+#define NFTW_WRAP_NAME CONCAT(wrap_, NFTW_NAME)
+#define NFTW_REAL_NAME CONCAT(real_, NFTW_NAME)
+#define NFTW_STAT_NAME stat
+#define NFTW_STAT_STRUCT stat
+#define NFTW_LSTAT_NAME lstat
+#define NFTW_CALLBACK_NAME CONCAT(wrap_callback_, NFTW_NAME)
+#define NFTW_GUTS_INCLUDE "nftw.c"
+#define NFTW_STORAGE_STRUCT_NAME CONCAT(storage_struct_, NFTW_NAME)
+#define NFTW_STORAGE_ARRAY_SIZE CONCAT(storage_size_, NFTW_NAME)
+#define NFTW_MUTEX_NAME CONCAT(mutex_, NFTW_NAME)
+#define NFTW_STORAGE_ARRAY_NAME CONCAT(storage_array_, NFTW_NAME)
+#define NFTW_APPEND_FN_NAME CONCAT(append_to_array_, NFTW_NAME)
+#define NFTW_DELETE_FN_NAME CONCAT(delete_from_array_, NFTW_NAME)
+#define NFTW_FIND_FN_NAME CONCAT(find_in_array_, NFTW_NAME)
+
+#include "guts/nftw_wrapper_base.c"
@@ -14,7 +14,7 @@ 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);
+int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag); /* hand_wrapped=1 */
int glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob);
int lutimes(const char *path, const struct timeval *tv); /* flags=AT_SYMLINK_NOFOLLOW */
char *mkdtemp(char *template);
Add a wrapper for nftw and ftw[1] calls (along with nftw64 and ftw64). The call in brief: it accepts a path, which it walks. For every entries it finds, it calls a user-specified callback function, and passes some information about the entry to this callback. The implementation saves the callback from the nftw call, and subtitutes it with its own "fake_callback". When the real nftw calls the fake_callback, it corrects the stat struct it received with information queried from pseudo. Afterwards it calls the original callback and passes the now corrected information to it. The 4 functions are very similar to each other: nftw-nftw64 and ftw-ftw64 are identical to their pair, except for the stat struct they use (stat vs stat64). nftw is a "superset" of ftw: nftw is backwards compatible with ftw, but it also accepts a number of extra flags to modify its behavior. Since all the 4 functions are so similar, the same codebase is used to implement all (which is also fairly similar to their implementation in glibc). [1]: https://linux.die.net/man/3/nftw Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> --- guts/README | 6 +- ports/linux/guts/ftw64.c | 16 -- ports/linux/nftw64/guts/ftw64.c | 29 ++++ ports/linux/{ => nftw64}/guts/nftw64.c | 7 +- ports/linux/nftw64/pseudo_wrappers.c | 45 ++++++ ports/linux/nftw64/wrapfuncs.in | 2 + ports/linux/subports | 14 ++ ports/linux/wrapfuncs.in | 2 - ports/unix/guts/ftw.c | 13 +- ports/unix/guts/nftw.c | 7 +- ports/unix/guts/nftw_wrapper_base.c | 211 +++++++++++++++++++++++++ ports/unix/pseudo_wrappers.c | 45 ++++++ ports/unix/wrapfuncs.in | 2 +- 13 files changed, 373 insertions(+), 26 deletions(-) delete mode 100644 ports/linux/guts/ftw64.c create mode 100644 ports/linux/nftw64/guts/ftw64.c rename ports/linux/{ => nftw64}/guts/nftw64.c (57%) create mode 100644 ports/linux/nftw64/pseudo_wrappers.c create mode 100644 ports/linux/nftw64/wrapfuncs.in create mode 100644 ports/unix/guts/nftw_wrapper_base.c