aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2026-02-27 13:57:40 +0000
committerRon Yorston <rmy@pobox.com>2026-02-27 14:27:40 +0000
commited525e2db10081f095d2fccc1b02830654470401 (patch)
tree4467394a255c596be9c3573d8d0b5e4f3eb3dcea
parent05219c1f97be1dc7a3f825a5a658a137d90b03a2 (diff)
downloadbusybox-w32-long_paths.tar.gz
busybox-w32-long_paths.tar.bz2
busybox-w32-long_paths.zip
win32: add (some) long path supportlong_paths
Use the longPathAware manifest entry to handle path lengths above 260. This requires the LongPathsEnabled registry key to be set at run-time. Modify (some) WIN32 API calls to use the wide-character variant. These features are controlled by the build-time configuration option FEATURE_LONG_PATHS, which is not enabled by default.
-rw-r--r--Config.in9
-rw-r--r--libbb/appletlib.c3
-rw-r--r--win32/dirent.c74
-rw-r--r--win32/mingw.c25
-rw-r--r--win32/resources/resources.rc12
5 files changed, 109 insertions, 14 deletions
diff --git a/Config.in b/Config.in
index 51de85284..39d9b0589 100644
--- a/Config.in
+++ b/Config.in
@@ -494,6 +494,15 @@ config FEATURE_PNG_ALL
494 494
495endchoice 495endchoice
496 496
497config FEATURE_LONG_PATHS
498 bool "Support for long paths"
499 default n
500 depends on FEATURE_RESOURCES
501 help
502 Allow paths longer than 260 characters. This is only supported
503 on Windows 10+. The registry key LongPathsEnabled must be set
504 to 1.
505
497config FEATURE_EURO 506config FEATURE_EURO
498 bool "Support the euro currency symbol" 507 bool "Support the euro currency symbol"
499 default y 508 default y
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 481cc278e..12de2b610 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -928,6 +928,9 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
928# if ENABLE_FEATURE_UTF8_MANIFEST 928# if ENABLE_FEATURE_UTF8_MANIFEST
929 full_write1_str("; Unicode"); 929 full_write1_str("; Unicode");
930# endif 930# endif
931# if ENABLE_FEATURE_LONG_PATHS
932 full_write1_str("; long paths");
933# endif
931 full_write1_str(")\n\n"); 934 full_write1_str(")\n\n");
932# else 935# else
933 full_write1_str(" multi-call binary.\n"); /* reuse */ 936 full_write1_str(" multi-call binary.\n"); /* reuse */
diff --git a/win32/dirent.c b/win32/dirent.c
index f0e8deae2..872f44fbc 100644
--- a/win32/dirent.c
+++ b/win32/dirent.c
@@ -8,26 +8,44 @@ struct DIR {
8 int got_dotdot; 8 int got_dotdot;
9}; 9};
10 10
11static inline unsigned char get_dtype(DWORD attr, DWORD reserved0)
12{
13 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) &&
14 (reserved0 == IO_REPARSE_TAG_SYMLINK ||
15 reserved0 == IO_REPARSE_TAG_MOUNT_POINT ||
16 reserved0 == IO_REPARSE_TAG_APPEXECLINK))
17 return DT_LNK;
18 if (attr & FILE_ATTRIBUTE_DIRECTORY)
19 return DT_DIR;
20 return DT_REG;
21}
22
23#if !ENABLE_FEATURE_LONG_PATHS
11static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata) 24static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
12{ 25{
13 /* copy file name from WIN32_FIND_DATA to dirent */ 26 /* copy file name from WIN32_FIND_DATA to dirent */
14 strcpy(ent->d_name, fdata->cFileName); 27 strcpy(ent->d_name, fdata->cFileName);
15 28 ent->d_type = get_dtype(fdata->dwFileAttributes, fdata->dwReserved0);
16 if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
17 (fdata->dwReserved0 == IO_REPARSE_TAG_SYMLINK ||
18 fdata->dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT ||
19 fdata->dwReserved0 == IO_REPARSE_TAG_APPEXECLINK))
20 ent->d_type = DT_LNK;
21 else if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
22 ent->d_type = DT_DIR;
23 else
24 ent->d_type = DT_REG;
25} 29}
30#else
31static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
32{
33 /* copy file name from WIN32_FIND_DATAW to dirent */
34 WideCharToMultiByte(CP_ACP, 0, fdata->cFileName, -1,
35 ent->d_name, PATH_MAX, NULL, NULL);
36 ent->d_type = get_dtype(fdata->dwFileAttributes, fdata->dwReserved0);
37}
38#endif
26 39
27DIR *opendir(const char *name) 40DIR *opendir(const char *name)
28{ 41{
42#if !ENABLE_FEATURE_LONG_PATHS
29 char pattern[MAX_PATH]; 43 char pattern[MAX_PATH];
30 WIN32_FIND_DATAA fdata; 44 WIN32_FIND_DATAA fdata;
45#else
46 wchar_t wpattern[32768];
47 WIN32_FIND_DATAW fdata;
48#endif
31 HANDLE h; 49 HANDLE h;
32 int len; 50 int len;
33 DIR *dir; 51 DIR *dir;
@@ -37,6 +55,8 @@ DIR *opendir(const char *name)
37 errno = EINVAL; 55 errno = EINVAL;
38 return NULL; 56 return NULL;
39 } 57 }
58
59#if !ENABLE_FEATURE_LONG_PATHS
40 /* check that the pattern won't be too long for FindFirstFileA */ 60 /* check that the pattern won't be too long for FindFirstFileA */
41 len = strlen(name); 61 len = strlen(name);
42 if (len + 2 >= MAX_PATH) { 62 if (len + 2 >= MAX_PATH) {
@@ -59,6 +79,30 @@ DIR *opendir(const char *name)
59 errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(); 79 errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix();
60 return NULL; 80 return NULL;
61 } 81 }
82#else /* ENABLE_FEATURE_LONG_PATHS */
83 /* Convert to wide string. CP_ACP is CP_UTF8 when the UTF-8 manifest
84 * is active, else the system ANSI code page. */
85 len = MultiByteToWideChar(CP_ACP, 0, name, -1, wpattern, 32760);
86 if (len == 0) {
87 errno = EINVAL;
88 return NULL;
89 }
90 len--; /* exclude null terminator from length */
91
92 /* append optional '\' and wildcard '*' */
93 if (len && wpattern[len - 1] != L'\\' && wpattern[len - 1] != L'/')
94 wpattern[len++] = L'\\';
95 wpattern[len++] = L'*';
96 wpattern[len] = 0;
97
98 /* open find handle using wide API */
99 h = FindFirstFileW(wpattern, &fdata);
100 if (h == INVALID_HANDLE_VALUE) {
101 DWORD err = GetLastError();
102 errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix();
103 return NULL;
104 }
105#endif
62 106
63 /* initialize DIR structure and copy first dir entry */ 107 /* initialize DIR structure and copy first dir entry */
64 dir = xzalloc(sizeof(DIR)); 108 dir = xzalloc(sizeof(DIR));
@@ -80,8 +124,14 @@ struct dirent *readdir(DIR *dir)
80 /* if first entry, dirent has already been set up by opendir */ 124 /* if first entry, dirent has already been set up by opendir */
81 if (dir->not_first) { 125 if (dir->not_first) {
82 /* get next entry and convert from WIN32_FIND_DATA to dirent */ 126 /* get next entry and convert from WIN32_FIND_DATA to dirent */
127#if !ENABLE_FEATURE_LONG_PATHS
83 WIN32_FIND_DATAA fdata; 128 WIN32_FIND_DATAA fdata;
84 if (FindNextFileA(dir->dd_handle, &fdata)) { 129 if (FindNextFileA(dir->dd_handle, &fdata))
130#else
131 WIN32_FIND_DATAW fdata;
132 if (FindNextFileW(dir->dd_handle, &fdata))
133#endif
134 {
85 finddata2dirent(&dir->dd_dir, &fdata); 135 finddata2dirent(&dir->dd_dir, &fdata);
86 } else if (!dir->got_dot) { 136 } else if (!dir->got_dot) {
87 strcpy(dir->dd_dir.d_name, "."); 137 strcpy(dir->dd_dir.d_name, ".");
diff --git a/win32/mingw.c b/win32/mingw.c
index 122337d19..0c28d87fe 100644
--- a/win32/mingw.c
+++ b/win32/mingw.c
@@ -396,6 +396,9 @@ static inline mode_t file_attr_to_st_mode(DWORD attr)
396 396
397static int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata) 397static int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
398{ 398{
399#if ENABLE_FEATURE_LONG_PATHS
400 wchar_t wpath[32768];
401#endif
399 char *want_dir; 402 char *want_dir;
400 int dev = get_dev_type(fname); 403 int dev = get_dev_type(fname);
401 404
@@ -412,7 +415,18 @@ static int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
412 } 415 }
413 416
414 want_dir = last_char_is_dir_sep(fname); 417 want_dir = last_char_is_dir_sep(fname);
415 if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata)) { 418
419#if !ENABLE_FEATURE_LONG_PATHS
420 if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata))
421#else
422 /* Convert to wide string for long path support. CP_ACP is CP_UTF8
423 * when the UTF-8 manifest is active, else the system ANSI code page. */
424 if (MultiByteToWideChar(CP_ACP, 0, fname, -1, wpath, 32768) == 0)
425 return EINVAL;
426
427 if (GetFileAttributesExW(wpath, GetFileExInfoStandard, fdata))
428#endif
429 {
416 if (!(fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && want_dir) 430 if (!(fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && want_dir)
417 return ENOTDIR; 431 return ENOTDIR;
418 fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_DEVICE; 432 fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_DEVICE;
@@ -421,9 +435,16 @@ static int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
421 435
422 if (GetLastError() == ERROR_SHARING_VIOLATION) { 436 if (GetLastError() == ERROR_SHARING_VIOLATION) {
423 HANDLE hnd; 437 HANDLE hnd;
438#if !ENABLE_FEATURE_LONG_PATHS
424 WIN32_FIND_DATA fd; 439 WIN32_FIND_DATA fd;
425 440
426 if ((hnd=FindFirstFile(fname, &fd)) != INVALID_HANDLE_VALUE) { 441 if ((hnd=FindFirstFile(fname, &fd)) != INVALID_HANDLE_VALUE)
442#else
443 WIN32_FIND_DATAW fd;
444
445 if ((hnd=FindFirstFileW(wpath, &fd)) != INVALID_HANDLE_VALUE)
446#endif
447 {
427 fdata->dwFileAttributes = 448 fdata->dwFileAttributes =
428 fd.dwFileAttributes & ~FILE_ATTRIBUTE_DEVICE; 449 fd.dwFileAttributes & ~FILE_ATTRIBUTE_DEVICE;
429 fdata->ftCreationTime = fd.ftCreationTime; 450 fdata->ftCreationTime = fd.ftCreationTime;
diff --git a/win32/resources/resources.rc b/win32/resources/resources.rc
index b76f1af23..50642831c 100644
--- a/win32/resources/resources.rc
+++ b/win32/resources/resources.rc
@@ -52,6 +52,9 @@ BEGIN
52 "<asmv3:application>" 52 "<asmv3:application>"
53 "<asmv3:windowsSettings>" 53 "<asmv3:windowsSettings>"
54 "<activeCodePage xmlns=""http://schemas.microsoft.com/SMI/2019/WindowsSettings"">UTF-8</activeCodePage>" 54 "<activeCodePage xmlns=""http://schemas.microsoft.com/SMI/2019/WindowsSettings"">UTF-8</activeCodePage>"
55# if ENABLE_FEATURE_LONG_PATHS
56 "<longPathAware xmlns=""http://schemas.microsoft.com/SMI/2016/WindowsSettings"">true</longPathAware>"
57# endif
55 "</asmv3:windowsSettings>" 58 "</asmv3:windowsSettings>"
56 "</asmv3:application>" 59 "</asmv3:application>"
57 "<trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3"">" 60 "<trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3"">"
@@ -78,7 +81,16 @@ END
781 24 811 24
79BEGIN 82BEGIN
80 "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>" 83 "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>"
84# if !ENABLE_FEATURE_LONG_PATHS
81 "<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">" 85 "<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">"
86# else
87 "<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" xmlns:asmv3=""urn:schemas-microsoft-com:asm.v3"" manifestVersion=""1.0"">"
88 "<asmv3:application>"
89 "<asmv3:windowsSettings>"
90 "<longPathAware xmlns=""http://schemas.microsoft.com/SMI/2016/WindowsSettings"">true</longPathAware>"
91 "</asmv3:windowsSettings>"
92 "</asmv3:application>"
93# endif
82 "<trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3"">" 94 "<trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3"">"
83 "<security>" 95 "<security>"
84 "<requestedPrivileges>" 96 "<requestedPrivileges>"