From patchwork Mon Apr 7 19:14:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gyorgy Sarvari X-Patchwork-Id: 60894 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5BB56C36010 for ; Mon, 7 Apr 2025 19:14:23 +0000 (UTC) Received: from mail-ed1-f45.google.com (mail-ed1-f45.google.com [209.85.208.45]) by mx.groups.io with SMTP id smtpd.web11.56292.1744053259159296536 for ; Mon, 07 Apr 2025 12:14:19 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=IogH76rl; spf=pass (domain: gmail.com, ip: 209.85.208.45, mailfrom: skandigraun@gmail.com) Received: by mail-ed1-f45.google.com with SMTP id 4fb4d7f45d1cf-5ec9d24acfbso10701730a12.0 for ; Mon, 07 Apr 2025 12:14:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1744053257; x=1744658057; darn=lists.yoctoproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uByFGzjN4xZLlQrIhFPjuQheG2Rb3LBGPC9Kj8kWMNk=; b=IogH76rluvkNyb8jpxzXPfCrSNVIxp4afN7wGrbhTANoQ/1ml7au5nHEylOK8o2B/2 VAANODr+2sp/QcQh/7kl3AnUdXxgYMkX6+tXC5KEzB4OimbOTf5gxWoeFkzISU0ENB2A UZao1rOmEENwXj5ByGXt32LCxUfadKRzfJqXI8DP5/yDQ/vQdecgEakZOvIi3g2W6ZQ5 x6vrkrBRvCfq7sQM0oTRp2godQws1cGNh7n9jZh7EUHkGmlJi+2ZkrydR5v/2bs0y7Yl iEgMYdsnekBqhbPBJ0Y7hNxJVEyqcTWRFe1vTBb+4AlDnNvdk40byMh/x1bMbTzlg1as raVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744053257; x=1744658057; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uByFGzjN4xZLlQrIhFPjuQheG2Rb3LBGPC9Kj8kWMNk=; b=sF/ZvbXNx1tEH9cuUcFOtMc61J6JAAZVLP57jEigs0M86MPKG/tr5uwhRCYt6fJdlI UCP28ZUpSeKDPJWlteOU/ZOcVBxu1oEbe6Q9VmhM6FZwLIoRFFzpLTVsyNZ25orB6NHQ AB83wySMfpx/mt6eRD9OK73iBnjCJD2jXb8p84fEdVIRjn7o7pnl5VV2Uorex7IDwGQ3 mm7ZuXCN3oQjBlLK5WVrQCyK+DJSNiRcCgn/jOLTtThRxy9p+crudDopVGindfHqb1rm wFtCMdpyd+3ZtA4lanx6a27l4sBr77aEIpBCYOIllkGSwjW9Du20ChhLSAenTe/kbast p4TA== X-Gm-Message-State: AOJu0Ywr1jMmuxWd/ZSh9EUzvXr2+dAToWpTu9AwXPUJlEA+JYLwsfHH ZHcd1ujI3n1DDf4qSbvXMJ4iPbUd80of7lIMZ0ucyrI+uGbwXndOWf2T7A== X-Gm-Gg: ASbGnctsqXibEGhv1FlIWFWcIA7JWqAqlO04JwZXl18AB9/kvdLA5YdyeoXAJDtSPsv OsXAw7bE2G68VCObbHR0GsQqeUMN3w+BcUs4QXT5MEqevygQ6UBPwuk4tdFNqOsHqfqe8CBA8Jt p7kFPosO3eLtoWBaZog4gNt2svMK9PXLF2gq2vNop0T4y9n9V8ZrXPVKnBPExKFrFB5LaB58cyx blBwD/G10zB4rnnOytl4SrCTvZQNAs/DLvucQx7R4lZZ/3Oo9muBSjJMMPKJU7WfkaG/oBoV2JU ehbBsJntgq1RcpuKz9is+2z3NnA67yDzylaeu4qHeARZ3tirhRXlf18HMuLK X-Google-Smtp-Source: AGHT+IGHFeh5njLcsxvnpJEWlnpFOZnsDFVB0/62w9zUptObTDzXJIEr4WJy6kS8YMGLyVTLF+5sYw== X-Received: by 2002:a05:6402:4415:b0:5e4:d52b:78a2 with SMTP id 4fb4d7f45d1cf-5f1f4808cb2mr473501a12.15.1744053256884; Mon, 07 Apr 2025 12:14:16 -0700 (PDT) Received: from localhost.localdomain ([51.154.145.205]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5f087eedf61sm6916078a12.32.2025.04.07.12.14.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Apr 2025 12:14:16 -0700 (PDT) From: Gyorgy Sarvari To: yocto-patches@lists.yoctoproject.org Cc: landervanloock@gmail.com Subject: [pseudo][PATCH v2 1/2] nftw, ftw: add wrapper Date: Mon, 7 Apr 2025 21:14:13 +0200 Message-ID: <20250407191414.2992785-2-skandigraun@gmail.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250407191414.2992785-1-skandigraun@gmail.com> References: <20250407191414.2992785-1-skandigraun@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 07 Apr 2025 19:14:23 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/1298 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 --- 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 diff --git a/guts/README b/guts/README index 0a1fe5f..5bcc198 100644 --- a/guts/README +++ b/guts/README @@ -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 diff --git a/ports/linux/guts/ftw64.c b/ports/linux/guts/ftw64.c deleted file mode 100644 index 48adb80..0000000 --- a/ports/linux/guts/ftw64.c +++ /dev/null @@ -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; - * } - */ diff --git a/ports/linux/nftw64/guts/ftw64.c b/ports/linux/nftw64/guts/ftw64.c new file mode 100644 index 0000000..1fe0868 --- /dev/null +++ b/ports/linux/nftw64/guts/ftw64.c @@ -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; + * } + */ diff --git a/ports/linux/guts/nftw64.c b/ports/linux/nftw64/guts/nftw64.c similarity index 57% rename from ports/linux/guts/nftw64.c rename to ports/linux/nftw64/guts/nftw64.c index 816faba..0dac9d3 100644 --- a/ports/linux/guts/nftw64.c +++ b/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; * } diff --git a/ports/linux/nftw64/pseudo_wrappers.c b/ports/linux/nftw64/pseudo_wrappers.c new file mode 100644 index 0000000..2bd8227 --- /dev/null +++ b/ports/linux/nftw64/pseudo_wrappers.c @@ -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" diff --git a/ports/linux/nftw64/wrapfuncs.in b/ports/linux/nftw64/wrapfuncs.in new file mode 100644 index 0000000..3e2ed67 --- /dev/null +++ b/ports/linux/nftw64/wrapfuncs.in @@ -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); diff --git a/ports/linux/subports b/ports/linux/subports index 099ea59..b55bea9 100755 --- a/ports/linux/subports +++ b/ports/linux/subports @@ -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 < +#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 diff --git a/ports/linux/wrapfuncs.in b/ports/linux/wrapfuncs.in index 60ce5f5..c4b06ae 100644 --- a/ports/linux/wrapfuncs.in +++ b/ports/linux/wrapfuncs.in @@ -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); diff --git a/ports/unix/guts/ftw.c b/ports/unix/guts/ftw.c index 58945a1..4b3f49f 100644 --- a/ports/unix/guts/ftw.c +++ b/ports/unix/guts/ftw.c @@ -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; * } diff --git a/ports/unix/guts/nftw.c b/ports/unix/guts/nftw.c index dac3106..d22cd1c 100644 --- a/ports/unix/guts/nftw.c +++ b/ports/unix/guts/nftw.c @@ -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; * } diff --git a/ports/unix/guts/nftw_wrapper_base.c b/ports/unix/guts/nftw_wrapper_base.c new file mode 100644 index 0000000..7479b80 --- /dev/null +++ b/ports/unix/guts/nftw_wrapper_base.c @@ -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; +} diff --git a/ports/unix/pseudo_wrappers.c b/ports/unix/pseudo_wrappers.c index bf69aa9..5f15930 100644 --- a/ports/unix/pseudo_wrappers.c +++ b/ports/unix/pseudo_wrappers.c @@ -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" diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in index 7724fc7..5b1f557 100644 --- a/ports/unix/wrapfuncs.in +++ b/ports/unix/wrapfuncs.in @@ -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);