Skip to content

Commit 2b643e9

Browse files
committed
Auto merge of #89174 - ChrisDenton:automatic-verbatim-paths, r=dtolnay
Automatically convert paths to verbatim for filesystem operations that support it This allows using longer paths without the user needing to `canonicalize` or manually prefix paths. If the path is already verbatim then this has no effect. Fixes: #32689
2 parents 22f1ad7 + 37e4c84 commit 2b643e9

File tree

5 files changed

+203
-13
lines changed

5 files changed

+203
-13
lines changed

library/std/src/fs/tests.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1411,3 +1411,32 @@ fn symlink_hard_link() {
14111411
// "hard_link" should still appear as a symlink.
14121412
assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
14131413
}
1414+
1415+
/// Ensure `fs::create_dir` works on Windows with longer paths.
1416+
#[test]
1417+
#[cfg(windows)]
1418+
fn create_dir_long_paths() {
1419+
use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1420+
const PATH_LEN: usize = 247;
1421+
1422+
let tmpdir = tmpdir();
1423+
let mut path = tmpdir.path().to_path_buf();
1424+
path.push("a");
1425+
let mut path = path.into_os_string();
1426+
1427+
let utf16_len = path.encode_wide().count();
1428+
if utf16_len >= PATH_LEN {
1429+
// Skip the test in the unlikely event the local user has a long temp directory path.
1430+
// This should not affect CI.
1431+
return;
1432+
}
1433+
// Increase the length of the path.
1434+
path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1435+
1436+
// This should succeed.
1437+
fs::create_dir(&path).unwrap();
1438+
1439+
// This will fail if the path isn't converted to verbatim.
1440+
path.push("a");
1441+
fs::create_dir(&path).unwrap();
1442+
}

library/std/src/sys/windows/c.rs

+6
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,12 @@ extern "system" {
977977
cchCount2: c_int,
978978
bIgnoreCase: BOOL,
979979
) -> c_int;
980+
pub fn GetFullPathNameW(
981+
lpFileName: LPCWSTR,
982+
nBufferLength: DWORD,
983+
lpBuffer: LPWSTR,
984+
lpFilePart: *mut LPWSTR,
985+
) -> DWORD;
980986
}
981987

982988
#[link(name = "ws2_32")]

library/std/src/sys/windows/fs.rs

+14-13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::sys::time::SystemTime;
1414
use crate::sys::{c, cvt};
1515
use crate::sys_common::{AsInner, FromInner, IntoInner};
1616

17+
use super::path::maybe_verbatim;
1718
use super::to_u16s;
1819

