Re: [PATCH v2] busybox: fix TOCTOU race in various directory traversal

Sertonix via busybox <[email protected]>
Newsgroups gmane.linux.busybox
Message-ID <[email protected]>
v2: rebased for changes to the master branch

_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox
0001-recursive_action-prevent-file-type-confusion-when-fi.patch (text/x-patch, 7.2 KB)
From c48bb32079da360b918a497cc2157b6cf79ea66f Mon Sep 17 00:00:00 2001
From: Sertonix <[email protected]>
Date: Tue, 9 Dec 2025 15:12:14 +0100
Subject: [PATCH 1/7] recursive_action: prevent file type confusion when files
 changed

It could for example unintentionally follow symlinks when a directory
is replaced with a symlink after the type check. Callers need to use
state->dirfd and state->baseName in some cases to make this effective.

function                                             old     new   delta
recursive_action1                                    418     721    +303
recursive_action                                     108     157     +49
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 352/0)             Total: 352 bytes
---
 include/libbb.h          |  2 ++
 libbb/recursive_action.c | 66 +++++++++++++++++++++++++++-------------
 2 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/include/libbb.h b/include/libbb.h
index 76d79da23..808ef365e 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -541,6 +541,8 @@ typedef struct recursive_state {
 	unsigned flags;
 	unsigned depth;
 	void *userData;
+	char *fileName, *baseName;
+	int dirfd;
 	int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
 	int FAST_FUNC  (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
 } recursive_state_t;
diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c
index b1c4bfad7..f168001be 100644
--- a/libbb/recursive_action.c
+++ b/libbb/recursive_action.c
@@ -15,10 +15,8 @@
  * location, and do something (something specified
  * by the fileAction and dirAction function pointers).
  *
- * Unfortunately, while nftw(3) could replace this and reduce
- * code size a bit, nftw() wasn't supported before GNU libc 2.1,
- * and so isn't sufficiently portable to take over since glibc2.1
- * is so stinking huge.
+ * Unfortunately, while nftw(3) works very similar it does not expose
+ * the file descriptors to allow safe usage.
  */
 
 static int FAST_FUNC true_action(struct recursive_state *state UNUSED_PARAM,
@@ -64,11 +62,11 @@ static int FAST_FUNC true_action(struct recursive_state *state UNUSED_PARAM,
  * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
  */
 
-static int recursive_action1(recursive_state_t *state, const char *fileName)
+static int recursive_action1(recursive_state_t *state)
 {
 	struct stat statbuf;
 	unsigned follow;
-	int status;
+	int status, olddirfd = state->dirfd;
 	DIR *dir;
 	struct dirent *next;
 
@@ -76,17 +74,17 @@ static int recursive_action1(recursive_state_t *state, const char *fileName)
 	if (state->depth == 0)
 		follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0;
 	follow &= state->flags;
-	status = (follow ? stat : lstat)(fileName, &statbuf);
+	status = fstatat(state->dirfd, state->baseName, &statbuf, follow ? 0 : AT_SYMLINK_NOFOLLOW);
 	if (status < 0) {
 #ifdef DEBUG_RECURS_ACTION
 		bb_error_msg("status=%d flags=%x", status, state->flags);
 #endif
 		if ((state->flags & ACTION_DANGLING_OK)
 		 && errno == ENOENT
-		 && lstat(fileName, &statbuf) == 0
+		 && fstatat(state->dirfd, state->baseName, &statbuf, AT_SYMLINK_NOFOLLOW) == 0
 		) {
 			/* Dangling link */
-			return state->fileAction(state, fileName, &statbuf);
+			return state->fileAction(state, state->fileName, &statbuf);
 		}
 		goto done_nak_warn;
 	}
@@ -97,57 +95,75 @@ static int recursive_action1(recursive_state_t *state, const char *fileName)
 	if ( /* (!(state->flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */
 	 !S_ISDIR(statbuf.st_mode)
 	) {
-		return state->fileAction(state, fileName, &statbuf);
+		return state->fileAction(state, state->fileName, &statbuf);
 	}
 
 	/* It's a directory (or a link to one, and followLinks is set) */
 
 	if (!(state->flags & ACTION_RECURSE)) {
-		return state->dirAction(state, fileName, &statbuf);
+		return state->dirAction(state, state->fileName, &statbuf);
 	}
 
 	if (!(state->flags & ACTION_DEPTHFIRST)) {
-		status = state->dirAction(state, fileName, &statbuf);
+		status = state->dirAction(state, state->fileName, &statbuf);
 		if (status == FALSE)
 			goto done_nak_warn;
 		if (status == SKIP)
 			return TRUE;
 	}
 
-	dir = opendir(fileName);
+	state->dirfd = openat(olddirfd, state->baseName, O_RDONLY | O_DIRECTORY | (follow ? 0 : O_NOFOLLOW));
+	if (state->dirfd < 0)
+		goto done_nak_warn;
+	dir = fdopendir(state->dirfd);
 	if (!dir) {
 		/* findutils-4.1.20 reports this */
 		/* (i.e. it doesn't silently return with exit code 1) */
 		/* To trigger: "find -exec rm -rf {} \;" */
 		goto done_nak_warn;
 	}
+	state->dirfd = dirfd(dir);
 	status = TRUE;
 	while ((next = readdir(dir)) != NULL) {
-		char *nextFile;
+		size_t n1, n2, n3;
 		int s;
 
-		nextFile = concat_subpath_file(fileName, next->d_name);
-		if (nextFile == NULL)
+		if (DOT_OR_DOTDOT(next->d_name))
 			continue;
 
+		n1 = strlen(state->fileName);
+		n2 = (state->fileName[n1 - 1] != '/'); /* 1: "path has no trailing slash" */
+		n3 = strlen(next->d_name) + 1;
+
+		state->fileName = xrealloc(state->fileName, n1 + n2 + n3);
+		if (n2)
+			state->fileName[n1] = '/';
+		state->baseName = &state->fileName[n1+n2];
+		memcpy(state->baseName, next->d_name, n3);
+
 		/* process every file (NB: ACTION_RECURSE is set in flags) */
 		state->depth++;
-		s = recursive_action1(state, nextFile);
+		s = recursive_action1(state);
 		if (s == FALSE)
 			status = FALSE;
-		free(nextFile);
 		state->depth--;
 
+		state->fileName = xrealloc(state->fileName, n1 + 1);
+		state->fileName[n1] = '\0';
+		state->baseName = strrchr(state->fileName, '/');
+		state->baseName = state->baseName ? state->baseName + 1 : state->fileName;
+
 //#define RECURSE_RESULT_ABORT -1
 //		if (s == RECURSE_RESULT_ABORT) {
 //			closedir(dir);
 //			return s;
 //		}
 	}
+	state->dirfd = olddirfd;
 	closedir(dir);
 
 	if (state->flags & ACTION_DEPTHFIRST) {
-		if (!state->dirAction(state, fileName, &statbuf))
+		if (!state->dirAction(state, state->fileName, &statbuf))
 			goto done_nak_warn;
 	}
 
@@ -155,7 +171,9 @@ static int recursive_action1(recursive_state_t *state, const char *fileName)
 
  done_nak_warn:
 	if (!(state->flags & ACTION_QUIET))
-		bb_simple_perror_msg(fileName);
+		bb_simple_perror_msg(state->fileName);
+
+	state->dirfd = olddirfd;
 	return FALSE;
 }
 
@@ -165,6 +183,7 @@ int FAST_FUNC recursive_action(const char *fileName,
 		int FAST_FUNC  (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
 		void *userData)
 {
+	int ret;
 	/* Keeping a part of variables of recusive descent in a "state structure"
 	 * instead of passing ALL of them down as parameters of recursive_action1()
 	 * relieves register pressure, both in recursive_action1()
@@ -174,8 +193,13 @@ int FAST_FUNC recursive_action(const char *fileName,
 	state.flags = flags;
 	state.depth = 0;
 	state.userData = userData;
+	state.fileName = xstrdup(fileName);
+	state.baseName = state.fileName;
+	state.dirfd = xopen(".", O_RDONLY|O_DIRECTORY);
 	state.fileAction = fileAction ? fileAction : true_action;
 	state.dirAction  =  dirAction ?  dirAction : true_action;
 
-	return recursive_action1(&state, fileName);
+	ret = recursive_action1(&state);
+	free(state.fileName);
+	return ret;
 }
-- 
2.53.0
0002-remove_file-switch-to-using-recursive_action.patch (text/x-patch, 9.3 KB)
From 1a8a9b2c946e8184da10925d3cb19f67ee271022 Mon Sep 17 00:00:00 2001
From: Sertonix <[email protected]>
Date: Tue, 9 Dec 2025 15:17:50 +0100
Subject: [PATCH 2/7] remove_file: switch to using recursive_action

Prevents recursive deletion to follow symlinks

function                                             old     new   delta
fileAction                                           200     337    +137
recursive_action                                     157     178     +21
recursive_action1                                    721     737     +16
.rodata                                           100908  100875     -33
remove_file                                          694      47    -647
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/2 up/down: 174/-680)         Total: -506 bytes
---
 coreutils/chown.c        |   2 +-
 findutils/find.c         |   4 +-
 findutils/grep.c         |   2 +-
 include/libbb.h          |   9 ++--
 libbb/recursive_action.c |  13 +++--
 libbb/remove_file.c      | 109 ++++++++++++---------------------------
 6 files changed, 49 insertions(+), 90 deletions(-)

diff --git a/coreutils/chown.c b/coreutils/chown.c
index 528a2a05a..c32812b8a 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -147,7 +147,7 @@ int chown_main(int argc UNUSED_PARAM, char **argv)
 		param.chown_func = lchown;
 	}
 
-	flags = ACTION_DEPTHFIRST; /* match coreutils order */
+	flags = ACTION_DEPTH_POST; /* match coreutils order */
 	if (OPT_RECURSE)
 		flags |= ACTION_RECURSE;
 	if (OPT_TRAVERSE_TOP)
diff --git a/findutils/find.c b/findutils/find.c
index 31c996988..582bc83bb 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -1307,7 +1307,7 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_DEPTH
 		else if (parm == OPT_DEPTH) {
 			dbg("%d", __LINE__);
-			G.recurse_flags |= ACTION_DEPTHFIRST;
+			G.recurse_flags |= ACTION_DEPTH_POST;
 		}
 #endif
 /* Actions are grouped by operators
@@ -1369,7 +1369,7 @@ static action*** parse_params(char **argv)
 		else if (parm == PARM_delete) {
 			dbg("%d", __LINE__);
 			G.need_print = 0;
-			G.recurse_flags |= ACTION_DEPTHFIRST;
+			G.recurse_flags |= ACTION_DEPTH_POST;
 			(void) ALLOC_ACTION(delete);
 		}
 #endif
diff --git a/findutils/grep.c b/findutils/grep.c
index f6d0a73f4..0a29be3a8 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -695,7 +695,7 @@ static int grep_dir(const char *dir)
 		| ACTION_RECURSE
 		| ((option_mask32 & OPT_R) ? ACTION_FOLLOWLINKS : 0)
 		| ACTION_FOLLOWLINKS_L0 /* grep -r ... SYMLINK follows it */
-		| ACTION_DEPTHFIRST
+		| ACTION_DEPTH_POST
 		| 0,
 		/* fileAction= */ file_action_grep,
 		/* dirAction= */ NULL,
diff --git a/include/libbb.h b/include/libbb.h
index 808ef365e..801fe101f 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -532,9 +532,10 @@ enum {
 	ACTION_RECURSE        = (1 << 0),
 	ACTION_FOLLOWLINKS    = (1 << 1),
 	ACTION_FOLLOWLINKS_L0 = (1 << 2),
-	ACTION_DEPTHFIRST     = (1 << 3),
-	ACTION_QUIET          = (1 << 4),
-	ACTION_DANGLING_OK    = (1 << 5),
+	ACTION_DEPTH_PRE      = (1 << 3),
+	ACTION_DEPTH_POST     = (1 << 4),
+	ACTION_QUIET          = (1 << 5),
+	ACTION_DANGLING_OK    = (1 << 6),
 };
 typedef uint8_t recurse_flags_t;
 typedef struct recursive_state {
@@ -542,7 +543,7 @@ typedef struct recursive_state {
 	unsigned depth;
 	void *userData;
 	char *fileName, *baseName;
-	int dirfd;
+	int dirfd, state;
 	int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
 	int FAST_FUNC  (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
 } recursive_state_t;
diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c
index f168001be..48182ded2 100644
--- a/libbb/recursive_action.c
+++ b/libbb/recursive_action.c
@@ -48,13 +48,13 @@ static int FAST_FUNC true_action(struct recursive_state *state UNUSED_PARAM,
  * on each file/subdirectory.
  * If any one of these calls returns 0, current recursive_action() returns 0.
  *
- * If !ACTION_DEPTHFIRST, dirAction is called before recurse.
+ * If ACTION_DEPTH_PRE, dirAction is called before recurse.
  * Return value of 0 (FALSE) is an error: prevents recursion,
  * the warning is printed (unless ACTION_QUIET) and recursive_action() returns 0.
  * Return value of 2 (SKIP) prevents recursion, instead recursive_action()
  * returns 1 (TRUE, no error).
  *
- * If ACTION_DEPTHFIRST, dirAction is called after recurse.
+ * If ACTION_DEPTH_POST, dirAction is called after recurse.
  * If it returns 0, the warning is printed and recursive_action() returns 0.
  *
  * ACTION_FOLLOWLINKS mainly controls handling of links to dirs.
@@ -104,7 +104,8 @@ static int recursive_action1(recursive_state_t *state)
 		return state->dirAction(state, state->fileName, &statbuf);
 	}
 
-	if (!(state->flags & ACTION_DEPTHFIRST)) {
+	if (state->flags & ACTION_DEPTH_PRE) {
+		state->state = ACTION_DEPTH_PRE;
 		status = state->dirAction(state, state->fileName, &statbuf);
 		if (status == FALSE)
 			goto done_nak_warn;
@@ -162,7 +163,8 @@ static int recursive_action1(recursive_state_t *state)
 	state->dirfd = olddirfd;
 	closedir(dir);
 
-	if (state->flags & ACTION_DEPTHFIRST) {
+	if (state->flags & ACTION_DEPTH_POST) {
+		state->state = ACTION_DEPTH_POST;
 		if (!state->dirAction(state, state->fileName, &statbuf))
 			goto done_nak_warn;
 	}
@@ -190,12 +192,13 @@ int FAST_FUNC recursive_action(const char *fileName,
 	 * and in every file/dirAction().
 	 */
 	recursive_state_t state;
-	state.flags = flags;
+	state.flags = flags | ((flags & (ACTION_DEPTH_PRE|ACTION_DEPTH_POST)) ? 0 : ACTION_DEPTH_PRE);
 	state.depth = 0;
 	state.userData = userData;
 	state.fileName = xstrdup(fileName);
 	state.baseName = state.fileName;
 	state.dirfd = xopen(".", O_RDONLY|O_DIRECTORY);
+	state.state = 0;
 	state.fileAction = fileAction ? fileAction : true_action;
 	state.dirAction  =  dirAction ?  dirAction : true_action;
 
diff --git a/libbb/remove_file.c b/libbb/remove_file.c
index 1505e6218..8721a1ea0 100644
--- a/libbb/remove_file.c
+++ b/libbb/remove_file.c
@@ -10,97 +10,52 @@
 
 /* Used from NOFORK applets. Must not allocate anything */
 
-int FAST_FUNC remove_file(const char *path, int flags)
+static int FAST_FUNC fileAction(struct recursive_state *state,
+		const char *fileName,
+		struct stat *statbuf)
 {
-	struct stat path_stat;
-
-	if (lstat(path, &path_stat) < 0) {
-		if (errno != ENOENT) {
-			bb_perror_msg("can't stat '%s'", path);
-			return -1;
-		}
-		if (!(flags & FILEUTILS_FORCE)) {
-			bb_perror_msg("can't remove '%s'", path);
-			return -1;
-		}
-		return 0;
-	}
-
-	if (S_ISDIR(path_stat.st_mode)) {
-		DIR *dp;
-		struct dirent *d;
-		int status = 0;
+	int flags = *((int*)state->userData);
+	int isdir = S_ISDIR(statbuf->st_mode);
 
-		if (!(flags & FILEUTILS_RECUR)) {
-			bb_error_msg("'%s' is a directory", path);
-			return -1;
+	if (!isdir || (state->state & ACTION_DEPTH_PRE)) {
+		if (isdir && !(flags & FILEUTILS_RECUR)) {
+			bb_error_msg("'%s' is a directory", fileName);
+			return FALSE;
 		}
 
-		if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && isatty(0))
+		if ((!(flags & FILEUTILS_FORCE)
+		     && faccessat(state->dirfd, state->baseName, W_OK, 0) < 0
+		     && !S_ISLNK(statbuf->st_mode)
+		     && isatty(0))
 		 || (flags & FILEUTILS_INTERACTIVE)
 		) {
-			fprintf(stderr, "%s: descend into directory '%s'? ",
-					applet_name, path);
-			if (!bb_ask_y_confirmation())
-				return 0;
-		}
-
-		dp = opendir(path);
-		if (dp == NULL) {
-			return -1;
-		}
-
-		while ((d = readdir(dp)) != NULL) {
-			char *new_path;
-
-			new_path = concat_subpath_file(path, d->d_name);
-			if (new_path == NULL)
-				continue;
-			if (remove_file(new_path, flags) < 0)
-				status = -1;
-			free(new_path);
-		}
-		closedir(dp);
-
-		if (flags & FILEUTILS_INTERACTIVE) {
-			fprintf(stderr, "%s: remove directory '%s'? ",
-					applet_name, path);
+			fprintf(stderr, "%s: %s '%s'? ", isdir ? "remove" : "descend into directory", applet_name, state->fileName);
 			if (!bb_ask_y_confirmation())
-				return status;
-		}
-
-		if (status == 0 && rmdir(path) < 0) {
-			bb_perror_msg("can't remove '%s'", path);
-			return -1;
-		}
-
-		if (flags & FILEUTILS_VERBOSE) {
-			printf("removed directory: '%s'\n", path);
+				return isdir ? SKIP : TRUE;
 		}
 
-		return status;
+		if (isdir)
+			return TRUE;
 	}
 
-	/* !ISDIR */
-	if ((!(flags & FILEUTILS_FORCE)
-	     && access(path, W_OK) < 0
-	     && !S_ISLNK(path_stat.st_mode)
-	     && isatty(0))
-	 || (flags & FILEUTILS_INTERACTIVE)
-	) {
-		fprintf(stderr, "%s: remove '%s'? ", applet_name, path);
-		if (!bb_ask_y_confirmation())
-			return 0;
-	}
-
-	if (unlink(path) < 0) {
-		bb_perror_msg("can't remove '%s'", path);
-		return -1;
+	// FIXME isdir && status == 0
+	if (unlinkat(state->dirfd, state->baseName, isdir ? AT_REMOVEDIR : 0) < 0) {
+		bb_perror_msg("can't remove '%s'", fileName);
+		return FALSE;
 	}
 
 	if (flags & FILEUTILS_VERBOSE) {
-		printf("removed '%s'\n", path);
+		printf("removed %s'%s'\n", isdir ? "directory: " : "", fileName);
 	}
 
-	return 0;
+	return TRUE;
+}
+
+int FAST_FUNC remove_file(const char *path, int flags)
+{
+	int ret = recursive_action(path,
+		ACTION_QUIET|ACTION_DEPTH_PRE|ACTION_DEPTH_POST | ((flags & FILEUTILS_RECUR) ? ACTION_RECURSE : 0),
+		fileAction, fileAction, &flags
+	);
+	return ret == FALSE ? -1 : 0;
 }
-- 
2.53.0
0003-archival-tar-use-dirfd-of-recursive_action.patch (text/x-patch, 1.2 KB)
From c025accc9ee777a38eb6d217c63bad29a94261f7 Mon Sep 17 00:00:00 2001
From: Sertonix <[email protected]>
Date: Tue, 9 Dec 2025 14:30:54 +0100
Subject: [PATCH 3/7] archival/tar: use dirfd of recursive_action

function                                             old     new   delta
writeFileToTarball                                   497     542     +45
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/0 up/down: 45/0)               Total: 45 bytes
---
 archival/tar.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/archival/tar.c b/archival/tar.c
index 0a100eb31..b0a737e0d 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -526,8 +526,9 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
 	/* Is this a regular file? */
 	if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
 		/* open the file we want to archive, and make sure all is well */
-		inputFileFd = open_or_warn(fileName, O_RDONLY);
+		inputFileFd = openat(state->dirfd, state->baseName, O_RDONLY);
 		if (inputFileFd < 0) {
+			bb_perror_msg("can't open '%s'", fileName);
 			return FALSE; /* make recursive_action() return FALSE */
 		}
 	}
-- 
2.53.0
0004-chmod-use-dirfd-from-recursive_action.patch (text/x-patch, 1.4 KB)
From be4dd0684790259e438d2e9a6777de3268c29085 Mon Sep 17 00:00:00 2001
From: Sertonix <[email protected]>
Date: Tue, 9 Dec 2025 14:39:48 +0100
Subject: [PATCH 4/7] chmod: use dirfd from recursive_action

function                                             old     new   delta
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/0 up/down: 0/0)                 Total: 0 bytes
---
 coreutils/chmod.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/coreutils/chmod.c b/coreutils/chmod.c
index 5832cc51b..5714ae595 100644
--- a/coreutils/chmod.c
+++ b/coreutils/chmod.c
@@ -75,7 +75,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state,
 	/* match coreutils behavior */
 	if (state->depth == 0) {
 		/* statbuf holds lstat result, but we need stat (follow link) */
-		if (stat(fileName, statbuf))
+		if (fstatat(state->dirfd, state->baseName, statbuf, 0))
 			goto err;
 	} else { /* depth > 0: skip links */
 		if (S_ISLNK(statbuf->st_mode))
@@ -86,7 +86,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state,
 	if (newmode == (mode_t)-1)
 		bb_error_msg_and_die("invalid mode '%s'", (char *)state->userData);
 
-	if (chmod(fileName, newmode) == 0) {
+	if (fchmodat(state->dirfd, state->baseName, newmode, AT_SYMLINK_NOFOLLOW) == 0) {
 		if (OPT_VERBOSE
 		 || (OPT_CHANGED
 		     && (statbuf->st_mode & 07777) != (newmode & 07777))
-- 
2.53.0
0005-chown-use-dirfd-from-recursive_action.patch (text/x-patch, 2.1 KB)
From 2904e23a9b286cc85cb4a2d9f4e33c0025b2fae9 Mon Sep 17 00:00:00 2001
From: Sertonix <[email protected]>
Date: Tue, 9 Dec 2025 14:39:57 +0100
Subject: [PATCH 5/7] chown: use dirfd from recursive_action

function                                             old     new   delta
chown_main                                           220     215      -5
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-5)               Total: -5 bytes
---
 coreutils/chown.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/coreutils/chown.c b/coreutils/chown.c
index c32812b8a..11ee4693e 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -94,14 +94,12 @@ static const char chown_longopts[] ALIGN1 =
 	;
 #endif
 
-typedef int (*chown_fptr)(const char *, uid_t, gid_t);
-
 struct param_t {
 	struct bb_uidgid_t ugid;
-	chown_fptr chown_func;
+	int chown_flag;
 };
 
-static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
+static int FAST_FUNC fileAction(struct recursive_state *state,
 		const char *fileName, struct stat *statbuf)
 {
 #define param  (*(struct param_t*)state->userData)
@@ -109,7 +107,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
 	uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid;
 	gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid;
 
-	if (param.chown_func(fileName, u, g) == 0) {
+	if (fchownat(state->dirfd, state->baseName, u, g, param.chown_flag) == 0) {
 		if (OPT_VERBOSE
 		 || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
 		) {
@@ -139,12 +137,12 @@ int chown_main(int argc UNUSED_PARAM, char **argv)
 	argv += optind;
 
 	/* This matches coreutils behavior (almost - see below) */
-	param.chown_func = chown;
+	param.chown_flag = 0;
 	if (OPT_NODEREF
 	/* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
 	IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
 	) {
-		param.chown_func = lchown;
+		param.chown_flag = AT_SYMLINK_NOFOLLOW;
 	}
 
 	flags = ACTION_DEPTH_POST; /* match coreutils order */
-- 
2.53.0
0006-run_parts-use-dirfd-from-recursive_action.patch (text/x-patch, 1.1 KB)
From 814aa7b1e6b20bd87913deedf45b14f9c2fe821f Mon Sep 17 00:00:00 2001
From: Sertonix <[email protected]>
Date: Tue, 9 Dec 2025 14:42:26 +0100
Subject: [PATCH 6/7] run_parts: use dirfd from recursive_action

function                                             old     new   delta
act                                                  218     226      +8
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/0 up/down: 8/0)                 Total: 8 bytes
---
 debianutils/run_parts.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c
index 780df3e96..cff7a0390 100644
--- a/debianutils/run_parts.c
+++ b/debianutils/run_parts.c
@@ -151,7 +151,7 @@ static int FAST_FUNC act(struct recursive_state *state,
 	if (state->depth == 1
 	 && (  !(statbuf->st_mode & (S_IFREG | S_IFLNK))
 	    || invalid_name(file)
-	    || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0))
+	    || (!(option_mask32 & OPT_l) && faccessat(state->dirfd, state->baseName, W_OK, 0) != 0))
 	) {
 		return SKIP;
 	}
-- 
2.53.0
0007-recursive_action-remove-fileName-argument-from-fileA.patch (text/x-patch, 27.4 KB)
From 84bfea20c29964c9697ce2f9ef4c5f471f9dfe44 Mon Sep 17 00:00:00 2001
From: Sertonix <[email protected]>
Date: Tue, 9 Dec 2025 17:24:43 +0100
Subject: [PATCH 7/7] recursive_action: remove fileName argument from
 fileAction/dirAction

function                                             old     new   delta
skip_dir                                             176     180      +4
uuidcache_check_device                               112     114      +2
fileAction                                           337     338      +1
dir_act                                              275     276      +1
add_to_prg_cache_if_socket                           276     277      +1
add_to_dirlist                                        77      78      +1
act                                                  226     221      -5
writeFileToTarball                                   542     528     -14
recursive_action1                                    737     721     -16
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 6/3 up/down: 10/-35)            Total: -25 bytes
---
 archival/tar.c                     | 15 +++++++--------
 coreutils/chmod.c                  |  5 ++---
 coreutils/chown.c                  |  6 +++---
 debianutils/run_parts.c            |  6 +++---
 editors/diff.c                     |  8 +++-----
 findutils/find.c                   |  7 +++----
 findutils/grep.c                   | 13 ++++++-------
 include/libbb.h                    |  8 ++++----
 libbb/recursive_action.c           | 15 +++++++--------
 libbb/remove_file.c                |  7 +++----
 modutils/depmod.c                  |  9 ++++-----
 modutils/modprobe-small.c          |  3 +--
 modutils/modprobe.c                |  5 ++---
 networking/netstat.c               |  8 +++-----
 selinux/chcon.c                    | 19 +++++++++----------
 selinux/setfiles.c                 |  5 ++---
 util-linux/lspci.c                 |  5 ++---
 util-linux/lsusb.c                 |  9 ++++-----
 util-linux/mdev.c                  |  8 +++-----
 util-linux/volume_id/get_devname.c |  7 +++----
 20 files changed, 74 insertions(+), 94 deletions(-)

diff --git a/archival/tar.c b/archival/tar.c
index b0a737e0d..8d6e0bbcf 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -465,17 +465,16 @@ static int exclude_file(const llist_t *excluded_files, const char *file)
 # endif
 
 static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
-		const char *fileName,
 		struct stat *statbuf)
 {
 	struct TarBallInfo *tbInfo = (struct TarBallInfo *) state->userData;
 	const char *header_name;
 	int inputFileFd = -1;
 
-	DBG("writeFileToTarball('%s')", fileName);
+	DBG("writeFileToTarball('%s')", state->fileName);
 
 	/* Strip leading '/' and such (must be before memorizing hardlink's name) */
-	header_name = skip_unsafe_prefix(fileName);
+	header_name = skip_unsafe_prefix(state->fileName);
 
 	if (header_name[0] == '\0')
 		return TRUE;
@@ -485,7 +484,7 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
 
 	/* It is against the rules to archive a socket */
 	if (S_ISSOCK(statbuf->st_mode)) {
-		bb_error_msg("%s: socket ignored", fileName);
+		bb_error_msg("%s: socket ignored", state->fileName);
 		return TRUE;
 	}
 
@@ -512,7 +511,7 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
 	if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev
 	 && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino
 	) {
-		bb_error_msg("%s: file is the archive; skipping", fileName);
+		bb_error_msg("%s: file is the archive; skipping", state->fileName);
 		return TRUE;
 	}
 
@@ -528,13 +527,13 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
 		/* open the file we want to archive, and make sure all is well */
 		inputFileFd = openat(state->dirfd, state->baseName, O_RDONLY);
 		if (inputFileFd < 0) {
-			bb_perror_msg("can't open '%s'", fileName);
+			bb_perror_msg("can't open '%s'", state->fileName);
 			return FALSE; /* make recursive_action() return FALSE */
 		}
 	}
 
 	/* Add an entry to the tarball */
-	if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
+	if (writeTarHeader(tbInfo, header_name, state->fileName, statbuf) == FALSE) {
 		return FALSE; /* make recursive_action() return FALSE */
 	}
 
@@ -551,7 +550,7 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
 		////off_t readSize;
 		////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
 		////if (readSize != statbuf->st_size && readSize >= 0) {
-		////	bb_error_msg_and_die("short read from %s, aborting", fileName);
+		////	bb_error_msg_and_die("short read from %s, aborting", state->fileName);
 		////}
 
 		/* Check that file did not grow in between? */
diff --git a/coreutils/chmod.c b/coreutils/chmod.c
index 5714ae595..c2a4961f2 100644
--- a/coreutils/chmod.c
+++ b/coreutils/chmod.c
@@ -67,7 +67,6 @@
  */
 
 static int FAST_FUNC fileAction(struct recursive_state *state,
-		const char *fileName,
 		struct stat *statbuf)
 {
 	mode_t newmode;
@@ -92,14 +91,14 @@ static int FAST_FUNC fileAction(struct recursive_state *state,
 		     && (statbuf->st_mode & 07777) != (newmode & 07777))
 		) {
 			char modestr[12];
-			printf("mode of '%s' changed to %04o (%s)\n", fileName,
+			printf("mode of '%s' changed to %04o (%s)\n", state->fileName,
 				newmode & 07777, bb_mode_string(modestr, newmode)+1);
 		}
 		return TRUE;
 	}
  err:
 	if (!OPT_QUIET)
-		bb_simple_perror_msg(fileName);
+		bb_simple_perror_msg(state->fileName);
 	return FALSE;
 }
 
diff --git a/coreutils/chown.c b/coreutils/chown.c
index 11ee4693e..720364383 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -100,7 +100,7 @@ struct param_t {
 };
 
 static int FAST_FUNC fileAction(struct recursive_state *state,
-		const char *fileName, struct stat *statbuf)
+		struct stat *statbuf)
 {
 #define param  (*(struct param_t*)state->userData)
 #define opt option_mask32
@@ -112,12 +112,12 @@ static int FAST_FUNC fileAction(struct recursive_state *state,
 		 || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
 		) {
 			printf("changed ownership of '%s' to %u:%u\n",
-					fileName, (unsigned)u, (unsigned)g);
+					state->fileName, (unsigned)u, (unsigned)g);
 		}
 		return TRUE;
 	}
 	if (!OPT_QUIET)
-		bb_simple_perror_msg(fileName);
+		bb_simple_perror_msg(state->fileName);
 	return FALSE;
 #undef opt
 #undef param
diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c
index cff7a0390..edb06e050 100644
--- a/debianutils/run_parts.c
+++ b/debianutils/run_parts.c
@@ -143,21 +143,21 @@ static int bb_alphasort(const void *p1, const void *p2)
 }
 
 static int FAST_FUNC act(struct recursive_state *state,
-		const char *file, struct stat *statbuf)
+		struct stat *statbuf)
 {
 	if (state->depth == 0)
 		return TRUE;
 
 	if (state->depth == 1
 	 && (  !(statbuf->st_mode & (S_IFREG | S_IFLNK))
-	    || invalid_name(file)
+	    || invalid_name(state->fileName)
 	    || (!(option_mask32 & OPT_l) && faccessat(state->dirfd, state->baseName, W_OK, 0) != 0))
 	) {
 		return SKIP;
 	}
 
 	names = xrealloc_vector(names, 4, cur);
-	names[cur++] = xstrdup(file);
+	names[cur++] = xstrdup(state->fileName);
 	/*names[cur] = NULL; - xrealloc_vector did it */
 
 	return TRUE;
diff --git a/editors/diff.c b/editors/diff.c
index 6f4ed9712..862e132b8 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -804,11 +804,10 @@ struct dlist {
 
 /* This function adds a filename to dl, the directory listing. */
 static int FAST_FUNC add_to_dirlist(struct recursive_state *state,
-		const char *filename,
 		struct stat *sb UNUSED_PARAM)
 {
 	struct dlist *const l = state->userData;
-	const char *file = filename + l->len;
+	const char *file = state->fileName + l->len;
 	while (*file == '/')
 		file++;
 	l->dl = xrealloc_vector(l->dl, 6, l->e);
@@ -821,11 +820,10 @@ static int FAST_FUNC add_to_dirlist(struct recursive_state *state,
  * to the list and prevents recursive_action from recursing into it.
  */
 static int FAST_FUNC skip_dir(struct recursive_state *state,
-		const char *filename,
 		struct stat *sb)
 {
 	if (!(option_mask32 & FLAG(r)) && state->depth) {
-		add_to_dirlist(state, filename, sb);
+		add_to_dirlist(state, sb);
 		return SKIP;
 	}
 	if (!(option_mask32 & FLAG(N))) {
@@ -834,7 +832,7 @@ static int FAST_FUNC skip_dir(struct recursive_state *state,
 		 * Testcase: diff -r /tmp /
 		 * (it would recurse deep into /proc without this code) */
 		struct dlist *const l = state->userData;
-		filename += l->len;
+		char *filename = state->fileName + l->len;
 		if (filename[0]) {
 			struct stat osb;
 			char *othername = concat_path_file(G.other_dir, filename);
diff --git a/findutils/find.c b/findutils/find.c
index 582bc83bb..443b02849 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -1011,7 +1011,6 @@ ACTF(links)
 
 static int FAST_FUNC fileAction(
 		struct recursive_state *state IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
-		const char *fileName,
 		struct stat *statbuf)
 {
 	int r;
@@ -1024,7 +1023,7 @@ static int FAST_FUNC fileAction(
 			if (G.xdev_dev[i] == statbuf->st_dev)
 				goto found;
 		}
-		//bb_error_msg("'%s': not same fs", fileName);
+		//bb_error_msg("'%s': not same fs", state->fileName);
 		same_fs = 0;
  found: ;
 	}
@@ -1040,10 +1039,10 @@ static int FAST_FUNC fileAction(
 		return SKIP; /* stop recursing */
 #endif
 
-	r = exec_actions(G.actions, fileName, statbuf);
+	r = exec_actions(G.actions, state->fileName, statbuf);
 	/* Had no explicit -print[0] or -exec? then print */
 	if ((r & TRUE) && G.need_print)
-		puts(fileName);
+		puts(state->fileName);
 
 #if ENABLE_FEATURE_FIND_MAXDEPTH
 	if (S_ISDIR(statbuf->st_mode)) {
diff --git a/findutils/grep.c b/findutils/grep.c
index 0a29be3a8..ca97e5f12 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -654,8 +654,7 @@ static void load_pattern_list(llist_t **lst, char *pattern)
 		llist_add_to(lst, new_grep_list_data(p, 0));
 }
 
-static int FAST_FUNC file_action_grep(struct recursive_state *state UNUSED_PARAM,
-		const char *filename,
+static int FAST_FUNC file_action_grep(struct recursive_state *state,
 		struct stat *statbuf)
 {
 	FILE *file;
@@ -666,23 +665,23 @@ static int FAST_FUNC file_action_grep(struct recursive_state *state UNUSED_PARAM
 	 * example will return the raw directory contents). */
 	if (S_ISLNK(statbuf->st_mode)) {
 		struct stat sb;
-		if (stat(filename, &sb) != 0) {
+		if (stat(state->fileName, &sb) != 0) {
 			if (!SUPPRESS_ERR_MSGS)
-				bb_simple_perror_msg(filename);
+				bb_simple_perror_msg(state->fileName);
 			return 0;
 		}
 		if (S_ISDIR(sb.st_mode))
 			return 1;
 	}
 
-	file = fopen_for_read(filename);
+	file = fopen_for_read(state->fileName);
 	if (file == NULL) {
 		if (!SUPPRESS_ERR_MSGS)
-			bb_simple_perror_msg(filename);
+			bb_simple_perror_msg(state->fileName);
 		open_errors = 1;
 		return 0;
 	}
-	cur_file = filename;
+	cur_file = state->fileName;
 	*(int*)state->userData |= grep_file(file);
 	fclose(file);
 	return 1;
diff --git a/include/libbb.h b/include/libbb.h
index 801fe101f..dd7246195 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -544,12 +544,12 @@ typedef struct recursive_state {
 	void *userData;
 	char *fileName, *baseName;
 	int dirfd, state;
-	int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
-	int FAST_FUNC  (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf);
+	int FAST_FUNC (*fileAction)(struct recursive_state *state, struct stat* statbuf);
+	int FAST_FUNC  (*dirAction)(struct recursive_state *state, struct stat* statbuf);
 } recursive_state_t;
 int recursive_action(const char *fileName, unsigned flags,
-	int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
-	int FAST_FUNC  (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
+	int FAST_FUNC (*fileAction)(struct recursive_state *state, struct stat* statbuf),
+	int FAST_FUNC  (*dirAction)(struct recursive_state *state, struct stat* statbuf),
 	void *userData
 ) FAST_FUNC;
 
diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c
index 48182ded2..f4034121b 100644
--- a/libbb/recursive_action.c
+++ b/libbb/recursive_action.c
@@ -20,7 +20,6 @@
  */
 
 static int FAST_FUNC true_action(struct recursive_state *state UNUSED_PARAM,
-		const char *fileName UNUSED_PARAM,
 		struct stat *statbuf UNUSED_PARAM)
 {
 	return TRUE;
@@ -84,7 +83,7 @@ static int recursive_action1(recursive_state_t *state)
 		 && fstatat(state->dirfd, state->baseName, &statbuf, AT_SYMLINK_NOFOLLOW) == 0
 		) {
 			/* Dangling link */
-			return state->fileAction(state, state->fileName, &statbuf);
+			return state->fileAction(state, &statbuf);
 		}
 		goto done_nak_warn;
 	}
@@ -95,18 +94,18 @@ static int recursive_action1(recursive_state_t *state)
 	if ( /* (!(state->flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */
 	 !S_ISDIR(statbuf.st_mode)
 	) {
-		return state->fileAction(state, state->fileName, &statbuf);
+		return state->fileAction(state, &statbuf);
 	}
 
 	/* It's a directory (or a link to one, and followLinks is set) */
 
 	if (!(state->flags & ACTION_RECURSE)) {
-		return state->dirAction(state, state->fileName, &statbuf);
+		return state->dirAction(state, &statbuf);
 	}
 
 	if (state->flags & ACTION_DEPTH_PRE) {
 		state->state = ACTION_DEPTH_PRE;
-		status = state->dirAction(state, state->fileName, &statbuf);
+		status = state->dirAction(state, &statbuf);
 		if (status == FALSE)
 			goto done_nak_warn;
 		if (status == SKIP)
@@ -165,7 +164,7 @@ static int recursive_action1(recursive_state_t *state)
 
 	if (state->flags & ACTION_DEPTH_POST) {
 		state->state = ACTION_DEPTH_POST;
-		if (!state->dirAction(state, state->fileName, &statbuf))
+		if (!state->dirAction(state, &statbuf))
 			goto done_nak_warn;
 	}
 
@@ -181,8 +180,8 @@ static int recursive_action1(recursive_state_t *state)
 
 int FAST_FUNC recursive_action(const char *fileName,
 		unsigned flags,
-		int FAST_FUNC (*fileAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
-		int FAST_FUNC  (*dirAction)(struct recursive_state *state, const char *fileName, struct stat* statbuf),
+		int FAST_FUNC (*fileAction)(struct recursive_state *state, struct stat* statbuf),
+		int FAST_FUNC  (*dirAction)(struct recursive_state *state, struct stat* statbuf),
 		void *userData)
 {
 	int ret;
diff --git a/libbb/remove_file.c b/libbb/remove_file.c
index 8721a1ea0..8a8d3b939 100644
--- a/libbb/remove_file.c
+++ b/libbb/remove_file.c
@@ -11,7 +11,6 @@
 /* Used from NOFORK applets. Must not allocate anything */
 
 static int FAST_FUNC fileAction(struct recursive_state *state,
-		const char *fileName,
 		struct stat *statbuf)
 {
 	int flags = *((int*)state->userData);
@@ -19,7 +18,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state,
 
 	if (!isdir || (state->state & ACTION_DEPTH_PRE)) {
 		if (isdir && !(flags & FILEUTILS_RECUR)) {
-			bb_error_msg("'%s' is a directory", fileName);
+			bb_error_msg("'%s' is a directory", state->fileName);
 			return FALSE;
 		}
 
@@ -40,12 +39,12 @@ static int FAST_FUNC fileAction(struct recursive_state *state,
 
 	// FIXME isdir && status == 0
 	if (unlinkat(state->dirfd, state->baseName, isdir ? AT_REMOVEDIR : 0) < 0) {
-		bb_perror_msg("can't remove '%s'", fileName);
+		bb_perror_msg("can't remove '%s'", state->fileName);
 		return FALSE;
 	}
 
 	if (flags & FILEUTILS_VERBOSE) {
-		printf("removed %s'%s'\n", isdir ? "directory: " : "", fileName);
+		printf("removed %s'%s'\n", isdir ? "directory: " : "", state->fileName);
 	}
 
 	return TRUE;
diff --git a/modutils/depmod.c b/modutils/depmod.c
index bb42bbefe..4118dc215 100644
--- a/modutils/depmod.c
+++ b/modutils/depmod.c
@@ -33,7 +33,6 @@
  */
 
 static int FAST_FUNC parse_module(struct recursive_state *state,
-		const char *fname,
 		struct stat *sb UNUSED_PARAM)
 {
 	module_db *modules = state->userData;
@@ -43,13 +42,13 @@ static int FAST_FUNC parse_module(struct recursive_state *state,
 	/* Arbitrary. Was sb->st_size, but that breaks .gz etc */
 	size_t len = (64*1024*1024 - 4096);
 
-	if (strrstr(fname, ".ko") == NULL)
+	if (strrstr(state->fileName, ".ko") == NULL)
 		return TRUE;
 
-	image = xmalloc_open_zipped_read_close(fname, &len);
+	image = xmalloc_open_zipped_read_close(state->fileName, &len);
 
-	e = moddb_get_or_create(modules, bb_get_last_path_component_nostrip(fname));
-	e->name = xstrdup(fname + 2); /* skip "./" */
+	e = moddb_get_or_create(modules, bb_get_last_path_component_nostrip(state->fileName));
+	e->name = xstrdup(state->fileName + 2); /* skip "./" */
 
 	for (ptr = image; ptr < image + len - 10; ptr++) {
 		if (is_prefixed_with(ptr, "depends=")) {
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 7f584102d..dc759889f 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -379,7 +379,6 @@ static int parse_module(module_info *info, const char *pathname)
 }
 
 static FAST_FUNC int fileAction(struct recursive_state *state,
-		const char *pathname,
 		struct stat *sb UNUSED_PARAM)
 {
 	const char *modname_to_match = state->userData;
@@ -388,7 +387,7 @@ static FAST_FUNC int fileAction(struct recursive_state *state,
 	bool is_remove = (ENABLE_RMMOD && ONLY_APPLET)
 		|| ((ENABLE_RMMOD || ENABLE_MODPROBE) && (option_mask32 & OPT_r));
 
-	pathname += 2; /* skip "./" */
+	char *pathname = state->fileName + 2; /* skip "./" */
 	fname = bb_get_last_path_component_nostrip(pathname);
 	if (!strrstr(fname, ".ko")) {
 		dbg1_error_msg("'%s' is not a module", pathname);
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index f890abe53..f9cec8241 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -236,7 +236,6 @@ static void add_probe(const char *name)
 }
 
 static int FAST_FUNC config_file_action(struct recursive_state *state,
-		const char *filename,
 		struct stat *statbuf UNUSED_PARAM)
 {
 	char *tokens[3];
@@ -246,7 +245,7 @@ static int FAST_FUNC config_file_action(struct recursive_state *state,
 	const char *base;
 
 	/* Skip files that begin with a "." */
-	base = bb_basename(filename);
+	base = bb_basename(state->fileName);
 	if (base[0] == '.')
 		goto error;
 
@@ -268,7 +267,7 @@ static int FAST_FUNC config_file_action(struct recursive_state *state,
 			goto error;
 	}
 
-	p = config_open2(filename, fopen_for_read);
+	p = config_open2(state->fileName, fopen_for_read);
 	if (p == NULL) {
 		rc = FALSE;
 		goto error;
diff --git a/networking/netstat.c b/networking/netstat.c
index d7afa8fdd..47f332493 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -273,13 +273,12 @@ static long extract_socket_inode(const char *lname)
 }
 
 static int FAST_FUNC add_to_prg_cache_if_socket(struct recursive_state *state,
-		const char *fileName,
 		struct stat *statbuf UNUSED_PARAM)
 {
 	char *linkname;
 	long inode;
 
-	linkname = xmalloc_readlink(fileName);
+	linkname = xmalloc_readlink(state->fileName);
 	if (linkname != NULL) {
 		inode = extract_socket_inode(linkname);
 		free(linkname);
@@ -292,7 +291,6 @@ static int FAST_FUNC add_to_prg_cache_if_socket(struct recursive_state *state,
 }
 
 static int FAST_FUNC dir_act(struct recursive_state *state,
-		const char *fileName,
 		struct stat *statbuf UNUSED_PARAM)
 {
 	const char *pid;
@@ -304,11 +302,11 @@ static int FAST_FUNC dir_act(struct recursive_state *state,
 	if (state->depth == 0) /* "/proc" itself */
 		return TRUE; /* continue looking one level below /proc */
 
-	pid = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
+	pid = state->fileName + sizeof("/proc/")-1; /* point after "/proc/" */
 	if (!isdigit(pid[0])) /* skip /proc entries which aren't processes */
 		return SKIP;
 
-	len = snprintf(proc_pid_fname, sizeof(proc_pid_fname), "%s/cmdline", fileName);
+	len = snprintf(proc_pid_fname, sizeof(proc_pid_fname), "%s/cmdline", state->fileName);
 	n = open_read_close(proc_pid_fname, cmdline_buf, sizeof(cmdline_buf) - 1);
 	if (n < 0)
 		return FALSE;
diff --git a/selinux/chcon.c b/selinux/chcon.c
index e1778a36a..39b362847 100644
--- a/selinux/chcon.c
+++ b/selinux/chcon.c
@@ -63,7 +63,6 @@ static char *range = NULL;
 static char *specified_context = NULL;
 
 static int FAST_FUNC change_filedir_context(struct recursive_state *state UNUSED_PARAM,
-		const char *fname,
 		struct stat *stbuf UNUSED_PARAM)
 {
 	context_t context = NULL;
@@ -73,18 +72,18 @@ static int FAST_FUNC change_filedir_context(struct recursive_state *state UNUSED
 	int status = 0;
 
 	if (option_mask32 & OPT_NODEREFERENCE) {
-		status = lgetfilecon(fname, &file_context);
+		status = lgetfilecon(state->fileName, &file_context);
 	} else {
-		status = getfilecon(fname, &file_context);
+		status = getfilecon(state->fileName, &file_context);
 	}
 	if (status < 0 && errno != ENODATA) {
 		if ((option_mask32 & OPT_QUIET) == 0)
-			bb_error_msg("can't obtain security context: %s", fname);
+			bb_error_msg("can't obtain security context: %s", state->fileName);
 		goto skip;
 	}
 
 	if (file_context == NULL && specified_context == NULL) {
-		bb_error_msg("can't apply partial context to unlabeled file %s", fname);
+		bb_error_msg("can't apply partial context to unlabeled file %s", state->fileName);
 		goto skip;
 	}
 
@@ -113,25 +112,25 @@ static int FAST_FUNC change_filedir_context(struct recursive_state *state UNUSED
 		int fail;
 
 		if (option_mask32 & OPT_NODEREFERENCE) {
-			fail = lsetfilecon(fname, context_string);
+			fail = lsetfilecon(state->fileName, context_string);
 		} else {
-			fail = setfilecon(fname, context_string);
+			fail = setfilecon(state->fileName, context_string);
 		}
 		if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) {
 			printf(!fail
 				? "context of %s changed to %s\n"
 				: "can't change context of %s to %s\n",
-				fname, context_string);
+				state->fileName, context_string);
 		}
 		if (!fail) {
 			rc = TRUE;
 		} else if ((option_mask32 & OPT_QUIET) == 0) {
 			bb_error_msg("can't change context of %s to %s",
-					fname, context_string);
+					state->fileName, context_string);
 		}
 	} else {
 		if (option_mask32 & OPT_VERBOSE) {
-			printf("context of %s retained as %s\n", fname, context_string);
+			printf("context of %s retained as %s\n", state->fileName, context_string);
 		}
 		rc = TRUE;
 	}
diff --git a/selinux/setfiles.c b/selinux/setfiles.c
index 70e68a666..a43e06bfd 100644
--- a/selinux/setfiles.c
+++ b/selinux/setfiles.c
@@ -463,8 +463,7 @@ static int restore(const char *file)
  * This function is called by recursive_action on each file during
  * the directory traversal.
  */
-static int FAST_FUNC apply_spec(struct recursive_state *state UNUSED_PARAM,
-		const char *file,
+static int FAST_FUNC apply_spec(struct recursive_state *state,
 		struct stat *sb)
 {
 	if (!follow_mounts) {
@@ -473,7 +472,7 @@ static int FAST_FUNC apply_spec(struct recursive_state *state UNUSED_PARAM,
 			return SKIP;
 		}
 	}
-	errors |= restore(file);
+	errors |= restore(state->file);
 	if (abort_on_error && errors)
 		return FALSE;
 	return TRUE;
diff --git a/util-linux/lspci.c b/util-linux/lspci.c
index 1a43f71e2..92ff1c628 100644
--- a/util-linux/lspci.c
+++ b/util-linux/lspci.c
@@ -37,8 +37,7 @@ enum {
 /*
  * PCI_SLOT_NAME PCI_CLASS: PCI_VID:PCI_DID [PCI_SUBSYS_VID:PCI_SUBSYS_DID] [DRIVER]
  */
-static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
-		const char *fileName,
+static int FAST_FUNC fileAction(struct recursive_state *state,
 		struct stat *statbuf UNUSED_PARAM)
 {
 	parser_t *parser;
@@ -47,7 +46,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
 	int pci_class = 0, pci_vid = 0, pci_did = 0;
 	int pci_subsys_vid = 0, pci_subsys_did = 0;
 
-	char *uevent_filename = concat_path_file(fileName, "uevent");
+	char *uevent_filename = concat_path_file(state->fileName, "uevent");
 	parser = config_open2(uevent_filename, fopen_for_read);
 	free(uevent_filename);
 
diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c
index f7d0de32d..f5823c17d 100644
--- a/util-linux/lsusb.c
+++ b/util-linux/lsusb.c
@@ -42,15 +42,14 @@ static char * FAST_FUNC add_sysfs_prop(const char *dir, const char *suffix,
 	return trim(buf);
 }
 
-static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
-		const char *fileName,
+static int FAST_FUNC fileAction(struct recursive_state *state,
 		struct stat *statbuf UNUSED_PARAM)
 {
 	parser_t *parser;
 	char *tokens[4];
 	char *busnum = NULL, *devnum = NULL;
 	int product_vid = 0, product_did = 0;
-	char *uevent_filename = concat_path_file(fileName, "uevent");
+	char *uevent_filename = concat_path_file(state->fileName, "uevent");
 
 	parser = config_open2(uevent_filename, fopen_for_read);
 	free(uevent_filename);
@@ -81,10 +80,10 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
 	if (busnum) {
 		char name[256], *p;
 
-		p = add_sysfs_prop(fileName, "/manufacturer", name, sizeof(name) - 1);
+		p = add_sysfs_prop(state->fileName, "/manufacturer", name, sizeof(name) - 1);
 		if (p != name)
 			p = stpcpy(p, " ");
-		add_sysfs_prop(fileName, "/product", p, name + sizeof(name) - p);
+		add_sysfs_prop(state->fileName, "/product", p, name + sizeof(name) - p);
 
 		printf("Bus %s Device %s: ID %04x:%04x %s\n", busnum, devnum,
 		       product_vid, product_did, name);
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index e98d46743..230f6b133 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -845,19 +845,18 @@ static ssize_t readlink2(char *buf, size_t bufsize)
  * We act only on "/sys/.../dev" (pseudo)file
  */
 static int FAST_FUNC fileAction(struct recursive_state *state,
-		const char *fileName,
 		struct stat *statbuf UNUSED_PARAM)
 {
-	size_t len = strlen(fileName) - 4; /* can't underflow */
+	size_t len = strlen(state->fileName) - 4; /* can't underflow */
 	char *path = state->userData;	/* char array[PATH_MAX + SCRATCH_SIZE] */
 	char subsys[PATH_MAX];
 	int res;
 
 	/* Is it a ".../dev" file? (len check is for paranoid reasons) */
-	if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
+	if (strcmp(state->fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
 		return FALSE; /* not .../dev */
 
-	strcpy(path, fileName);
+	strcpy(path, state->fileName);
 	path[len] = '\0';
 
 	/* Read ".../subsystem" symlink in the same directory where ".../dev" is */
@@ -887,7 +886,6 @@ static int FAST_FUNC fileAction(struct recursive_state *state,
 
 /* Directory callback for /sys/ traversal */
 static int FAST_FUNC dirAction(struct recursive_state *state,
-		const char *fileName UNUSED_PARAM,
 		struct stat *statbuf UNUSED_PARAM)
 {
 	return (state->depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c
index 52bb8e03a..7d5ae7f02 100644
--- a/util-linux/volume_id/get_devname.c
+++ b/util-linux/volume_id/get_devname.c
@@ -103,14 +103,13 @@ uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uu
  * add a cache entry for this device.
  * If device node does not exist, it will be temporarily created. */
 static int FAST_FUNC
-uuidcache_check_device(struct recursive_state *state UNUSED_PARAM,
-		const char *device,
+uuidcache_check_device(struct recursive_state *state,
 		struct stat *statbuf)
 {
 	/* note: this check rejects links to devices, among other nodes */
 	if (!S_ISBLK(statbuf->st_mode)
 #if ENABLE_FEATURE_VOLUMEID_UBIFS
-	 && !(S_ISCHR(statbuf->st_mode) && is_prefixed_with(bb_basename(device), "ubi"))
+	 && !(S_ISCHR(statbuf->st_mode) && is_prefixed_with(bb_basename(state->fileName), "ubi"))
 #endif
 	)
 		return TRUE;
@@ -123,7 +122,7 @@ uuidcache_check_device(struct recursive_state *state UNUSED_PARAM,
 	if (major(statbuf->st_rdev) == 2)
 		return TRUE;
 
-	add_to_uuid_cache(device);
+	add_to_uuid_cache(state->fileName);
 
 	return TRUE;
 }
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.