From 7975bdf25d1834ae9969dfb86712de0011edb880 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 10 Nov 2020 14:37:00 -0700 Subject: [PATCH] traditional Windows directory paths are limited to 247 characters ... not 259 characters, which is the limit on file paths. --- .../racket-doc/scribblings/reference/windows-paths.scrbl | 7 ++++--- racket/src/bc/src/file.c | 9 +++++++-- racket/src/cs/schemified/io.scm | 5 +++-- racket/src/io/file/host.rkt | 8 +++++--- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pkgs/racket-doc/scribblings/reference/windows-paths.scrbl b/pkgs/racket-doc/scribblings/reference/windows-paths.scrbl index 9e283c6a3e..c12149ad5f 100644 --- a/pkgs/racket-doc/scribblings/reference/windows-paths.scrbl +++ b/pkgs/racket-doc/scribblings/reference/windows-paths.scrbl @@ -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. diff --git a/racket/src/bc/src/file.c b/racket/src/bc/src/file.c index fbf0e5f55f..3c3f9f8ab5 100644 --- a/racket/src/bc/src/file.c +++ b/racket/src/bc/src/file.c @@ -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; } diff --git a/racket/src/cs/schemified/io.scm b/racket/src/cs/schemified/io.scm index 8cfca2498f..72496453ae 100644 --- a/racket/src/cs/schemified/io.scm +++ b/racket/src/cs/schemified/io.scm @@ -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 diff --git a/racket/src/io/file/host.rkt b/racket/src/io/file/host.rkt index acc794cb86..3f335b69cd 100644 --- a/racket/src/io/file/host.rkt +++ b/racket/src/io/file/host.rkt @@ -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 #\\))