1 pub use std
::os
::windows
::fs
::{symlink_file
, symlink_dir
};
2 pub use std
::fs
::remove_dir
as remove_symlink_dir
;
4 use std
::io
::{self, Error
};
7 use std
::os
::windows
::ffi
::OsStrExt
;
8 use std
::os
::windows
::raw
::HANDLE
;
15 pub fn symlink_auto
<P
: AsRef
<Path
>, Q
: AsRef
<Path
>>(src
: P
, dst
: Q
) -> io
::Result
<()> {
16 if fs
::metadata(src
.as_ref())?
.is_dir() {
17 symlink_dir(src
.as_ref(), dst
.as_ref())
19 symlink_file(src
.as_ref(), dst
.as_ref())
24 pub fn remove_symlink_auto
<P
: AsRef
<Path
>>(path
: P
) -> io
::Result
<()> {
25 // Ideally we’d be able to do fs::metadata(path.as_ref())?.file_type().{is_symlink_dir,
26 // is_symlink}() or similar, but the standard library doesn’t expose that; really, we care
27 // about whether the internal FileType object is a SymlinkFile or a SymlinkDir, but that’s not
28 // exposed in any way, so ☹. Instead, we copy all that mess of code and call the Windows API
29 // directly ourselves. Icky, isn’t it? (The alternative is copying the struct and transmuting;
30 // that’s even more icky, though quite a bit shorter.)
31 match symlink_type(path
.as_ref())?
{
32 SymlinkType
::Dir
=> fs
::remove_dir(path
),
33 SymlinkType
::File
=> fs
::remove_file(path
),
34 SymlinkType
::Not
=> Err(io
::Error
::new(io
::ErrorKind
::InvalidInput
,
35 "path is not a symlink")),
39 pub enum SymlinkType
{
45 // Taken from rust/src/libstd/sys/windows/mod.rs
46 fn to_u16s
<S
: AsRef
<OsStr
>>(s
: S
) -> io
::Result
<Vec
<u16>> {
47 let mut encoded
: Vec
<u16> = s
.as_ref().encode_wide().collect();
48 if encoded
.iter().any(|&u
| u
== 0) {
49 return Err(io
::Error
::new(io
::ErrorKind
::InvalidInput
,
50 "strings passed to the Windows API cannot contain NULs"));
56 // Drawn from rust/src/libstd/sys/windows/fs.rs; derived from stat(path) and File::open(path,
57 // opts).file_attr().file_type().{is_symlink, is_symlink_dir}().
58 pub fn symlink_type(path
: &Path
) -> io
::Result
<SymlinkType
> {
59 // Derived from File::file_attr, FileAttr::file_type, File::reparse_point, FileType::new,
60 // FileType::is_symlink and FileType::is_symlink_dir (all from libstd/sys/windows/fs.rs).
61 fn symlink_type(handle
: HANDLE
) -> io
::Result
<SymlinkType
> {
63 let mut info
: c
::BY_HANDLE_FILE_INFORMATION
= mem
::zeroed();
64 if c
::GetFileInformationByHandle(handle
, &mut info
) == 0 {
65 return Err(io
::Error
::last_os_error());
67 if info
.dwFileAttributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
68 let mut space
= [0; c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
];
70 if c
::DeviceIoControl(handle
,
71 c
::FSCTL_GET_REPARSE_POINT
,
74 space
.as_mut_ptr() as *mut _
,
75 space
.len() as c
::DWORD
,
77 ptr
::null_mut()) != 0 {
78 let buf
= &*(space
.as_ptr() as *const c
::REPARSE_DATA_BUFFER
);
79 return Ok(match (info
.dwFileAttributes
& c
::FILE_ATTRIBUTE_DIRECTORY
!= 0,
80 info
.dwFileAttributes
& c
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0,
82 (_
, false, _
) => SymlinkType
::Not
,
83 (false, true, c
::IO_REPARSE_TAG_SYMLINK
) => SymlinkType
::File
,
84 (true, true, c
::IO_REPARSE_TAG_SYMLINK
) => SymlinkType
::Dir
,
85 (true, true, c
::IO_REPARSE_TAG_MOUNT_POINT
) => SymlinkType
::Dir
,
86 (_
, true, _
) => SymlinkType
::Not
,
95 let path
= to_u16s(path
)?
;
97 c
::CreateFileW(path
.as_ptr(),
99 c
::FILE_SHARE_READ
| c
::FILE_SHARE_WRITE
| c
::FILE_SHARE_DELETE
,
102 c
::FILE_FLAG_BACKUP_SEMANTICS
,
105 if handle
== c
::INVALID_HANDLE_VALUE
{
106 Err(Error
::last_os_error())
108 let out
= symlink_type(handle
);
109 unsafe { let _
= c
::CloseHandle(handle
); }