Skip to content

Commit 7de520d

Browse files
committed
Use verbatim paths by default
This enables users of the Rust standard library to ignore the limit of 260 "characters" per file name – instead the new limit is around 32K "characters". See also "Naming Files, Paths, and Namespaces" on MSDN, section "Maximum Path Name Limitations": https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx In order to use verbatim paths, the following conversions are performed: - `C:\` -> `\\?\C:\`. - `\\server\share\` -> `\\?\UNC\server\share\`. - `..` are evaluated by popping the last component. - `.` are dropped. - Relative paths are joined with `os::current_dir` (Special case: Path name is along the lines of `C:foobar`. This is a relative path if the current directory is on drive `C:`, otherwise it's an absolute path).
1 parent aca2057 commit 7de520d

File tree

1 file changed

+87
-18
lines changed
  • src/libstd/sys/windows

1 file changed

+87
-18
lines changed

src/libstd/sys/windows/fs.rs

Lines changed: 87 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
use io::prelude::*;
1212
use os::windows::prelude::*;
1313

14+
use env;
1415
use ffi::OsString;
1516
use fmt;
1617
use io::{self, Error, SeekFrom};
1718
use libc::{self, HANDLE};
1819
use mem;
19-
use path::{Path, PathBuf};
20+
use path::{self, Path, PathBuf};
2021
use ptr;
2122
use slice;
2223
use sync::Arc;
@@ -227,7 +228,7 @@ impl File {
227228
}
228229

229230
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
230-
let path = to_utf16(path);
231+
let path = try!(to_system_path(path));
231232
let handle = unsafe {
232233
libc::CreateFileW(path.as_ptr(),
233234
opts.get_desired_access(),
@@ -378,8 +379,76 @@ impl fmt::Debug for File {
378379
}
379380
}
380381

381-
pub fn to_utf16(s: &Path) -> Vec<u16> {
382-
s.as_os_str().encode_wide().chain(Some(0)).collect()
382+
pub fn to_u16s(path: &Path) -> Vec<u16> {
383+
path.as_os_str().encode_wide().chain(Some(0)).collect()
384+
}
385+
386+
fn to_system_path(path: &Path) -> io::Result<Vec<u16>> {
387+
Ok(to_u16s(&*try!(to_system_path_impl(path, 0))))
388+
}
389+
390+
pub fn to_system_path_impl(path: &Path, recursion: u8) -> io::Result<PathBuf> {
391+
if recursion > 1 { unreachable!(); }
392+
let recursion = recursion + 1;
393+
394+
let mut components = path.components();
395+
let maybe_prefix = {
396+
let mut copy = components.clone();
397+
copy.next().and_then(|c| {
398+
match c {
399+
path::Component::Prefix(p) => {
400+
components = copy;
401+
Some(p.kind())
402+
}
403+
_ => None,
404+
}
405+
})
406+
};
407+
let mut new_path: PathBuf = match maybe_prefix {
408+
Some(path::Prefix::Verbatim(..))
409+
| Some(path::Prefix::VerbatimUNC(..))
410+
| Some(path::Prefix::VerbatimDisk(..))
411+
| Some(path::Prefix::DeviceNS(..)) => {
412+
return Ok(path.into())
413+
}
414+
// unwrap: The function can only fail getting the current directory,
415+
// but since the working directory is already absolute, it will not
416+
// need this value again.
417+
None => to_system_path_impl(&*try!(env::current_dir()), recursion).unwrap(),
418+
Some(path::Prefix::Disk(disk)) => {
419+
let current_dir = try!(env::current_dir());
420+
// unwrap: Absolute paths always have a prefix.
421+
let current_drive = match current_dir.prefix().unwrap() {
422+
path::Prefix::VerbatimDisk(d) | path::Prefix::Disk(d) => Some(d),
423+
_ => None,
424+
};
425+
if !path.has_root() && current_drive == Some(disk) {
426+
// unwrap: See above.
427+
to_system_path_impl(&current_dir, recursion).unwrap()
428+
} else {
429+
format!(r"\\?\{}:\", disk as char).into()
430+
}
431+
}
432+
Some(path::Prefix::UNC(server, share)) => {
433+
let mut new_path = OsString::new();
434+
new_path.push(r"\\?\UNC\");
435+
new_path.push(server);
436+
new_path.push(r"\");
437+
new_path.push(share);
438+
new_path.push(r"\");
439+
new_path.into()
440+
}
441+
};
442+
for c in components {
443+
use path::Component::*;
444+
match c {
445+
Prefix(..) => unreachable!(),
446+
CurDir => {},
447+
ParentDir => { let _ = new_path.pop(); }
448+
RootDir | Normal(..) => new_path.push(c.as_os_str()),
449+
}
450+
}
451+
Ok(new_path)
383452
}
384453

385454
impl FileAttr {
@@ -450,7 +519,7 @@ impl DirBuilder {
450519
pub fn new() -> DirBuilder { DirBuilder }
451520

452521
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
453-
let p = to_utf16(p);
522+
let p = try!(to_system_path(p));
454523
try!(cvt(unsafe {
455524
libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
456525
}));
@@ -461,7 +530,7 @@ impl DirBuilder {
461530
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
462531
let root = p.to_path_buf();
463532
let star = p.join("*");
464-
let path = to_utf16(&star);
533+
let path = try!(to_system_path(&star));
465534

466535
unsafe {
467536
let mut wfd = mem::zeroed();
@@ -479,14 +548,14 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
479548
}
480549

481550
pub fn unlink(p: &Path) -> io::Result<()> {
482-
let p_utf16 = to_utf16(p);
551+
let p_utf16 = try!(to_system_path(p));
483552
try!(cvt(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) }));
484553
Ok(())
485554
}
486555

487556
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
488-
let old = to_utf16(old);
489-
let new = to_utf16(new);
557+
let old = try!(to_system_path(old));
558+
let new = try!(to_system_path(new));
490559
try!(cvt(unsafe {
491560
libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
492561
libc::MOVEFILE_REPLACE_EXISTING)
@@ -495,7 +564,7 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
495564
}
496565

497566
pub fn rmdir(p: &Path) -> io::Result<()> {
498-
let p = to_utf16(p);
567+
let p = try!(to_system_path(p));
499568
try!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }));
500569
Ok(())
501570
}
@@ -510,8 +579,8 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
510579
}
511580

512581
pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
513-
let src = to_utf16(src);
514-
let dst = to_utf16(dst);
582+
let src = try!(to_system_path(src));
583+
let dst = try!(to_system_path(dst));
515584
let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
516585
try!(cvt(unsafe {
517586
c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
@@ -520,8 +589,8 @@ pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
520589
}
521590

522591
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
523-
let src = to_utf16(src);
524-
let dst = to_utf16(dst);
592+
let src = try!(to_system_path(src));
593+
let dst = try!(to_system_path(dst));
525594
try!(cvt(unsafe {
526595
libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
527596
}));
@@ -547,7 +616,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
547616
}
548617

549618
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
550-
let utf16 = to_utf16(p);
619+
let utf16 = try!(to_system_path(p));
551620
unsafe {
552621
let mut attr: FileAttr = mem::zeroed();
553622
try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(),
@@ -564,7 +633,7 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
564633
}
565634

566635
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
567-
let p = to_utf16(p);
636+
let p = try!(to_system_path(p));
568637
unsafe {
569638
try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs)));
570639
Ok(())
@@ -602,8 +671,8 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
602671
*(lpData as *mut i64) = TotalBytesTransferred;
603672
c::PROGRESS_CONTINUE
604673
}
605-
let pfrom = to_utf16(from);
606-
let pto = to_utf16(to);
674+
let pfrom = try!(to_system_path(from));
675+
let pto = try!(to_system_path(to));
607676
let mut size = 0i64;
608677
try!(cvt(unsafe {
609678
c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),

0 commit comments

Comments
 (0)