From cbfa58d56c3ca59538bf23e30105ce27ed5ef948 Mon Sep 17 00:00:00 2001
From: Ron Yorston <rmy@pobox.com>
Date: Wed, 4 Sep 2024 12:47:03 +0100
Subject: ash: optimise running of scripts (2)

Commit 4b7b4a960 (ash: optimise running of scripts) avoided creation
of a process when running a script.  There's another case where we
can do the same:  if the script is being run from a FS_SHELLEXEC
shell.

- Check the necessary conditions for this to happen.

- Allocate two extra slots in the argv array for FS_SHELLEXEC.

- Set the index of the script file in the argv array.  Without this
  the test 'pidof this' failed because the command name hadn't been
  correctly set.

Adds 80-96 bytes.
---
 include/mingw.h   |  1 +
 libbb/appletlib.c |  7 +++++++
 shell/ash.c       | 26 ++++++++++++++++++--------
 3 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/include/mingw.h b/include/mingw.h
index ed7884e39..65940b40b 100644
--- a/include/mingw.h
+++ b/include/mingw.h
@@ -667,3 +667,4 @@ enum {
 	ADMIN_ENABLED = 2
 };
 int elevation_state(void);
+void set_interp(int i) FAST_FUNC;
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 97dfb3df8..1ff7fe6c8 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -394,6 +394,13 @@ bool re_execed;
 static int interp = 0;
 char bb_comm[COMM_LEN];
 char bb_command_line[128];
+
+# if ENABLE_FEATURE_SH_STANDALONE
+void FAST_FUNC set_interp(int i)
+{
+	interp = i;
+}
+# endif
 #endif
 
 
diff --git a/shell/ash.c b/shell/ash.c
index 2e4181b3f..d9a7bee83 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -9099,6 +9099,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
 #endif
 {
 #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
+	struct forkshell *fs = (struct forkshell *)sticky_mem_start;
 	interp_t interp;
 #endif
 #if ENABLE_FEATURE_SH_STANDALONE
@@ -9106,7 +9107,6 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
 # if ENABLE_PLATFORM_MINGW32
 		/* Treat all applets as NOEXEC, including the shell itself if
 		 * this is a FS_SHELLEXEC shell. */
-		struct forkshell *fs = (struct forkshell *)sticky_mem_start;
 		if (applet_main[applet_no] != ash_main ||
 				(fs && fs->fpid == FS_SHELLEXEC)) {
  run_noexec:
@@ -9152,21 +9152,28 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
 	add_win32_extension((char *)cmd);
 
 # if ENABLE_FEATURE_SH_STANDALONE
-	/* If nfpath is non-NULL, evalcommand() has determined this
-	 * command doesn't need a fork().  If the command is a script
-	 * with an interpreter which is an applet we can run it as if
-	 * it were a noexec applet. */
+	/* If nfpath is non-NULL evalcommand() has determined this
+	 * command doesn't need a fork().  Even it's NULL we can
+	 * possibly avoid a fork if this is a FS_SHELLEXEC shell. */
+	if (nfpath == NULL && (fs && fs->fpid == FS_SHELLEXEC))
+		nfpath = pathval();
+
+	/* If nfpath is non-NULL and the command is a script with an
+	 * interpreter which is an applet, we can run it as if it
+	 * were a noexec applet. */
 	if (nfpath && parse_interpreter(cmd, &interp)) {
 		applet_no = find_applet_by_name_for_sh(interp.name, nfpath);
 		if (applet_no >= 0) {
 			argv[0] = (char *)cmd;
-			/* evalcommand() has added two elements before argv */
+			/* evalcommand()/spawn_forkshell() add two elements before argv */
 			if (interp.opts) {
 				argv--;
 				argv[0] = (char *)interp.opts;
 			}
 			argv--;
 			cmd = argv[0] = (char *)interp.name;
+			/* Identify the index of the script file in argv */
+			set_interp(1 + (interp.opts != NULL));
 			goto run_noexec;
 		}
 	}
@@ -16842,7 +16849,8 @@ argv_size(struct datasize ds, char **p)
 			ds.funcstringsize += align_len(*p);
 			p++;
 		}
-		ds.funcblocksize += sizeof(char *);
+		// Allow two extra elements for tryexec().
+		ds.funcblocksize += 3 * sizeof(char *);
 	}
 	return ds;
 }
@@ -16856,6 +16864,8 @@ argv_copy(char **p)
 #endif
 
 	if (p) {
+		// Allow two extra elements for tryexec().
+		funcblock = (char *) funcblock + 2 * sizeof(char *);
 		while (*p) {
 			new = funcblock;
 			funcblock = (char *) funcblock + sizeof(char *);
@@ -16866,7 +16876,7 @@ argv_copy(char **p)
 		new = funcblock;
 		funcblock = (char *) funcblock + sizeof(char *);
 		*new = NULL;
-		return start;
+		return start + 2;
 	}
 	return NULL;
 }
-- 
cgit v1.2.3-55-g6feb