1920
pub struct File {
@@ -281,7 +282,7 @@ impl OpenOptions {
281282

282283
impl File {
283284
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
284-
let path = to_u16s(path)?;
285+
let path = maybe_verbatim(path)?;
285286
let handle = unsafe {
286287
c::CreateFileW(
287288
path.as_ptr(),
@@ -706,7 +707,7 @@ impl DirBuilder {
706707
}
707708

708709
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
709-
let p = to_u16s(p)?;
710+
let p = maybe_verbatim(p)?;
710711
cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;
711712
Ok(())
712713
}
@@ -715,7 +716,7 @@ impl DirBuilder {
715716
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
716717
let root = p.to_path_buf();
717718
let star = p.join("*");
718-
let path = to_u16s(&star)?;
719+
let path = maybe_verbatim(&star)?;
719720

720721
unsafe {
721722
let mut wfd = mem::zeroed();
@@ -733,20 +734,20 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
733734
}
734735

735736
pub fn unlink(p: &Path) -> io::Result<()> {
736-
let p_u16s = to_u16s(p)?;
737+
let p_u16s = maybe_verbatim(p)?;
737738
cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
738739
Ok(())
739740
}
740741

741742
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
742-
let old = to_u16s(old)?;
743-
let new = to_u16s(new)?;
743+
let old = maybe_verbatim(old)?;
744+
let new = maybe_verbatim(new)?;
744745
cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) })?;
745746
Ok(())
746747
}
747748

748749
pub fn rmdir(p: &Path) -> io::Result<()> {
749-
let p = to_u16s(p)?;
750+
let p = maybe_verbatim(p)?;
750751
cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
751752
Ok(())
752753
}
@@ -794,7 +795,7 @@ pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
794795

795796
pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {
796797
let original = to_u16s(original)?;
797-
let link = to_u16s(link)?;
798+
let link = maybe_verbatim(link)?;
798799
let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
799800
// Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
800801
// Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
@@ -823,8 +824,8 @@ pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()>
823824

824825
#[cfg(not(target_vendor = "uwp"))]
825826
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
826-
let original = to_u16s(original)?;
827-
let link = to_u16s(link)?;
827+
let original = maybe_verbatim(original)?;
828+
let link = maybe_verbatim(link)?;
828829
cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
829830
Ok(())
830831
}
@@ -857,7 +858,7 @@ pub fn lstat(path: &Path) -> io::Result<FileAttr> {
857858
}
858859

859860
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
860-
let p = to_u16s(p)?;
861+
let p = maybe_verbatim(p)?;
861862
unsafe {
862863
cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
863864
Ok(())
@@ -900,8 +901,8 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
900901
}
901902
c::PROGRESS_CONTINUE
902903
}
903-
let pfrom = to_u16s(from)?;
904-
let pto = to_u16s(to)?;
904+
let pfrom = maybe_verbatim(from)?;
905+
let pto = maybe_verbatim(to)?;
905906
let mut size = 0i64;
906907
cvt(unsafe {
907908
c::CopyFileExW(

library/std/src/sys/windows/path.rs

+101
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
use super::{c, fill_utf16_buf, to_u16s};
12
use crate::ffi::OsStr;
3+
use crate::io;
24
use crate::mem;
5+
use crate::path::Path;
36
use crate::path::Prefix;
7+
use crate::ptr;
48

59
#[cfg(test)]
610
mod tests;
@@ -141,3 +145,100 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
141145
None => (path, OsStr::new("")),
142146
}
143147
}
148+
149+
/// Returns a UTF-16 encoded path capable of bypassing the legacy `MAX_PATH` limits.
150+
///
151+
/// This path may or may not have a verbatim prefix.
152+
pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
153+
// Normally the MAX_PATH is 260 UTF-16 code units (including the NULL).
154+
// However, for APIs such as CreateDirectory[1], the limit is 248.
155+
//
156+
// [1]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectorya#parameters
157+
const LEGACY_MAX_PATH: usize = 248;
158+
// UTF-16 encoded code points, used in parsing and building UTF-16 paths.
159+
// All of these are in the ASCII range so they can be cast directly to `u16`.
160+
const SEP: u16 = b'\\' as _;
161+
const ALT_SEP: u16 = b'/' as _;
162+
const QUERY: u16 = b'?' as _;
163+
const COLON: u16 = b':' as _;
164+
const DOT: u16 = b'.' as _;
165+
const U: u16 = b'U' as _;
166+
const N: u16 = b'N' as _;
167+
const C: u16 = b'C' as _;
168+
169+
// \\?\
170+
const VERBATIM_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP];
171+
// \??\
172+
const NT_PREFIX: &[u16] = &[SEP, QUERY, QUERY, SEP];
173+
// \\?\UNC\
174+
const UNC_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP, U, N, C, SEP];
175+
176+
let mut path = to_u16s(path)?;
177+
if path.starts_with(VERBATIM_PREFIX) || path.starts_with(NT_PREFIX) {
178+
// Early return for paths that are already verbatim.
179+
return Ok(path);
180+
} else if path.len() < LEGACY_MAX_PATH {
181+
// Early return if an absolute path is less < 260 UTF-16 code units.
182+
// This is an optimization to avoid calling `GetFullPathNameW` unnecessarily.
183+
match path.as_slice() {
184+
// Starts with `D:`, `D:\`, `D:/`, etc.
185+
// Does not match if the path starts with a `\` or `/`.
186+
[drive, COLON, 0] | [drive, COLON, SEP | ALT_SEP, ..]
187+
if *drive != SEP && *drive != ALT_SEP =>
188+
{
189+
return Ok(path);
190+
}
191+
// Starts with `\\`, `//`, etc
192+
[SEP | ALT_SEP, SEP | ALT_SEP, ..] => return Ok(path),
193+
_ => {}
194+
}
195+
}
196+
197+
// Firstly, get the absolute path using `GetFullPathNameW`.
198+
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
199+
let lpfilename = path.as_ptr();
200+
fill_utf16_buf(
201+
// SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
202+
// `lpfilename` is a pointer to a null terminated string that is not
203+
// invalidated until after `GetFullPathNameW` returns successfully.
204+
|buffer, size| unsafe {
205+
// While the docs for `GetFullPathNameW` have the standard note
206+
// about needing a `\\?\` path for a long lpfilename, this does not
207+
// appear to be true in practice.
208+
// See:
209+
// https://stackoverflow.com/questions/38036943/getfullpathnamew-and-long-windows-file-paths
210+
// https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
211+
c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut())
212+
},
213+
|mut absolute| {
214+
path.clear();
215+
216+
// Secondly, add the verbatim prefix. This is easier here because we know the
217+
// path is now absolute and fully normalized (e.g. `/` has been changed to `\`).
218+
let prefix = match absolute {
219+
// C:\ => \\?\C:\
220+
[_, COLON, SEP, ..] => VERBATIM_PREFIX,
221+
// \\.\ => \\?\
222+
[SEP, SEP, DOT, SEP, ..] => {
223+
absolute = &absolute[4..];
224+
VERBATIM_PREFIX
225+
}
226+
// Leave \\?\ and \??\ as-is.
227+
[SEP, SEP, QUERY, SEP, ..] | [SEP, QUERY, QUERY, SEP, ..] => &[],
228+
// \\ => \\?\UNC\
229+
[SEP, SEP, ..] => {
230+
absolute = &absolute[2..];
231+
UNC_PREFIX
232+
}
233+
// Anything else we leave alone.
234+
_ => &[],
235+
};
236+
237+
path.reserve_exact(prefix.len() + absolute.len() + 1);
238+
path.extend_from_slice(prefix);
239+
path.extend_from_slice(absolute);
240+
path.push(0);
241+
},
242+
)?;
243+
Ok(path)
244+
}

