From 7de520d59a9b4bda8400c221cac8678ed97b0094 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Thu, 13 Aug 2015 12:32:01 +0200 Subject: [PATCH] Use verbatim paths by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- src/libstd/sys/windows/fs.rs | 105 +++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 18 deletions(-) diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index d413d536cc85b..9394c38c3a6cf 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -11,12 +11,13 @@ use io::prelude::*; use os::windows::prelude::*; +use env; use ffi::OsString; use fmt; use io::{self, Error, SeekFrom}; use libc::{self, HANDLE}; use mem; -use path::{Path, PathBuf}; +use path::{self, Path, PathBuf}; use ptr; use slice; use sync::Arc; @@ -227,7 +228,7 @@ impl File { } pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = to_utf16(path); + let path = try!(to_system_path(path)); let handle = unsafe { libc::CreateFileW(path.as_ptr(), opts.get_desired_access(), @@ -378,8 +379,76 @@ impl fmt::Debug for File { } } -pub fn to_utf16(s: &Path) -> Vec { - s.as_os_str().encode_wide().chain(Some(0)).collect() +pub fn to_u16s(path: &Path) -> Vec { + path.as_os_str().encode_wide().chain(Some(0)).collect() +} + +fn to_system_path(path: &Path) -> io::Result> { + Ok(to_u16s(&*try!(to_system_path_impl(path, 0)))) +} + +pub fn to_system_path_impl(path: &Path, recursion: u8) -> io::Result { + if recursion > 1 { unreachable!(); } + let recursion = recursion + 1; + + let mut components = path.components(); + let maybe_prefix = { + let mut copy = components.clone(); + copy.next().and_then(|c| { + match c { + path::Component::Prefix(p) => { + components = copy; + Some(p.kind()) + } + _ => None, + } + }) + }; + let mut new_path: PathBuf = match maybe_prefix { + Some(path::Prefix::Verbatim(..)) + | Some(path::Prefix::VerbatimUNC(..)) + | Some(path::Prefix::VerbatimDisk(..)) + | Some(path::Prefix::DeviceNS(..)) => { + return Ok(path.into()) + } + // unwrap: The function can only fail getting the current directory, + // but since the working directory is already absolute, it will not + // need this value again. + None => to_system_path_impl(&*try!(env::current_dir()), recursion).unwrap(), + Some(path::Prefix::Disk(disk)) => { + let current_dir = try!(env::current_dir()); + // unwrap: Absolute paths always have a prefix. + let current_drive = match current_dir.prefix().unwrap() { + path::Prefix::VerbatimDisk(d) | path::Prefix::Disk(d) => Some(d), + _ => None, + }; + if !path.has_root() && current_drive == Some(disk) { + // unwrap: See above. + to_system_path_impl(¤t_dir, recursion).unwrap() + } else { + format!(r"\\?\{}:\", disk as char).into() + } + } + Some(path::Prefix::UNC(server, share)) => { + let mut new_path = OsString::new(); + new_path.push(r"\\?\UNC\"); + new_path.push(server); + new_path.push(r"\"); + new_path.push(share); + new_path.push(r"\"); + new_path.into() + } + }; + for c in components { + use path::Component::*; + match c { + Prefix(..) => unreachable!(), + CurDir => {}, + ParentDir => { let _ = new_path.pop(); } + RootDir | Normal(..) => new_path.push(c.as_os_str()), + } + } + Ok(new_path) } impl FileAttr { @@ -450,7 +519,7 @@ impl DirBuilder { pub fn new() -> DirBuilder { DirBuilder } pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let p = to_utf16(p); + let p = try!(to_system_path(p)); try!(cvt(unsafe { libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })); @@ -461,7 +530,7 @@ impl DirBuilder { pub fn readdir(p: &Path) -> io::Result { let root = p.to_path_buf(); let star = p.join("*"); - let path = to_utf16(&star); + let path = try!(to_system_path(&star)); unsafe { let mut wfd = mem::zeroed(); @@ -479,14 +548,14 @@ pub fn readdir(p: &Path) -> io::Result { } pub fn unlink(p: &Path) -> io::Result<()> { - let p_utf16 = to_utf16(p); + let p_utf16 = try!(to_system_path(p)); try!(cvt(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })); Ok(()) } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = to_utf16(old); - let new = to_utf16(new); + let old = try!(to_system_path(old)); + let new = try!(to_system_path(new)); try!(cvt(unsafe { libc::MoveFileExW(old.as_ptr(), new.as_ptr(), libc::MOVEFILE_REPLACE_EXISTING) @@ -495,7 +564,7 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { } pub fn rmdir(p: &Path) -> io::Result<()> { - let p = to_utf16(p); + let p = try!(to_system_path(p)); try!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })); Ok(()) } @@ -510,8 +579,8 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { } pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> { - let src = to_utf16(src); - let dst = to_utf16(dst); + let src = try!(to_system_path(src)); + let dst = try!(to_system_path(dst)); let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; try!(cvt(unsafe { 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<()> { } pub fn link(src: &Path, dst: &Path) -> io::Result<()> { - let src = to_utf16(src); - let dst = to_utf16(dst); + let src = try!(to_system_path(src)); + let dst = try!(to_system_path(dst)); try!(cvt(unsafe { libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut()) })); @@ -547,7 +616,7 @@ pub fn stat(p: &Path) -> io::Result { } pub fn lstat(p: &Path) -> io::Result { - let utf16 = to_utf16(p); + let utf16 = try!(to_system_path(p)); unsafe { let mut attr: FileAttr = mem::zeroed(); try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(), @@ -564,7 +633,7 @@ pub fn lstat(p: &Path) -> io::Result { } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = to_utf16(p); + let p = try!(to_system_path(p)); unsafe { try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))); Ok(()) @@ -602,8 +671,8 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { *(lpData as *mut i64) = TotalBytesTransferred; c::PROGRESS_CONTINUE } - let pfrom = to_utf16(from); - let pto = to_utf16(to); + let pfrom = try!(to_system_path(from)); + let pto = try!(to_system_path(to)); let mut size = 0i64; try!(cvt(unsafe { c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),