aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2018-02-26 20:39:23 +0000
committerRon Yorston <rmy@pobox.com>2018-02-26 20:39:23 +0000
commit650f67507f2718dec0d4282afea619cfe7a53305 (patch)
tree82e523133f961e8337f42765307b63dc737f2580
parentfcb5c968bef6c4fd234e000aaeaa160ac1d16f11 (diff)
downloadbusybox-w32-650f67507f2718dec0d4282afea619cfe7a53305.tar.gz
busybox-w32-650f67507f2718dec0d4282afea619cfe7a53305.tar.bz2
busybox-w32-650f67507f2718dec0d4282afea619cfe7a53305.zip
win32: move detection of file formats to stat(2)
Move the code to detect shell scripts and binary executables from mingw_access to a separate function, has_exec_format. Call this function in do_lstat to decide whether to set the executable bits in the file mode. This will slow down stat but has a couple of advantages: - shell scripts are highlighted in ls output - the test applet can use stat(2) to detect executable files The new function is used to handle another corner case in spawnveq: binary executables without the usual .exe extension are only run by spawnve if the file name ends with '.'. Two minor changes: - file_is_win32_executable has been renamed add_win32_extension to clarify what it does - a call to file_is_executable has been removed from find_command in ash as it resulted in unhelpful error messages.
-rw-r--r--coreutils/test.c15
-rw-r--r--debianutils/which.c2
-rw-r--r--include/mingw.h3
-rw-r--r--libbb/executable.c2
-rw-r--r--shell/ash.c6
-rw-r--r--win32/mingw.c111
-rw-r--r--win32/process.c20
7 files changed, 82 insertions, 77 deletions
diff --git a/coreutils/test.c b/coreutils/test.c
index ed708c6d3..a8286525a 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -637,21 +637,6 @@ static int filstat(char *nm, enum token mode)
637 return 0; 637 return 0;
638 } 638 }
639 639
640#if ENABLE_PLATFORM_MINGW32
641 if (mode == FILEX) {
642 char *p;
643
644 if (file_is_executable(nm)) {
645 return 1;
646 }
647 else if ((p=file_is_win32_executable(nm))) {
648 free(p);
649 return 1;
650 }
651 return 0;
652 }
653#endif
654
655 if (stat(nm, &s) != 0) 640 if (stat(nm, &s) != 0)
656 return 0; 641 return 0;
657 if (mode == FILEXIST) 642 if (mode == FILEXIST)
diff --git a/debianutils/which.c b/debianutils/which.c
index 13f922615..9060a5b47 100644
--- a/debianutils/which.c
+++ b/debianutils/which.c
@@ -71,7 +71,7 @@ int which_main(int argc UNUSED_PARAM, char **argv)
71 puts(*argv); 71 puts(*argv);
72 } 72 }
73#if ENABLE_PLATFORM_MINGW32 73#if ENABLE_PLATFORM_MINGW32
74 else if ((p=file_is_win32_executable(*argv)) != NULL) { 74 else if ((p=add_win32_extension(*argv)) != NULL) {
75 missing = 0; 75 missing = 0;
76 puts(p); 76 puts(p);
77 free(p); 77 free(p);
diff --git a/include/mingw.h b/include/mingw.h
index 09c140d31..c116e551a 100644
--- a/include/mingw.h
+++ b/include/mingw.h
@@ -476,7 +476,8 @@ void init_winsock(void);
476 476
477int has_exe_suffix(const char *p); 477int has_exe_suffix(const char *p);
478int has_bat_suffix(const char *p); 478int has_bat_suffix(const char *p);
479char *file_is_win32_executable(const char *p); 479char *add_win32_extension(const char *p);
480int has_exec_format(const char *name);
480 481
481int err_win_to_posix(DWORD winerr); 482int err_win_to_posix(DWORD winerr);
482 483
diff --git a/libbb/executable.c b/libbb/executable.c
index 8e2f99732..76b10f790 100644
--- a/libbb/executable.c
+++ b/libbb/executable.c
@@ -77,7 +77,7 @@ char* FAST_FUNC find_executable(const char *filename, char **PATHp)
77 return p; 77 return p;
78 } 78 }
79#if ENABLE_PLATFORM_MINGW32 79#if ENABLE_PLATFORM_MINGW32
80 else if ((w=file_is_win32_executable(p))) { 80 else if ((w=add_win32_extension(p))) {
81 *PATHp = n; 81 *PATHp = n;
82 free(p); 82 free(p);
83 return w; 83 return w;
diff --git a/shell/ash.c b/shell/ash.c
index 30f3b558b..7dec5dfc7 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -13654,7 +13654,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13654 if (act & DO_ABS) { 13654 if (act & DO_ABS) {
13655 while (stat(name, &statb) < 0 13655 while (stat(name, &statb) < 0
13656#if ENABLE_PLATFORM_MINGW32 13656#if ENABLE_PLATFORM_MINGW32
13657 && (fullname=file_is_win32_executable(name)) == NULL 13657 && (fullname=add_win32_extension(name)) == NULL
13658#endif 13658#endif
13659 ) { 13659 ) {
13660#ifdef SYSV 13660#ifdef SYSV
@@ -13798,10 +13798,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13798 e = errno; 13798 e = errno;
13799 goto loop; 13799 goto loop;
13800 } 13800 }
13801 if (!file_is_executable(fullname)) {
13802 e = ENOEXEC;
13803 goto loop;
13804 }
13805 } 13801 }
13806 } 13802 }
13807#else 13803#else
diff --git a/win32/mingw.c b/win32/mingw.c
index bd4d9b34a..713778ff1 100644
--- a/win32/mingw.c
+++ b/win32/mingw.c
@@ -300,7 +300,8 @@ static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf)
300 buf->st_gid = DEFAULT_GID; 300 buf->st_gid = DEFAULT_GID;
301 buf->st_nlink = 1; 301 buf->st_nlink = 1;
302 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); 302 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
303 if (has_exe_suffix(file_name)) 303 if (S_ISREG(buf->st_mode) &&
304 (has_exe_suffix(file_name) || has_exec_format(file_name)))
304 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH; 305 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH;
305 buf->st_size = fdata.nFileSizeLow | 306 buf->st_size = fdata.nFileSizeLow |
306 (((off64_t)fdata.nFileSizeHigh)<<32); 307 (((off64_t)fdata.nFileSizeHigh)<<32);
@@ -982,9 +983,6 @@ int mingw_access(const char *name, int mode)
982{ 983{
983 int ret; 984 int ret;
984 struct stat s; 985 struct stat s;
985 int fd, n, sig;
986 unsigned int offset;
987 unsigned char buf[1024];
988 986
989 /* Windows can only handle test for existence, read or write */ 987 /* Windows can only handle test for existence, read or write */
990 if (mode == F_OK || (mode & ~X_OK)) { 988 if (mode == F_OK || (mode & ~X_OK)) {
@@ -994,52 +992,8 @@ int mingw_access(const char *name, int mode)
994 } 992 }
995 } 993 }
996 994
997 if (!mingw_stat(name, &s) && S_ISREG(s.st_mode)) { 995 if (!mingw_stat(name, &s) && S_ISREG(s.st_mode) && (s.st_mode&S_IXUSR)) {
998 996 return 0;
999 /* stat marks files as executable according to their suffix */
1000 if ((s.st_mode&S_IEXEC)) {
1001 return 0;
1002 }
1003
1004 fd = open(name, O_RDONLY);
1005 if (fd < 0)
1006 return -1;
1007 n = read(fd, buf, sizeof(buf)-1);
1008 close(fd);
1009 if (n < 4) /* at least '#!/x' and not error */
1010 return -1;
1011
1012 /* shell script */
1013 if (buf[0] == '#' && buf[1] == '!') {
1014 return 0;
1015 }
1016
1017 /*
1018 * Poke about in file to see if it's a PE binary. I've just copied
1019 * the magic from the file command.
1020 */
1021 if (buf[0] == 'M' && buf[1] == 'Z') {
1022 offset = (buf[0x19] << 8) + buf[0x18];
1023 if (offset > 0x3f) {
1024 offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) +
1025 (buf[0x3d] << 8) + buf[0x3c];
1026 if (offset < sizeof(buf)-100) {
1027 if (memcmp(buf+offset, "PE\0\0", 4) == 0) {
1028 sig = (buf[offset+25] << 8) + buf[offset+24];
1029 if (sig == 0x10b || sig == 0x20b) {
1030 sig = (buf[offset+23] << 8) + buf[offset+22];
1031 if ((sig & 0x2000) != 0) {
1032 /* DLL */
1033 return -1;
1034 }
1035 sig = buf[offset+92];
1036 return !(sig == 1 || sig == 2 ||
1037 sig == 3 || sig == 7);
1038 }
1039 }
1040 }
1041 }
1042 }
1043 } 997 }
1044 998
1045 return -1; 999 return -1;
@@ -1085,7 +1039,7 @@ int has_exe_suffix(const char *name)
1085 * 1039 *
1086 * if path already has a suffix don't even bother trying 1040 * if path already has a suffix don't even bother trying
1087 */ 1041 */
1088char *file_is_win32_executable(const char *p) 1042char *add_win32_extension(const char *p)
1089{ 1043{
1090 char *path; 1044 char *path;
1091 int i, len; 1045 int i, len;
@@ -1109,6 +1063,61 @@ char *file_is_win32_executable(const char *p)
1109 return NULL; 1063 return NULL;
1110} 1064}
1111 1065
1066/*
1067 * Examine a file's contents to determine if it can be executed. This
1068 * should be a last resort: in most cases it's much more efficient to
1069 * check the file extension.
1070 *
1071 * We look for two types of file: shell scripts and binary executables.
1072 */
1073int has_exec_format(const char *name)
1074{
1075 int fd, n, sig;
1076 unsigned int offset;
1077 unsigned char buf[1024];
1078
1079 fd = open(name, O_RDONLY);
1080 if (fd < 0)
1081 return 0;
1082 n = read(fd, buf, sizeof(buf)-1);
1083 close(fd);
1084 if (n < 4) /* at least '#!/x' and not error */
1085 return 0;
1086
1087 /* shell script */
1088 if (buf[0] == '#' && buf[1] == '!') {
1089 return 1;
1090 }
1091
1092 /*
1093 * Poke about in file to see if it's a PE binary. I've just copied
1094 * the magic from the file command.
1095 */
1096 if (buf[0] == 'M' && buf[1] == 'Z') {
1097 offset = (buf[0x19] << 8) + buf[0x18];
1098 if (offset > 0x3f) {
1099 offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) +
1100 (buf[0x3d] << 8) + buf[0x3c];
1101 if (offset < sizeof(buf)-100) {
1102 if (memcmp(buf+offset, "PE\0\0", 4) == 0) {
1103 sig = (buf[offset+25] << 8) + buf[offset+24];
1104 if (sig == 0x10b || sig == 0x20b) {
1105 sig = (buf[offset+23] << 8) + buf[offset+22];
1106 if ((sig & 0x2000) != 0) {
1107 /* DLL */
1108 return 0;
1109 }
1110 sig = buf[offset+92];
1111 return (sig == 1 || sig == 2 || sig == 3 || sig == 7);
1112 }
1113 }
1114 }
1115 }
1116 }
1117
1118 return 0;
1119}
1120
1112#undef opendir 1121#undef opendir
1113DIR *mingw_opendir(const char *path) 1122DIR *mingw_opendir(const char *path)
1114{ 1123{
diff --git a/win32/process.c b/win32/process.c
index da83d1c96..eda143e0e 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -192,6 +192,7 @@ static intptr_t
192spawnveq(int mode, const char *path, char *const *argv, char *const *env) 192spawnveq(int mode, const char *path, char *const *argv, char *const *env)
193{ 193{
194 char **new_argv; 194 char **new_argv;
195 char *new_path = NULL;
195 int i, argc = -1; 196 int i, argc = -1;
196 intptr_t ret; 197 intptr_t ret;
197 198
@@ -214,7 +215,7 @@ spawnveq(int mode, const char *path, char *const *argv, char *const *env)
214 p = strdup(new_argv[0]); 215 p = strdup(new_argv[0]);
215 } 216 }
216 else { 217 else {
217 p = file_is_win32_executable(new_argv[0]); 218 p = add_win32_extension(new_argv[0]);
218 } 219 }
219 220
220 if (p != NULL && has_bat_suffix(p)) { 221 if (p != NULL && has_bat_suffix(p)) {
@@ -231,12 +232,25 @@ spawnveq(int mode, const char *path, char *const *argv, char *const *env)
231 } 232 }
232 } 233 }
233 234
234 ret = spawnve(mode, path, new_argv, env); 235 /*
236 * Another special case: if a binary executable doesn't have an
237 * extension spawnve will only run it if the filename ends with a '.'.
238 */
239 if (!has_exe_suffix(path)) {
240 int len = strlen(path);
241
242 if (path[len-1] != '.' && has_exec_format(path)) {
243 new_path = xasprintf("%s.", path);
244 }
245 }
246
247 ret = spawnve(mode, new_path ? new_path : path, new_argv, env);
235 248
236 for (i = 0;i < argc;i++) 249 for (i = 0;i < argc;i++)
237 if (new_argv[i] != argv[i]) 250 if (new_argv[i] != argv[i])
238 free(new_argv[i]); 251 free(new_argv[i]);
239 free(new_argv); 252 free(new_argv);
253 free(new_path);
240 254
241 return ret; 255 return ret;
242} 256}
@@ -275,7 +289,7 @@ mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, char *con
275 memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc); 289 memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc);
276 290
277 if (file_is_executable(int_path) || 291 if (file_is_executable(int_path) ||
278 (fullpath=file_is_win32_executable(int_path)) != NULL) { 292 (fullpath=add_win32_extension(int_path)) != NULL) {
279 new_argv[0] = fullpath ? fullpath : int_path; 293 new_argv[0] = fullpath ? fullpath : int_path;
280 ret = spawnveq(mode, new_argv[0], new_argv, envp); 294 ret = spawnveq(mode, new_argv[0], new_argv, envp);
281 } else 295 } else