diff options
author | Ron Yorston <rmy@pobox.com> | 2018-02-26 20:39:23 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2018-02-26 20:39:23 +0000 |
commit | 650f67507f2718dec0d4282afea619cfe7a53305 (patch) | |
tree | 82e523133f961e8337f42765307b63dc737f2580 | |
parent | fcb5c968bef6c4fd234e000aaeaa160ac1d16f11 (diff) | |
download | busybox-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.c | 15 | ||||
-rw-r--r-- | debianutils/which.c | 2 | ||||
-rw-r--r-- | include/mingw.h | 3 | ||||
-rw-r--r-- | libbb/executable.c | 2 | ||||
-rw-r--r-- | shell/ash.c | 6 | ||||
-rw-r--r-- | win32/mingw.c | 111 | ||||
-rw-r--r-- | win32/process.c | 20 |
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 | ||
477 | int has_exe_suffix(const char *p); | 477 | int has_exe_suffix(const char *p); |
478 | int has_bat_suffix(const char *p); | 478 | int has_bat_suffix(const char *p); |
479 | char *file_is_win32_executable(const char *p); | 479 | char *add_win32_extension(const char *p); |
480 | int has_exec_format(const char *name); | ||
480 | 481 | ||
481 | int err_win_to_posix(DWORD winerr); | 482 | int 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 | */ |
1088 | char *file_is_win32_executable(const char *p) | 1042 | char *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 | */ | ||
1073 | int 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 |
1113 | DIR *mingw_opendir(const char *path) | 1122 | DIR *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 | |||
192 | spawnveq(int mode, const char *path, char *const *argv, char *const *env) | 192 | spawnveq(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 |