library/std/src/sys/windows/path/tests.rs

+53
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,56 @@ fn test_parse_next_component() {
4242
(OsStr::new(r"server"), OsStr::new(r"\\\\\\\\\\\\\share"))
4343
);
4444
}
45+
46+
#[test]
47+
fn verbatim() {
48+
use crate::path::Path;
49+
fn check(path: &str, expected: &str) {
50+
let verbatim = maybe_verbatim(Path::new(path)).unwrap();
51+
let verbatim = String::from_utf16_lossy(verbatim.strip_suffix(&[0]).unwrap());
52+
assert_eq!(&verbatim, expected, "{}", path);
53+
}
54+
55+
// Ensure long paths are correctly prefixed.
56+
check(
57+
r"C:\Program Files\Rust\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
58+
r"\\?\C:\Program Files\Rust\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
59+
);
60+
check(
61+
r"\\server\share\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
62+
r"\\?\UNC\server\share\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
63+
);
64+
check(
65+
r"\\.\PIPE\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
66+
r"\\?\PIPE\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
67+
);
68+
// `\\?\` prefixed paths are left unchanged...
69+
check(
70+
r"\\?\verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
71+
r"\\?\verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
72+
);
73+
// But `//?/` is not a verbatim prefix so it will be normalized.
74+
check(
75+
r"//?/E:/verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
76+
r"\\?\E:\verbatim\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
77+
);
78+
79+
// For performance, short absolute paths are left unchanged.
80+
check(r"C:\Program Files\Rust", r"C:\Program Files\Rust");
81+
check(r"\\server\share", r"\\server\share");
82+
check(r"\\.\COM1", r"\\.\COM1");
83+
84+
// Check that paths of length 247 are converted to verbatim.
85+
// This is necessary for `CreateDirectory`.
86+
check(
87+
r"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
88+
r"\\?\C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
89+
);
90+
91+
// Make sure opening a drive will work.
92+
check("Z:", "Z:");
93+
94+
// An empty path or a path that contains null are not valid paths.
95+
assert!(maybe_verbatim(Path::new("")).is_err());
96+
assert!(maybe_verbatim(Path::new("\0")).is_err());
97+
}

0 commit comments

Comments
 (0)