]> git.proxmox.com Git - rustc.git/blob - vendor/tempfile/src/file/imp/windows.rs
New upstream version 1.32.0~beta.2+dfsg1
[rustc.git] / vendor / tempfile / src / file / imp / windows.rs
1 use std::fs::File;
2 use std::io;
3 use std::os::windows::ffi::OsStrExt;
4 use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
5 use std::path::Path;
6 use std::ptr;
7
8 use winapi::shared::minwindef::DWORD;
9 use winapi::um::fileapi::{CreateFileW, SetFileAttributesW, CREATE_NEW};
10 use winapi::um::handleapi::INVALID_HANDLE_VALUE;
11 use winapi::um::winbase::{FILE_FLAG_DELETE_ON_CLOSE, MOVEFILE_REPLACE_EXISTING};
12 use winapi::um::winbase::{MoveFileExW, ReOpenFile};
13 use winapi::um::winnt::{FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY};
14 use winapi::um::winnt::{FILE_GENERIC_READ, FILE_GENERIC_WRITE, HANDLE};
15 use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};
16
17 use util;
18
19 #[cfg_attr(irustfmt, rustfmt_skip)]
20 const ACCESS: DWORD = FILE_GENERIC_READ
21 | FILE_GENERIC_WRITE;
22 #[cfg_attr(irustfmt, rustfmt_skip)]
23 const SHARE_MODE: DWORD = FILE_SHARE_DELETE
24 | FILE_SHARE_READ
25 | FILE_SHARE_WRITE;
26 #[cfg_attr(irustfmt, rustfmt_skip)]
27 const FLAGS: DWORD = FILE_ATTRIBUTE_HIDDEN
28 | FILE_ATTRIBUTE_TEMPORARY;
29
30 fn to_utf16(s: &Path) -> Vec<u16> {
31 s.as_os_str()
32 .encode_wide()
33 .chain(Some(0).into_iter())
34 .collect()
35 }
36
37 fn win_create(
38 path: &Path,
39 access: DWORD,
40 share_mode: DWORD,
41 disp: DWORD,
42 flags: DWORD,
43 ) -> io::Result<File> {
44 let path = to_utf16(path);
45 let handle = unsafe {
46 CreateFileW(
47 path.as_ptr(),
48 access,
49 share_mode,
50 0 as *mut _,
51 disp,
52 flags,
53 ptr::null_mut(),
54 )
55 };
56 if handle == INVALID_HANDLE_VALUE {
57 Err(io::Error::last_os_error())
58 } else {
59 Ok(unsafe { File::from_raw_handle(handle as RawHandle) })
60 }
61 }
62
63 pub fn create_named(path: &Path) -> io::Result<File> {
64 win_create(path, ACCESS, SHARE_MODE, CREATE_NEW, FLAGS)
65 }
66
67 pub fn create(dir: &Path) -> io::Result<File> {
68 util::create_helper(dir, ".tmp", "", ::NUM_RAND_CHARS, |path| {
69 win_create(
70 &path,
71 ACCESS,
72 0, // Exclusive
73 CREATE_NEW,
74 FLAGS | FILE_FLAG_DELETE_ON_CLOSE,
75 )
76 })
77 }
78
79 pub fn reopen(file: &File, _path: &Path) -> io::Result<File> {
80 let handle = file.as_raw_handle();
81 unsafe {
82 let handle = ReOpenFile(handle as HANDLE, ACCESS, SHARE_MODE, 0);
83 if handle == INVALID_HANDLE_VALUE {
84 Err(io::Error::last_os_error())
85 } else {
86 Ok(FromRawHandle::from_raw_handle(handle as RawHandle))
87 }
88 }
89 }
90
91 pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
92 // TODO: We should probably do this in one-shot using SetFileInformationByHandle but the API is
93 // really painful.
94
95 unsafe {
96 let old_path_w = to_utf16(old_path);
97 let new_path_w = to_utf16(new_path);
98
99 // Don't succeed if this fails. We don't want to claim to have successfully persisted a file
100 // still marked as temporary because this file won't have the same consistency guarantees.
101 if SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 {
102 return Err(io::Error::last_os_error());
103 }
104
105 let mut flags = 0;
106
107 if overwrite {
108 flags |= MOVEFILE_REPLACE_EXISTING;
109 }
110
111 if MoveFileExW(old_path_w.as_ptr(), new_path_w.as_ptr(), flags) == 0 {
112 let e = io::Error::last_os_error();
113 // If this fails, the temporary file is now un-hidden and no longer marked temporary
114 // (slightly less efficient) but it will still work.
115 let _ = SetFileAttributesW(old_path_w.as_ptr(), FLAGS);
116 Err(e)
117 } else {
118 Ok(())
119 }
120 }
121 }