aboutsummaryrefslogtreecommitdiff
path: root/win32
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 /win32
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.
Diffstat (limited to 'win32')
-rw-r--r--win32/mingw.c111
-rw-r--r--win32/process.c20
2 files changed, 77 insertions, 54 deletions
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