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
::*;
16 use ffi
::{OsString, AsOsStr}
;
17 use io
::{self, Error, SeekFrom}
;
18 use libc
::{self, HANDLE}
;
20 use path
::{Path, PathBuf}
;
23 use sys
::handle
::Handle
;
25 use sys_common
::FromInner
;
28 pub struct File { handle: Handle }
29 pub struct FileAttr { data: c::WIN32_FILE_ATTRIBUTE_DATA }
32 handle
: FindNextFileHandle
,
34 first
: Option
<libc
::WIN32_FIND_DATAW
>,
37 struct FindNextFileHandle(libc
::HANDLE
);
39 unsafe impl Send
for FindNextFileHandle {}
40 unsafe impl Sync
for FindNextFileHandle {}
44 data
: libc
::WIN32_FIND_DATAW
,
47 #[derive(Clone, Default)]
48 pub struct OpenOptions
{
54 desired_access
: Option
<libc
::DWORD
>,
55 share_mode
: Option
<libc
::DWORD
>,
56 creation_disposition
: Option
<libc
::DWORD
>,
57 flags_and_attributes
: Option
<libc
::DWORD
>,
60 #[derive(Clone, PartialEq, Eq, Debug)]
61 pub struct FilePermissions { attrs: libc::DWORD }
63 impl Iterator
for ReadDir
{
64 type Item
= io
::Result
<DirEntry
>;
65 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
66 if let Some(first
) = self.first
.take() {
67 if let Some(e
) = DirEntry
::new(&self.root
, &first
) {
72 let mut wfd
= mem
::zeroed();
74 if libc
::FindNextFileW(self.handle
.0, &mut wfd
) == 0 {
75 if libc
::GetLastError() ==
76 c
::ERROR_NO_MORE_FILES
as libc
::DWORD
{
79 return Some(Err(Error
::last_os_error()))
82 if let Some(e
) = DirEntry
::new(&self.root
, &wfd
) {
90 impl Drop
for FindNextFileHandle
{
92 let r
= unsafe { libc::FindClose(self.0) }
;
93 debug_assert
!(r
!= 0);
98 fn new(root
: &Arc
<PathBuf
>, wfd
: &libc
::WIN32_FIND_DATAW
) -> Option
<DirEntry
> {
99 match &wfd
.cFileName
[0..3] {
100 // check for '.' and '..'
102 [46, 46, 0, ..] => return None
,
112 pub fn path(&self) -> PathBuf
{
113 let filename
= super::truncate_utf16_at_nul(&self.data
.cFileName
);
114 self.root
.join(&<OsString
as OsStringExt
>::from_wide(filename
))
119 pub fn new() -> OpenOptions { Default::default() }
120 pub fn read(&mut self, read
: bool
) { self.read = read; }
121 pub fn write(&mut self, write
: bool
) { self.write = write; }
122 pub fn append(&mut self, append
: bool
) { self.append = append; }
123 pub fn create(&mut self, create
: bool
) { self.create = create; }
124 pub fn truncate(&mut self, truncate
: bool
) { self.truncate = truncate; }
125 pub fn creation_disposition(&mut self, val
: i32) {
126 self.creation_disposition
= Some(val
as libc
::DWORD
);
128 pub fn flags_and_attributes(&mut self, val
: i32) {
129 self.flags_and_attributes
= Some(val
as libc
::DWORD
);
131 pub fn desired_access(&mut self, val
: i32) {
132 self.desired_access
= Some(val
as libc
::DWORD
);
134 pub fn share_mode(&mut self, val
: i32) {
135 self.share_mode
= Some(val
as libc
::DWORD
);
138 fn get_desired_access(&self) -> libc
::DWORD
{
139 self.desired_access
.unwrap_or({
140 let mut base
= if self.read {libc::FILE_GENERIC_READ}
else {0}
|
141 if self.write {libc::FILE_GENERIC_WRITE}
else {0}
;
143 base
&= !libc
::FILE_WRITE_DATA
;
144 base
|= libc
::FILE_APPEND_DATA
;
150 fn get_share_mode(&self) -> libc
::DWORD
{
151 // libuv has a good comment about this, but the basic idea is that
152 // we try to emulate unix semantics by enabling all sharing by
153 // allowing things such as deleting a file while it's still open.
154 self.share_mode
.unwrap_or(libc
::FILE_SHARE_READ
|
155 libc
::FILE_SHARE_WRITE
|
156 libc
::FILE_SHARE_DELETE
)
159 fn get_creation_disposition(&self) -> libc
::DWORD
{
160 self.creation_disposition
.unwrap_or({
161 match (self.create
, self.truncate
) {
162 (true, true) => libc
::CREATE_ALWAYS
,
163 (true, false) => libc
::OPEN_ALWAYS
,
164 (false, false) => libc
::OPEN_EXISTING
,
166 if self.write
&& !self.append
{
169 libc
::TRUNCATE_EXISTING
176 fn get_flags_and_attributes(&self) -> libc
::DWORD
{
177 self.flags_and_attributes
.unwrap_or(libc
::FILE_ATTRIBUTE_NORMAL
)
182 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
183 let path
= to_utf16(path
);
184 let handle
= unsafe {
185 libc
::CreateFileW(path
.as_ptr(),
186 opts
.get_desired_access(),
187 opts
.get_share_mode(),
189 opts
.get_creation_disposition(),
190 opts
.get_flags_and_attributes(),
193 if handle
== libc
::INVALID_HANDLE_VALUE
{
194 Err(Error
::last_os_error())
196 Ok(File { handle: Handle::new(handle) }
)
200 pub fn fsync(&self) -> io
::Result
<()> {
201 try
!(cvt(unsafe { libc::FlushFileBuffers(self.handle.raw()) }
));
205 pub fn datasync(&self) -> io
::Result
<()> { self.fsync() }
207 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
208 let mut info
= c
::FILE_END_OF_FILE_INFO
{
209 EndOfFile
: size
as libc
::LARGE_INTEGER
,
211 let size
= mem
::size_of_val(&info
);
213 c
::SetFileInformationByHandle(self.handle
.raw(),
214 c
::FileEndOfFileInfo
,
215 &mut info
as *mut _
as *mut _
,
221 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
223 let mut info
: c
::BY_HANDLE_FILE_INFORMATION
= mem
::zeroed();
224 try
!(cvt(c
::GetFileInformationByHandle(self.handle
.raw(),
227 data
: c
::WIN32_FILE_ATTRIBUTE_DATA
{
228 dwFileAttributes
: info
.dwFileAttributes
,
229 ftCreationTime
: info
.ftCreationTime
,
230 ftLastAccessTime
: info
.ftLastAccessTime
,
231 ftLastWriteTime
: info
.ftLastWriteTime
,
232 nFileSizeHigh
: info
.nFileSizeHigh
,
233 nFileSizeLow
: info
.nFileSizeLow
,
239 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
240 self.handle
.read(buf
)
243 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
244 self.handle
.write(buf
)
247 pub fn flush(&self) -> io
::Result
<()> { Ok(()) }
249 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
250 let (whence
, pos
) = match pos
{
251 SeekFrom
::Start(n
) => (libc
::FILE_BEGIN
, n
as i64),
252 SeekFrom
::End(n
) => (libc
::FILE_END
, n
),
253 SeekFrom
::Current(n
) => (libc
::FILE_CURRENT
, n
),
255 let pos
= pos
as libc
::LARGE_INTEGER
;
258 libc
::SetFilePointerEx(self.handle
.raw(), pos
,
264 pub fn handle(&self) -> &Handle { &self.handle }
267 impl FromInner
<libc
::HANDLE
> for File
{
268 fn from_inner(handle
: libc
::HANDLE
) -> File
{
269 File { handle: Handle::new(handle) }
273 pub fn to_utf16(s
: &Path
) -> Vec
<u16> {
274 s
.as_os_str().encode_wide().chain(Some(0).into_iter()).collect()
278 pub fn is_dir(&self) -> bool
{
279 self.data
.dwFileAttributes
& c
::FILE_ATTRIBUTE_DIRECTORY
!= 0
281 pub fn is_file(&self) -> bool
{
284 pub fn size(&self) -> u64 {
285 ((self.data
.nFileSizeHigh
as u64) << 32) | (self.data
.nFileSizeLow
as u64)
287 pub fn perm(&self) -> FilePermissions
{
288 FilePermissions { attrs: self.data.dwFileAttributes }
291 pub fn accessed(&self) -> u64 { self.to_ms(&self.data.ftLastAccessTime) }
292 pub fn modified(&self) -> u64 { self.to_ms(&self.data.ftLastWriteTime) }
294 fn to_ms(&self, ft
: &libc
::FILETIME
) -> u64 {
295 // FILETIME is in 100ns intervals and there are 10000 intervals in a
297 let bits
= (ft
.dwLowDateTime
as u64) | ((ft
.dwHighDateTime
as u64) << 32);
302 impl FilePermissions
{
303 pub fn readonly(&self) -> bool
{
304 self.attrs
& c
::FILE_ATTRIBUTE_READONLY
!= 0
307 pub fn set_readonly(&mut self, readonly
: bool
) {
309 self.attrs
|= c
::FILE_ATTRIBUTE_READONLY
;
311 self.attrs
&= !c
::FILE_ATTRIBUTE_READONLY
;
316 pub fn mkdir(p
: &Path
) -> io
::Result
<()> {
319 libc
::CreateDirectoryW(p
.as_ptr(), ptr
::null_mut())
324 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
325 let root
= p
.to_path_buf();
326 let star
= p
.join("*");
327 let path
= to_utf16(&star
);
330 let mut wfd
= mem
::zeroed();
331 let find_handle
= libc
::FindFirstFileW(path
.as_ptr(), &mut wfd
);
332 if find_handle
!= libc
::INVALID_HANDLE_VALUE
{
334 handle
: FindNextFileHandle(find_handle
),
335 root
: Arc
::new(root
),
339 Err(Error
::last_os_error())
344 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
345 let p_utf16
= to_utf16(p
);
346 try
!(cvt(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) }
));
350 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
351 let old
= to_utf16(old
);
352 let new
= to_utf16(new
);
354 libc
::MoveFileExW(old
.as_ptr(), new
.as_ptr(),
355 libc
::MOVEFILE_REPLACE_EXISTING
)
360 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
362 try
!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }
));
366 pub fn readlink(p
: &Path
) -> io
::Result
<PathBuf
> {
367 use sys
::c
::compat
::kernel32
::GetFinalPathNameByHandleW
;
368 let mut opts
= OpenOptions
::new();
370 let file
= try
!(File
::open(p
, &opts
));;
372 // Specify (sz - 1) because the documentation states that it's the size
373 // without the null pointer
375 // FIXME: I have a feeling that this reads intermediate symlinks as well.
376 let ret
: OsString
= try
!(super::fill_utf16_buf_new(|buf
, sz
| unsafe {
377 GetFinalPathNameByHandleW(file
.handle
.raw(),
380 libc
::VOLUME_NAME_DOS
)
381 }, |s
| OsStringExt
::from_wide(s
)));
382 Ok(PathBuf
::from(&ret
))
385 pub fn symlink(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
386 use sys
::c
::compat
::kernel32
::CreateSymbolicLinkW
;
387 let src
= to_utf16(src
);
388 let dst
= to_utf16(dst
);
390 CreateSymbolicLinkW(dst
.as_ptr(), src
.as_ptr(), 0) as libc
::BOOL
395 pub fn link(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
396 let src
= to_utf16(src
);
397 let dst
= to_utf16(dst
);
399 libc
::CreateHardLinkW(dst
.as_ptr(), src
.as_ptr(), ptr
::null_mut())
404 pub fn stat(p
: &Path
) -> io
::Result
<FileAttr
> {
407 let mut attr
: FileAttr
= mem
::zeroed();
408 try
!(cvt(c
::GetFileAttributesExW(p
.as_ptr(),
409 c
::GetFileExInfoStandard
,
410 &mut attr
.data
as *mut _
as *mut _
)));
415 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
418 try
!(cvt(c
::SetFileAttributesW(p
.as_ptr(), perm
.attrs
)));
423 pub fn utimes(p
: &Path
, atime
: u64, mtime
: u64) -> io
::Result
<()> {
424 let atime
= super::ms_to_filetime(atime
);
425 let mtime
= super::ms_to_filetime(mtime
);
427 let mut o
= OpenOptions
::new();
429 let f
= try
!(File
::open(p
, &o
));
431 c
::SetFileTime(f
.handle
.raw(), 0 as *const _
, &atime
, &mtime
)