traditional Windows directory paths are limited to 247 characters

... not 259 characters, which is the limit on file paths.
This commit is contained in:
Matthew Flatt 2020-11-10 14:37:00 -07:00
parent 23710d5862
commit 7975bdf25d
4 changed files with 19 additions and 10 deletions

View File

@ -217,9 +217,10 @@ sequences that are otherwise ill-formed as Windows paths:
]
Outside of Racket, except for @litchar{\\?\} paths, pathnames are
typically limited to 259 characters. Racket internally converts
pathnames to @litchar{\\?\} form as needed to avoid this
limit; in that case, the path is first simplified syntactically (in the
typically limited to 259 characters when used as a file path and 247 characters when
used as a directory path. Racket internally converts
pathnames longer than 247 characters to @litchar{\\?\} form to avoid the
limits; in that case, the path is first simplified syntactically (in the
sense of @racket[simplify-path]). The operating system cannot access files through
@litchar{\\?\} paths that are longer than 32,000 characters or
so.

View File

@ -1717,7 +1717,12 @@ static char *do_expand_filename(Scheme_Object *o, char* filename, int ilen, cons
ilen = strlen(filename);
}
if (kind == SCHEME_WINDOWS_PATH_KIND) {
if (ilen > ((fullpath > 1) ? fullpath : 259)) {
/* While the limit can be increased in Windows 10, the maximum
length of a file path without resroting to \\?\ is 259
characters, and the maximum length of a directory path is 247
characters. Guard against the lower limit. */
# define LONGEST_NON_BSBS_PATH 247
if (ilen > ((fullpath > 1) ? fullpath : LONGEST_NON_BSBS_PATH)) {
if (!check_dos_slashslash_qm(filename, ilen, NULL, NULL, NULL)) {
/* Convert to \\?\ to avoid length limit. Collapse any
".." and "." indicators first syntactically (which is not ideal,
@ -1730,7 +1735,7 @@ static char *do_expand_filename(Scheme_Object *o, char* filename, int ilen, cons
filename = SCHEME_PATH_VAL(p);
ilen = SCHEME_PATH_LEN(p);
if (ilen > ((fullpath > 1) ? fullpath : 259)) { /* still too long after simplification? */
if (ilen > ((fullpath > 1) ? fullpath : LONGEST_NON_BSBS_PATH)) { /* still too long after simplification? */
filename = convert_to_backslashbackslash_qm(filename, &l, filename, &a, 0);
filename[l] = 0;
}

View File

@ -27574,11 +27574,12 @@
(protect-path-element (bytes->immutable-bytes s_0) 'windows)
'windows)
(host-> s_0))))
(define LONGEST-NON-BSBS-PATH 247)
(define handle-long-path
(lambda (who_0 p_0)
(if (eq? (system-type) 'windows)
(let ((bstr_0 (|#%app| path-bytes p_0)))
(if (if (> (unsafe-bytes-length bstr_0) 259)
(if (if (> (unsafe-bytes-length bstr_0) 247)
(not
(if (fx= (unsafe-bytes-ref bstr_0 0) 92)
(if (fx= (unsafe-bytes-ref bstr_0 1) 92)
@ -27590,7 +27591,7 @@
#f)
(let ((simple-p_0 (simplify-path-syntactically who_0 p_0 #f)))
(let ((simple-bstr_0 (|#%app| path-bytes simple-p_0)))
(if (<= (unsafe-bytes-length simple-bstr_0) 260)
(if (<= (unsafe-bytes-length simple-bstr_0) 247)
simple-p_0
(if (fx= (unsafe-bytes-ref simple-bstr_0 0) 92)
(path1.1

View File

@ -45,16 +45,18 @@
'windows)
(host-> s)))
;; If we end up with a Windows path that is longer than 260 bytes,
;; If we end up with a Windows path that is longer than 247 bytes
;; (traditional file path limit: 259; directory path limit: 247)
;; then add "\\?\" or "\\?\UNC" to the front. The path needs to be
;; abbsolute and otherwise fully normalized so that just adding to the
;; front is possible.
(define LONGEST-NON-BSBS-PATH 247)
(define (handle-long-path who p)
(cond
[(eq? (system-type) 'windows)
(define bstr (path-bytes p))
(cond
[(and ((bytes-length bstr) . > . 259)
[(and ((bytes-length bstr) . > . LONGEST-NON-BSBS-PATH)
(not (and (fx= (bytes-ref bstr 0) (char->integer #\\))
(fx= (bytes-ref bstr 1) (char->integer #\\))
(fx= (bytes-ref bstr 2) (char->integer #\?))
@ -63,7 +65,7 @@
(define simple-p (simplify-path-syntactically who p #f))
(define simple-bstr (path-bytes simple-p))
(cond
[((bytes-length simple-bstr) . <= . 260)
[((bytes-length simple-bstr) . <= . LONGEST-NON-BSBS-PATH)
;; Simplified path is short enough
simple-p]
[(fx= (bytes-ref simple-bstr 0) (char->integer #\\))