Skip to content

Commit 883da00

Browse files
committed
improve long path for windows
1 parent 2e369b1 commit 883da00

1 file changed

Lines changed: 41 additions & 13 deletions

File tree

src/tbox/platform/windows/prefix.h

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,23 +109,51 @@ static __tb_inline__ tb_wchar_t const* tb_path_absolute_w(tb_char_t const* path,
109109
tb_size_t size = tb_strlen(path);
110110
if (size >= MAX_PATH)
111111
{
112-
tb_char_t* e = data + size - 1;
113-
if (e + 5 < data + sizeof(data))
112+
/* choose the long-path prefix and the number of leading chars to drop
113+
*
114+
* - already "\\?\" prefixed => keep as-is
115+
* - UNC path "\\server\share\.." => "\\?\UNC\server\share\.."
116+
* - drive/other absolute path => "\\?\.."
117+
*
118+
* @see https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
119+
*/
120+
tb_char_t const* prefix = tb_null;
121+
tb_size_t plen = 0;
122+
tb_size_t skip = 0;
123+
if (data[0] == '\\' && data[1] == '\\' && data[2] == '?' && data[3] == '\\')
124+
; // already a long-path form, do nothing
125+
else if (data[0] == '\\' && data[1] == '\\')
114126
{
115-
e[5] = '\0';
116-
while (e >= data)
127+
// UNC path: replace the leading "\\" with "\\?\UNC\"
128+
prefix = "\\\\?\\UNC\\";
129+
plen = 8;
130+
skip = 2;
131+
}
132+
else
133+
{
134+
prefix = "\\\\?\\";
135+
plen = 4;
136+
}
137+
138+
if (prefix)
139+
{
140+
tb_size_t new_size = plen + size - skip;
141+
if (new_size < sizeof(data))
117142
{
118-
e[4] = *e;
119-
e--;
143+
/* shift the kept content (with the null terminator) right, then prepend the prefix
144+
*
145+
* copy from high to low to avoid overlap corruption, e.g. for a drive path:
146+
* "C:\..\0" => "\\?\C:\..\0"
147+
*/
148+
tb_char_t* d = data + new_size;
149+
tb_char_t const* s = data + size;
150+
while (s >= data + skip) *d-- = *s--;
151+
tb_memcpy(data, prefix, plen);
152+
path = data;
153+
size = new_size;
120154
}
121-
data[0] = '\\';
122-
data[1] = '\\';
123-
data[2] = '?';
124-
data[3] = '\\';
125-
path = data;
126-
size += 4; // add "\\?\" prefix length
155+
else return tb_null;
127156
}
128-
else return tb_null;
129157
}
130158

131159
// use tb_atow_n with known length to avoid tb_atow's internal strlen

0 commit comments

Comments
 (0)