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.
13 use os
::windows
::prelude
::*;
17 use io
::{self, Error, SeekFrom}
;
18 use libc
::{self, HANDLE}
;
20 use path
::{Path, PathBuf}
;
24 use sys
::handle
::Handle
;
26 use sys_common
::FromInner
;
29 pub struct File { handle: Handle }
32 data
: c
::WIN32_FILE_ATTRIBUTE_DATA
,
36 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
38 Dir
, File
, Symlink
, ReparsePoint
42 handle
: FindNextFileHandle
,
44 first
: Option
<libc
::WIN32_FIND_DATAW
>,
47 struct FindNextFileHandle(libc
::HANDLE
);
49 unsafe impl Send
for FindNextFileHandle {}
50 unsafe impl Sync
for FindNextFileHandle {}
54 data
: libc
::WIN32_FIND_DATAW
,
57 #[derive(Clone, Default)]
58 pub struct OpenOptions
{
64 desired_access
: Option
<libc
::DWORD
>,
65 share_mode
: Option
<libc
::DWORD
>,
66 creation_disposition
: Option
<libc
::DWORD
>,
67 flags_and_attributes
: Option
<libc
::DWORD
>,
68 security_attributes
: usize, // *mut T doesn't have a Default impl
71 #[derive(Clone, PartialEq, Eq, Debug)]
72 pub struct FilePermissions { attrs: libc::DWORD }
74 pub struct DirBuilder
;
76 impl Iterator
for ReadDir
{
77 type Item
= io
::Result
<DirEntry
>;
78 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
79 if let Some(first
) = self.first
.take() {
80 if let Some(e
) = DirEntry
::new(&self.root
, &first
) {
85 let mut wfd
= mem
::zeroed();
87 if libc
::FindNextFileW(self.handle
.0, &mut wfd
) == 0 {
88 if libc
::GetLastError() ==
89 c
::ERROR_NO_MORE_FILES
as libc
::DWORD
{
92 return Some(Err(Error
::last_os_error()))
95 if let Some(e
) = DirEntry
::new(&self.root
, &wfd
) {
103 impl Drop
for FindNextFileHandle
{
105 let r
= unsafe { libc::FindClose(self.0) }
;
106 debug_assert
!(r
!= 0);
111 fn new(root
: &Arc
<PathBuf
>, wfd
: &libc
::WIN32_FIND_DATAW
) -> Option
<DirEntry
> {
112 match &wfd
.cFileName
[0..3] {
113 // check for '.' and '..'
115 [46, 46, 0, ..] => return None
,
125 pub fn path(&self) -> PathBuf
{
126 self.root
.join(&self.file_name())
129 pub fn file_name(&self) -> OsString
{
130 let filename
= super::truncate_utf16_at_nul(&self.data
.cFileName
);
131 OsString
::from_wide(filename
)
134 pub fn file_type(&self) -> io
::Result
<FileType
> {
135 Ok(FileType
::new(self.data
.dwFileAttributes
,
136 self.data
.dwReserved0
== c
::IO_REPARSE_TAG_SYMLINK
))
139 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
141 data
: c
::WIN32_FILE_ATTRIBUTE_DATA
{
142 dwFileAttributes
: self.data
.dwFileAttributes
,
143 ftCreationTime
: self.data
.ftCreationTime
,
144 ftLastAccessTime
: self.data
.ftLastAccessTime
,
145 ftLastWriteTime
: self.data
.ftLastWriteTime
,
146 nFileSizeHigh
: self.data
.nFileSizeHigh
,
147 nFileSizeLow
: self.data
.nFileSizeLow
,
149 is_symlink
: self.data
.dwReserved0
== c
::IO_REPARSE_TAG_SYMLINK
,
155 pub fn new() -> OpenOptions { Default::default() }
156 pub fn read(&mut self, read
: bool
) { self.read = read; }
157 pub fn write(&mut self, write
: bool
) { self.write = write; }
158 pub fn append(&mut self, append
: bool
) { self.append = append; }
159 pub fn create(&mut self, create
: bool
) { self.create = create; }
160 pub fn truncate(&mut self, truncate
: bool
) { self.truncate = truncate; }
161 pub fn creation_disposition(&mut self, val
: i32) {
162 self.creation_disposition
= Some(val
as libc
::DWORD
);
164 pub fn flags_and_attributes(&mut self, val
: i32) {
165 self.flags_and_attributes
= Some(val
as libc
::DWORD
);
167 pub fn desired_access(&mut self, val
: i32) {
168 self.desired_access
= Some(val
as libc
::DWORD
);
170 pub fn share_mode(&mut self, val
: i32) {
171 self.share_mode
= Some(val
as libc
::DWORD
);
173 pub fn security_attributes(&mut self, attrs
: libc
::LPSECURITY_ATTRIBUTES
) {
174 self.security_attributes
= attrs
as usize;
177 fn get_desired_access(&self) -> libc
::DWORD
{
178 self.desired_access
.unwrap_or({
179 let mut base
= if self.read {libc::FILE_GENERIC_READ}
else {0}
|
180 if self.write {libc::FILE_GENERIC_WRITE}
else {0}
;
182 base
&= !libc
::FILE_WRITE_DATA
;
183 base
|= libc
::FILE_APPEND_DATA
;
189 fn get_share_mode(&self) -> libc
::DWORD
{
190 // libuv has a good comment about this, but the basic idea is that
191 // we try to emulate unix semantics by enabling all sharing by
192 // allowing things such as deleting a file while it's still open.
193 self.share_mode
.unwrap_or(libc
::FILE_SHARE_READ
|
194 libc
::FILE_SHARE_WRITE
|
195 libc
::FILE_SHARE_DELETE
)
198 fn get_creation_disposition(&self) -> libc
::DWORD
{
199 self.creation_disposition
.unwrap_or({
200 match (self.create
, self.truncate
) {
201 (true, true) => libc
::CREATE_ALWAYS
,
202 (true, false) => libc
::OPEN_ALWAYS
,
203 (false, false) => libc
::OPEN_EXISTING
,
205 if self.write
&& !self.append
{
208 libc
::TRUNCATE_EXISTING
215 fn get_flags_and_attributes(&self) -> libc
::DWORD
{
216 self.flags_and_attributes
.unwrap_or(libc
::FILE_ATTRIBUTE_NORMAL
)
221 fn open_reparse_point(path
: &Path
) -> io
::Result
<File
> {
222 let mut opts
= OpenOptions
::new();
224 opts
.flags_and_attributes(c
::FILE_FLAG_OPEN_REPARSE_POINT
as i32);
225 File
::open(path
, &opts
)
228 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
229 let path
= to_utf16(path
);
230 let handle
= unsafe {
231 libc
::CreateFileW(path
.as_ptr(),
232 opts
.get_desired_access(),
233 opts
.get_share_mode(),
234 opts
.security_attributes
as *mut _
,
235 opts
.get_creation_disposition(),
236 opts
.get_flags_and_attributes(),
239 if handle
== libc
::INVALID_HANDLE_VALUE
{
240 Err(Error
::last_os_error())
242 Ok(File { handle: Handle::new(handle) }
)
246 pub fn fsync(&self) -> io
::Result
<()> {
247 try
!(cvt(unsafe { libc::FlushFileBuffers(self.handle.raw()) }
));
251 pub fn datasync(&self) -> io
::Result
<()> { self.fsync() }
253 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
254 let mut info
= c
::FILE_END_OF_FILE_INFO
{
255 EndOfFile
: size
as libc
::LARGE_INTEGER
,
257 let size
= mem
::size_of_val(&info
);
259 c
::SetFileInformationByHandle(self.handle
.raw(),
260 c
::FileEndOfFileInfo
,
261 &mut info
as *mut _
as *mut _
,
267 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
269 let mut info
: c
::BY_HANDLE_FILE_INFORMATION
= mem
::zeroed();
270 try
!(cvt(c
::GetFileInformationByHandle(self.handle
.raw(),
272 let mut attr
= FileAttr
{
273 data
: c
::WIN32_FILE_ATTRIBUTE_DATA
{
274 dwFileAttributes
: info
.dwFileAttributes
,
275 ftCreationTime
: info
.ftCreationTime
,
276 ftLastAccessTime
: info
.ftLastAccessTime
,
277 ftLastWriteTime
: info
.ftLastWriteTime
,
278 nFileSizeHigh
: info
.nFileSizeHigh
,
279 nFileSizeLow
: info
.nFileSizeLow
,
283 if attr
.is_reparse_point() {
284 attr
.is_symlink
= self.is_symlink();
290 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
291 self.handle
.read(buf
)
294 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
295 self.handle
.write(buf
)
298 pub fn flush(&self) -> io
::Result
<()> { Ok(()) }
300 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
301 let (whence
, pos
) = match pos
{
302 SeekFrom
::Start(n
) => (libc
::FILE_BEGIN
, n
as i64),
303 SeekFrom
::End(n
) => (libc
::FILE_END
, n
),
304 SeekFrom
::Current(n
) => (libc
::FILE_CURRENT
, n
),
306 let pos
= pos
as libc
::LARGE_INTEGER
;
309 libc
::SetFilePointerEx(self.handle
.raw(), pos
,
315 pub fn handle(&self) -> &Handle { &self.handle }
317 fn is_symlink(&self) -> bool
{
318 self.readlink().is_ok()
321 fn readlink(&self) -> io
::Result
<PathBuf
> {
322 let mut space
= [0u8; c
::MAXIMUM_REPARSE_DATA_BUFFER_SIZE
];
327 c
::DeviceIoControl(self.handle
.raw(),
328 c
::FSCTL_GET_REPARSE_POINT
,
331 space
.as_mut_ptr() as *mut _
,
332 space
.len() as libc
::DWORD
,
336 let buf
: *const c
::REPARSE_DATA_BUFFER
= space
.as_ptr() as *const _
;
337 if (*buf
).ReparseTag
!= c
::IO_REPARSE_TAG_SYMLINK
{
338 return Err(io
::Error
::new(io
::ErrorKind
::Other
, "not a symlink"))
340 let info
: *const c
::SYMBOLIC_LINK_REPARSE_BUFFER
=
341 &(*buf
).rest
as *const _
as *const _
;
342 let path_buffer
= &(*info
).PathBuffer
as *const _
as *const u16;
343 let subst_off
= (*info
).SubstituteNameOffset
/ 2;
344 let subst_ptr
= path_buffer
.offset(subst_off
as isize);
345 let subst_len
= (*info
).SubstituteNameLength
/ 2;
346 let subst
= slice
::from_raw_parts(subst_ptr
, subst_len
as usize);
348 Ok(PathBuf
::from(OsString
::from_wide(subst
)))
352 pub fn into_handle(self) -> Handle { self.handle }
355 impl FromInner
<libc
::HANDLE
> for File
{
356 fn from_inner(handle
: libc
::HANDLE
) -> File
{
357 File { handle: Handle::new(handle) }
361 impl fmt
::Debug
for File
{
362 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
363 // FIXME(#24570): add more info here (e.g. path, mode)
364 f
.debug_struct("File")
365 .field("handle", &self.handle
.raw())
370 pub fn to_utf16(s
: &Path
) -> Vec
<u16> {
371 s
.as_os_str().encode_wide().chain(Some(0).into_iter()).collect()
375 pub fn size(&self) -> u64 {
376 ((self.data
.nFileSizeHigh
as u64) << 32) | (self.data
.nFileSizeLow
as u64)
379 pub fn perm(&self) -> FilePermissions
{
380 FilePermissions { attrs: self.data.dwFileAttributes }
383 pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 }
385 pub fn file_type(&self) -> FileType
{
386 FileType
::new(self.data
.dwFileAttributes
, self.is_symlink
)
389 pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) }
390 pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) }
391 pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) }
393 fn to_u64(&self, ft
: &libc
::FILETIME
) -> u64 {
394 (ft
.dwLowDateTime
as u64) | ((ft
.dwHighDateTime
as u64) << 32)
397 fn is_reparse_point(&self) -> bool
{
398 self.data
.dwFileAttributes
& libc
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0
402 impl FilePermissions
{
403 pub fn readonly(&self) -> bool
{
404 self.attrs
& c
::FILE_ATTRIBUTE_READONLY
!= 0
407 pub fn set_readonly(&mut self, readonly
: bool
) {
409 self.attrs
|= c
::FILE_ATTRIBUTE_READONLY
;
411 self.attrs
&= !c
::FILE_ATTRIBUTE_READONLY
;
417 fn new(attrs
: libc
::DWORD
, is_symlink
: bool
) -> FileType
{
418 if attrs
& libc
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
422 FileType
::ReparsePoint
424 } else if attrs
& c
::FILE_ATTRIBUTE_DIRECTORY
!= 0 {
431 pub fn is_dir(&self) -> bool { *self == FileType::Dir }
432 pub fn is_file(&self) -> bool { *self == FileType::File }
433 pub fn is_symlink(&self) -> bool { *self == FileType::Symlink }
437 pub fn new() -> DirBuilder { DirBuilder }
439 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
442 libc
::CreateDirectoryW(p
.as_ptr(), ptr
::null_mut())
448 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
449 let root
= p
.to_path_buf();
450 let star
= p
.join("*");
451 let path
= to_utf16(&star
);
454 let mut wfd
= mem
::zeroed();
455 let find_handle
= libc
::FindFirstFileW(path
.as_ptr(), &mut wfd
);
456 if find_handle
!= libc
::INVALID_HANDLE_VALUE
{
458 handle
: FindNextFileHandle(find_handle
),
459 root
: Arc
::new(root
),
463 Err(Error
::last_os_error())
468 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
469 let p_utf16
= to_utf16(p
);
470 try
!(cvt(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) }
));
474 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
475 let old
= to_utf16(old
);
476 let new
= to_utf16(new
);
478 libc
::MoveFileExW(old
.as_ptr(), new
.as_ptr(),
479 libc
::MOVEFILE_REPLACE_EXISTING
)
484 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
486 try
!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }
));
490 pub fn readlink(p
: &Path
) -> io
::Result
<PathBuf
> {
491 let file
= try
!(File
::open_reparse_point(p
));
495 pub fn symlink(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
496 symlink_inner(src
, dst
, false)
499 pub fn symlink_inner(src
: &Path
, dst
: &Path
, dir
: bool
) -> io
::Result
<()> {
500 use sys
::c
::compat
::kernel32
::CreateSymbolicLinkW
;
501 let src
= to_utf16(src
);
502 let dst
= to_utf16(dst
);
503 let flags
= if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY }
else { 0 }
;
505 CreateSymbolicLinkW(dst
.as_ptr(), src
.as_ptr(), flags
) as libc
::BOOL
510 pub fn link(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
511 let src
= to_utf16(src
);
512 let dst
= to_utf16(dst
);
514 libc
::CreateHardLinkW(dst
.as_ptr(), src
.as_ptr(), ptr
::null_mut())
519 pub fn stat(p
: &Path
) -> io
::Result
<FileAttr
> {
520 let attr
= try
!(lstat(p
));
521 if attr
.data
.dwFileAttributes
& libc
::FILE_ATTRIBUTE_REPARSE_POINT
!= 0 {
522 let opts
= OpenOptions
::new();
523 let file
= try
!(File
::open(p
, &opts
));
530 pub fn lstat(p
: &Path
) -> io
::Result
<FileAttr
> {
531 let utf16
= to_utf16(p
);
533 let mut attr
: FileAttr
= mem
::zeroed();
534 try
!(cvt(c
::GetFileAttributesExW(utf16
.as_ptr(),
535 c
::GetFileExInfoStandard
,
536 &mut attr
.data
as *mut _
as *mut _
)));
537 if attr
.is_reparse_point() {
538 attr
.is_symlink
= File
::open_reparse_point(p
).map(|f
| {
546 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
549 try
!(cvt(c
::SetFileAttributesW(p
.as_ptr(), perm
.attrs
)));
554 pub fn utimes(p
: &Path
, atime
: u64, mtime
: u64) -> io
::Result
<()> {
555 let atime
= super::ms_to_filetime(atime
);
556 let mtime
= super::ms_to_filetime(mtime
);
558 let mut o
= OpenOptions
::new();
560 let f
= try
!(File
::open(p
, &o
));
562 c
::SetFileTime(f
.handle
.raw(), 0 as *const _
, &atime
, &mtime
)
567 pub fn canonicalize(p
: &Path
) -> io
::Result
<PathBuf
> {
568 use sys
::c
::compat
::kernel32
::GetFinalPathNameByHandleW
;
570 let mut opts
= OpenOptions
::new();
572 let f
= try
!(File
::open(p
, &opts
));
573 super::fill_utf16_buf(|buf
, sz
| unsafe {
574 GetFinalPathNameByHandleW(f
.handle
.raw(), buf
, sz
,
575 libc
::VOLUME_NAME_DOS
)
577 PathBuf
::from(OsString
::from_wide(buf
))