1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use os
::windows
::prelude
::*;
15 use io
::{self, Error, SeekFrom}
;
17 use path
::{Path, PathBuf}
;
21 use sys
::handle
::Handle
;
22 use sys
::time
::SystemTime
;
24 use sys_common
::FromInner
;
28 pub struct File { handle: Handle }
33 creation_time
: c
::FILETIME
,
34 last_access_time
: c
::FILETIME
,
35 last_write_time
: c
::FILETIME
,
37 reparse_tag
: c
::DWORD
,
40 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
42 Dir
, File
, SymlinkFile
, SymlinkDir
, ReparsePoint
, MountPoint
,
46 handle
: FindNextFileHandle
,
48 first
: Option
<c
::WIN32_FIND_DATAW
>,
51 struct FindNextFileHandle(c
::HANDLE
);
53 unsafe impl Send
for FindNextFileHandle {}
54 unsafe impl Sync
for FindNextFileHandle {}
58 data
: c
::WIN32_FIND_DATAW
,
61 #[derive(Clone, Debug)]
62 pub struct OpenOptions
{
72 access_mode
: Option
<c
::DWORD
>,
75 security_qos_flags
: c
::DWORD
,
76 security_attributes
: usize, // FIXME: should be a reference
79 #[derive(Clone, PartialEq, Eq, Debug)]
80 pub struct FilePermissions { attrs: c::DWORD }
83 pub struct DirBuilder
;
85 impl fmt
::Debug
for ReadDir
{
86 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
87 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
88 // Thus the result will be e g 'ReadDir("C:\")'
89 fmt
::Debug
::fmt(&*self.root
, f
)
93 impl Iterator
for ReadDir
{
94 type Item
= io
::Result
<DirEntry
>;
95 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
96 if let Some(first
) = self.first
.take() {
97 if let Some(e
) = DirEntry
::new(&self.root
, &first
) {
102 let mut wfd
= mem
::zeroed();
104 if c
::FindNextFileW(self.handle
.0, &mut wfd
) == 0 {
105 if c
::GetLastError() == c
::ERROR_NO_MORE_FILES
{
108 return Some(Err(Error
::last_os_error()))
111 if let Some(e
) = DirEntry
::new(&self.root
, &wfd
) {
119 impl Drop
for FindNextFileHandle
{
121 let r
= unsafe { c::FindClose(self.0) }
;
122 debug_assert
!(r
!= 0);
127 fn new(root
: &Arc
<PathBuf
>, wfd
: &c
::WIN32_FIND_DATAW
) -> Option
<DirEntry
> {
128 match &wfd
.cFileName
[0..3] {
129 // check for '.' and '..'
131 &[46, 46, 0, ..] => return None
,
141 pub fn path(&self) -> PathBuf
{
142 self.root
.join(&self.file_name())
145 pub fn file_name(&self) -> OsString
{
146 let filename
= super::truncate_utf16_at_nul(&self.data
.cFileName
);
147 OsString
::from_wide(filename
)
150 pub fn file_type(&self) -> io
::Result
<FileType
> {
151 Ok(FileType
::new(self.data
.dwFileAttributes
,
152 /* reparse_tag = */ self.data
.dwReserved0
))
155 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
157 attributes
: self.data
.dwFileAttributes
,
158 creation_time
: self.data
.ftCreationTime
,
159 last_access_time
: self.data
.ftLastAccessTime
,
160 last_write_time
: self.data
.ftLastWriteTime
,
161 file_size
: ((self.data
.nFileSizeHigh
as u64) << 32) | (self.data
.nFileSizeLow
as u64),
162 reparse_tag
: if self.data
.dwFileAttributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
163 // reserved unless this is a reparse point
164 self.data
.dwReserved0
173 pub fn new() -> OpenOptions
{
185 share_mode
: c
::FILE_SHARE_READ
| c
::FILE_SHARE_WRITE
| c
::FILE_SHARE_DELETE
,
187 security_qos_flags
: 0,
188 security_attributes
: 0,
192 pub fn read(&mut self, read
: bool
) { self.read = read; }
193 pub fn write(&mut self, write
: bool
) { self.write = write; }
194 pub fn append(&mut self, append
: bool
) { self.append = append; }
195 pub fn truncate(&mut self, truncate
: bool
) { self.truncate = truncate; }
196 pub fn create(&mut self, create
: bool
) { self.create = create; }
197 pub fn create_new(&mut self, create_new
: bool
) { self.create_new = create_new; }
199 pub fn custom_flags(&mut self, flags
: u32) { self.custom_flags = flags; }
200 pub fn access_mode(&mut self, access_mode
: u32) { self.access_mode = Some(access_mode); }
201 pub fn share_mode(&mut self, share_mode
: u32) { self.share_mode = share_mode; }
202 pub fn attributes(&mut self, attrs
: u32) { self.attributes = attrs; }
203 pub fn security_qos_flags(&mut self, flags
: u32) { self.security_qos_flags = flags; }
204 pub fn security_attributes(&mut self, attrs
: c
::LPSECURITY_ATTRIBUTES
) {
205 self.security_attributes
= attrs
as usize;
208 fn get_access_mode(&self) -> io
::Result
<c
::DWORD
> {
209 const ERROR_INVALID_PARAMETER
: i32 = 87;
211 match (self.read
, self.write
, self.append
, self.access_mode
) {
212 (.., Some(mode
)) => Ok(mode
),
213 (true, false, false, None
) => Ok(c
::GENERIC_READ
),
214 (false, true, false, None
) => Ok(c
::GENERIC_WRITE
),
215 (true, true, false, None
) => Ok(c
::GENERIC_READ
| c
::GENERIC_WRITE
),
216 (false, _
, true, None
) => Ok(c
::FILE_GENERIC_WRITE
& !c
::FILE_WRITE_DATA
),
217 (true, _
, true, None
) => Ok(c
::GENERIC_READ
|
218 (c
::FILE_GENERIC_WRITE
& !c
::FILE_WRITE_DATA
)),
219 (false, false, false, None
) => Err(Error
::from_raw_os_error(ERROR_INVALID_PARAMETER
)),
223 fn get_creation_mode(&self) -> io
::Result
<c
::DWORD
> {
224 const ERROR_INVALID_PARAMETER
: i32 = 87;
226 match (self.write
, self.append
) {
229 if self.truncate
|| self.create
|| self.create_new
{
230 return Err(Error
::from_raw_os_error(ERROR_INVALID_PARAMETER
));
233 if self.truncate
&& !self.create_new
{
234 return Err(Error
::from_raw_os_error(ERROR_INVALID_PARAMETER
));
238 Ok(match (self.create
, self.truncate
, self.create_new
) {
239 (false, false, false) => c
::OPEN_EXISTING
,
240 (true, false, false) => c
::OPEN_ALWAYS
,
241 (false, true, false) => c
::TRUNCATE_EXISTING
,
242 (true, true, false) => c
::CREATE_ALWAYS
,
243 (_
, _
, true) => c
::CREATE_NEW
,
247 fn get_flags_and_attributes(&self) -> c
::DWORD
{
250 self.security_qos_flags
|
251 if self.security_qos_flags
!= 0 { c::SECURITY_SQOS_PRESENT }
else { 0 }
|
252 if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT }
else { 0 }
257 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
258 let path
= to_u16s(path
)?
;
259 let handle
= unsafe {
260 c
::CreateFileW(path
.as_ptr(),
261 opts
.get_access_mode()?
,
263 opts
.security_attributes
as *mut _
,
264 opts
.get_creation_mode()?
,
265 opts
.get_flags_and_attributes(),
268 if handle
== c
::INVALID_HANDLE_VALUE
{
269 Err(Error
::last_os_error())
271 Ok(File { handle: Handle::new(handle) }
)
275 pub fn fsync(&self) -> io
::Result
<()> {
276 cvt(unsafe { c::FlushFileBuffers(self.handle.raw()) }
)?
;
280 pub fn datasync(&self) -> io
::Result
<()> { self.fsync() }
282 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
283 let mut info
= c
::FILE_END_OF_FILE_INFO
{
284 EndOfFile
: size
as c
::LARGE_INTEGER
,
286 let size
= mem
::size_of_val(&info
);
288 c
::SetFileInformationByHandle(self.handle
.raw(),
289 c
::FileEndOfFileInfo
,
290 &mut info
as *mut _
as *mut _
,
296 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
298 let mut info
: c
::BY_HANDLE_FILE_INFORMATION
= mem
::zeroed();
299 cvt(c
::GetFileInformationByHandle(self.handle
.raw(),
301 let mut attr
= FileAttr
{
302 attributes
: info
.dwFileAttributes
,
303 creation_time
: info
.ftCreationTime
,
304 last_access_time
: info
.ftLastAccessTime
,
305 last_write_time
: info
.ftLastWriteTime
,
306 file_size
: ((info
.nFileSizeHigh
as u64) << 32) | (info
.nFileSizeLow
as u64),
309 if attr
.is_reparse_point() {
310 let mut b
= [0; c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
];
311 if let Ok((_
, buf
)) = self.reparse_point(&mut b
) {
312 attr
.reparse_tag
= buf
.ReparseTag
;
319 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
320 self.handle
.read(buf
)
323 pub fn read_at(&self, buf
: &mut [u8], offset
: u64) -> io
::Result
<usize> {
324 self.handle
.read_at(buf
, offset
)
327 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
328 self.handle
.write(buf
)
331 pub fn write_at(&self, buf
: &[u8], offset
: u64) -> io
::Result
<usize> {
332 self.handle
.write_at(buf
, offset
)
335 pub fn flush(&self) -> io
::Result
<()> { Ok(()) }
337 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
338 let (whence
, pos
) = match pos
{
339 // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this
341 SeekFrom
::Start(n
) => (c
::FILE_BEGIN
, n
as i64),
342 SeekFrom
::End(n
) => (c
::FILE_END
, n
),
343 SeekFrom
::Current(n
) => (c
::FILE_CURRENT
, n
),
345 let pos
= pos
as c
::LARGE_INTEGER
;
348 c
::SetFilePointerEx(self.handle
.raw(), pos
,
354 pub fn duplicate(&self) -> io
::Result
<File
> {
356 handle
: self.handle
.duplicate(0, true, c
::DUPLICATE_SAME_ACCESS
)?
,
360 pub fn handle(&self) -> &Handle { &self.handle }
362 pub fn into_handle(self) -> Handle { self.handle }
364 fn reparse_point
<'a
>(&self,
365 space
: &'a
mut [u8; c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
])
366 -> io
::Result
<(c
::DWORD
, &'a c
::REPARSE_DATA_BUFFER
)> {
370 c
::DeviceIoControl(self.handle
.raw(),
371 c
::FSCTL_GET_REPARSE_POINT
,
374 space
.as_mut_ptr() as *mut _
,
375 space
.len() as c
::DWORD
,
379 Ok((bytes
, &*(space
.as_ptr() as *const c
::REPARSE_DATA_BUFFER
)))
383 fn readlink(&self) -> io
::Result
<PathBuf
> {
384 let mut space
= [0u8; c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
];
385 let (_bytes
, buf
) = self.reparse_point(&mut space
)?
;
387 let (path_buffer
, subst_off
, subst_len
, relative
) = match buf
.ReparseTag
{
388 c
::IO_REPARSE_TAG_SYMLINK
=> {
389 let info
: *const c
::SYMBOLIC_LINK_REPARSE_BUFFER
=
390 &buf
.rest
as *const _
as *const _
;
391 (&(*info
).PathBuffer
as *const _
as *const u16,
392 (*info
).SubstituteNameOffset
/ 2,
393 (*info
).SubstituteNameLength
/ 2,
394 (*info
).Flags
& c
::SYMLINK_FLAG_RELATIVE
!= 0)
396 c
::IO_REPARSE_TAG_MOUNT_POINT
=> {
397 let info
: *const c
::MOUNT_POINT_REPARSE_BUFFER
=
398 &buf
.rest
as *const _
as *const _
;
399 (&(*info
).PathBuffer
as *const _
as *const u16,
400 (*info
).SubstituteNameOffset
/ 2,
401 (*info
).SubstituteNameLength
/ 2,
404 _
=> return Err(io
::Error
::new(io
::ErrorKind
::Other
,
405 "Unsupported reparse point type"))
407 let subst_ptr
= path_buffer
.offset(subst_off
as isize);
408 let mut subst
= slice
::from_raw_parts(subst_ptr
, subst_len
as usize);
409 // Absolute paths start with an NT internal namespace prefix `\??\`
410 // We should not let it leak through.
411 if !relative
&& subst
.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
414 Ok(PathBuf
::from(OsString
::from_wide(subst
)))
418 pub fn set_permissions(&self, perm
: FilePermissions
) -> io
::Result
<()> {
419 let mut info
= c
::FILE_BASIC_INFO
{
424 FileAttributes
: perm
.attrs
,
426 let size
= mem
::size_of_val(&info
);
428 c
::SetFileInformationByHandle(self.handle
.raw(),
430 &mut info
as *mut _
as *mut _
,
437 impl FromInner
<c
::HANDLE
> for File
{
438 fn from_inner(handle
: c
::HANDLE
) -> File
{
439 File { handle: Handle::new(handle) }
443 impl fmt
::Debug
for File
{
444 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
445 // FIXME(#24570): add more info here (e.g. mode)
446 let mut b
= f
.debug_struct("File");
447 b
.field("handle", &self.handle
.raw());
448 if let Ok(path
) = get_path(&self) {
449 b
.field("path", &path
);
456 pub fn size(&self) -> u64 {
460 pub fn perm(&self) -> FilePermissions
{
461 FilePermissions { attrs: self.attributes }
464 pub fn attrs(&self) -> u32 { self.attributes as u32 }
466 pub fn file_type(&self) -> FileType
{
467 FileType
::new(self.attributes
, self.reparse_tag
)
470 pub fn modified(&self) -> io
::Result
<SystemTime
> {
471 Ok(SystemTime
::from(self.last_write_time
))
474 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
475 Ok(SystemTime
::from(self.last_access_time
))
478 pub fn created(&self) -> io
::Result
<SystemTime
> {
479 Ok(SystemTime
::from(self.creation_time
))
482 pub fn modified_u64(&self) -> u64 {
483 to_u64(&self.last_write_time
)
486 pub fn accessed_u64(&self) -> u64 {
487 to_u64(&self.last_access_time
)
490 pub fn created_u64(&self) -> u64 {
491 to_u64(&self.creation_time
)
494 fn is_reparse_point(&self) -> bool
{
495 self.attributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0
499 fn to_u64(ft
: &c
::FILETIME
) -> u64 {
500 (ft
.dwLowDateTime
as u64) | ((ft
.dwHighDateTime
as u64) << 32)
503 impl FilePermissions
{
504 pub fn readonly(&self) -> bool
{
505 self.attrs
& c
::FILE_ATTRIBUTE_READONLY
!= 0
508 pub fn set_readonly(&mut self, readonly
: bool
) {
510 self.attrs
|= c
::FILE_ATTRIBUTE_READONLY
;
512 self.attrs
&= !c
::FILE_ATTRIBUTE_READONLY
;
518 fn new(attrs
: c
::DWORD
, reparse_tag
: c
::DWORD
) -> FileType
{
519 match (attrs
& c
::FILE_ATTRIBUTE_DIRECTORY
!= 0,
520 attrs
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0,
522 (false, false, _
) => FileType
::File
,
523 (true, false, _
) => FileType
::Dir
,
524 (false, true, c
::IO_REPARSE_TAG_SYMLINK
) => FileType
::SymlinkFile
,
525 (true, true, c
::IO_REPARSE_TAG_SYMLINK
) => FileType
::SymlinkDir
,
526 (true, true, c
::IO_REPARSE_TAG_MOUNT_POINT
) => FileType
::MountPoint
,
527 (_
, true, _
) => FileType
::ReparsePoint
,
528 // Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is
529 // invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint
530 // to indicate it is something symlink-like, but not something you can follow.
534 pub fn is_dir(&self) -> bool { *self == FileType::Dir }
535 pub fn is_file(&self) -> bool { *self == FileType::File }
536 pub fn is_symlink(&self) -> bool
{
537 *self == FileType
::SymlinkFile
||
538 *self == FileType
::SymlinkDir
||
539 *self == FileType
::MountPoint
541 pub fn is_symlink_dir(&self) -> bool
{
542 *self == FileType
::SymlinkDir
|| *self == FileType
::MountPoint
547 pub fn new() -> DirBuilder { DirBuilder }
549 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
552 c
::CreateDirectoryW(p
.as_ptr(), ptr
::null_mut())
558 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
559 let root
= p
.to_path_buf();
560 let star
= p
.join("*");
561 let path
= to_u16s(&star
)?
;
564 let mut wfd
= mem
::zeroed();
565 let find_handle
= c
::FindFirstFileW(path
.as_ptr(), &mut wfd
);
566 if find_handle
!= c
::INVALID_HANDLE_VALUE
{
568 handle
: FindNextFileHandle(find_handle
),
569 root
: Arc
::new(root
),
573 Err(Error
::last_os_error())
578 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
579 let p_u16s
= to_u16s(p
)?
;
580 cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) }
)?
;
584 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
585 let old
= to_u16s(old
)?
;
586 let new
= to_u16s(new
)?
;
588 c
::MoveFileExW(old
.as_ptr(), new
.as_ptr(), c
::MOVEFILE_REPLACE_EXISTING
)
593 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
595 cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }
)?
;
599 pub fn remove_dir_all(path
: &Path
) -> io
::Result
<()> {
600 let filetype
= lstat(path
)?
.file_type();
601 if filetype
.is_symlink() {
602 // On Windows symlinks to files and directories are removed differently.
603 // rmdir only deletes dir symlinks and junctions, not file symlinks.
606 remove_dir_all_recursive(path
)
610 fn remove_dir_all_recursive(path
: &Path
) -> io
::Result
<()> {
611 for child
in readdir(path
)?
{
613 let child_type
= child
.file_type()?
;
614 if child_type
.is_dir() {
615 remove_dir_all_recursive(&child
.path())?
;
616 } else if child_type
.is_symlink_dir() {
617 rmdir(&child
.path())?
;
619 unlink(&child
.path())?
;
625 pub fn readlink(path
: &Path
) -> io
::Result
<PathBuf
> {
626 // Open the link with no access mode, instead of generic read.
627 // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
628 // this is needed for a common case.
629 let mut opts
= OpenOptions
::new();
631 opts
.custom_flags(c
::FILE_FLAG_OPEN_REPARSE_POINT
|
632 c
::FILE_FLAG_BACKUP_SEMANTICS
);
633 let file
= File
::open(&path
, &opts
)?
;
637 pub fn symlink(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
638 symlink_inner(src
, dst
, false)
641 pub fn symlink_inner(src
: &Path
, dst
: &Path
, dir
: bool
) -> io
::Result
<()> {
642 let src
= to_u16s(src
)?
;
643 let dst
= to_u16s(dst
)?
;
644 let flags
= if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY }
else { 0 }
;
645 // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
646 // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
647 // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
648 // added to dwFlags to opt into this behaviour.
649 let result
= cvt(unsafe {
650 c
::CreateSymbolicLinkW(dst
.as_ptr(), src
.as_ptr(),
651 flags
| c
::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
) as c
::BOOL
653 if let Err(err
) = result
{
654 if err
.raw_os_error() == Some(c
::ERROR_INVALID_PARAMETER
as i32) {
655 // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
656 // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
658 c
::CreateSymbolicLinkW(dst
.as_ptr(), src
.as_ptr(), flags
) as c
::BOOL
667 pub fn link(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
668 let src
= to_u16s(src
)?
;
669 let dst
= to_u16s(dst
)?
;
671 c
::CreateHardLinkW(dst
.as_ptr(), src
.as_ptr(), ptr
::null_mut())
676 pub fn stat(path
: &Path
) -> io
::Result
<FileAttr
> {
677 let mut opts
= OpenOptions
::new();
678 // No read or write permissions are necessary
680 // This flag is so we can open directories too
681 opts
.custom_flags(c
::FILE_FLAG_BACKUP_SEMANTICS
);
682 let file
= File
::open(path
, &opts
)?
;
686 pub fn lstat(path
: &Path
) -> io
::Result
<FileAttr
> {
687 let mut opts
= OpenOptions
::new();
688 // No read or write permissions are necessary
690 opts
.custom_flags(c
::FILE_FLAG_BACKUP_SEMANTICS
| c
::FILE_FLAG_OPEN_REPARSE_POINT
);
691 let file
= File
::open(path
, &opts
)?
;
695 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
698 cvt(c
::SetFileAttributesW(p
.as_ptr(), perm
.attrs
))?
;
703 fn get_path(f
: &File
) -> io
::Result
<PathBuf
> {
704 super::fill_utf16_buf(|buf
, sz
| unsafe {
705 c
::GetFinalPathNameByHandleW(f
.handle
.raw(), buf
, sz
,
708 PathBuf
::from(OsString
::from_wide(buf
))
712 pub fn canonicalize(p
: &Path
) -> io
::Result
<PathBuf
> {
713 let mut opts
= OpenOptions
::new();
714 // No read or write permissions are necessary
716 // This flag is so we can open directories too
717 opts
.custom_flags(c
::FILE_FLAG_BACKUP_SEMANTICS
);
718 let f
= File
::open(p
, &opts
)?
;
722 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
723 unsafe extern "system" fn callback(
724 _TotalFileSize
: c
::LARGE_INTEGER
,
725 TotalBytesTransferred
: c
::LARGE_INTEGER
,
726 _StreamSize
: c
::LARGE_INTEGER
,
727 _StreamBytesTransferred
: c
::LARGE_INTEGER
,
728 _dwStreamNumber
: c
::DWORD
,
729 _dwCallbackReason
: c
::DWORD
,
730 _hSourceFile
: c
::HANDLE
,
731 _hDestinationFile
: c
::HANDLE
,
734 *(lpData
as *mut i64) = TotalBytesTransferred
;
737 let pfrom
= to_u16s(from
)?
;
738 let pto
= to_u16s(to
)?
;
741 c
::CopyFileExW(pfrom
.as_ptr(), pto
.as_ptr(), Some(callback
),
742 &mut size
as *mut _
as *mut _
, ptr
::null_mut(), 0)
748 pub fn symlink_junction
<P
: AsRef
<Path
>, Q
: AsRef
<Path
>>(src
: P
, dst
: Q
) -> io
::Result
<()> {
749 symlink_junction_inner(src
.as_ref(), dst
.as_ref())
752 // Creating a directory junction on windows involves dealing with reparse
753 // points and the DeviceIoControl function, and this code is a skeleton of
754 // what can be found here:
756 // http://www.flexhex.com/docs/articles/hard-links.phtml
758 fn symlink_junction_inner(target
: &Path
, junction
: &Path
) -> io
::Result
<()> {
759 let d
= DirBuilder
::new();
762 let mut opts
= OpenOptions
::new();
764 opts
.custom_flags(c
::FILE_FLAG_OPEN_REPARSE_POINT
|
765 c
::FILE_FLAG_BACKUP_SEMANTICS
);
766 let f
= File
::open(junction
, &opts
)?
;
767 let h
= f
.handle().raw();
770 let mut data
= [0u8; c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
];
771 let db
= data
.as_mut_ptr()
772 as *mut c
::REPARSE_MOUNTPOINT_DATA_BUFFER
;
773 let buf
= &mut (*db
).ReparseTarget
as *mut _
;
775 // FIXME: this conversion is very hacky
777 let v
= v
.iter().map(|x
| *x
as u16);
778 for c
in v
.chain(target
.as_os_str().encode_wide()) {
784 (*db
).ReparseTag
= c
::IO_REPARSE_TAG_MOUNT_POINT
;
785 (*db
).ReparseTargetMaximumLength
= (i
* 2) as c
::WORD
;
786 (*db
).ReparseTargetLength
= ((i
- 1) * 2) as c
::WORD
;
787 (*db
).ReparseDataLength
=
788 (*db
).ReparseTargetLength
as c
::DWORD
+ 12;
791 cvt(c
::DeviceIoControl(h
as *mut _
,
792 c
::FSCTL_SET_REPARSE_POINT
,
793 data
.as_ptr() as *mut _
,
794 (*db
).ReparseDataLength
+ 8,
797 ptr
::null_mut())).map(|_
| ())