1 use std::ffi::OsStr;
2 use std::fs::{File, OpenOptions};
3 use std::os::windows::ffi::OsStrExt;
4 use std::os::windows::fs::OpenOptionsExt;
5 use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
6 use std::path::Path;
7 use std::{io, iter};
8
9 use winapi::um::fileapi::SetFileAttributesW;
10 use winapi::um::handleapi::INVALID_HANDLE_VALUE;
11 use winapi::um::winbase::{MoveFileExW, ReOpenFile};
12 use winapi::um::winbase::{FILE_FLAG_DELETE_ON_CLOSE, MOVEFILE_REPLACE_EXISTING};
13 use winapi::um::winnt::{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 crate::util;
18
to_utf16(s: &Path) -> Vec<u16>19 fn to_utf16(s: &Path) -> Vec<u16> {
20 s.as_os_str().encode_wide().chain(iter::once(0)).collect()
21 }
22
create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File>23 pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
24 open_options
25 .create_new(true)
26 .read(true)
27 .write(true)
28 .custom_flags(FILE_ATTRIBUTE_TEMPORARY)
29 .open(path)
30 }
31
create(dir: &Path) -> io::Result<File>32 pub fn create(dir: &Path) -> io::Result<File> {
33 util::create_helper(
34 dir,
35 OsStr::new(".tmp"),
36 OsStr::new(""),
37 crate::NUM_RAND_CHARS,
38 |path| {
39 OpenOptions::new()
40 .create_new(true)
41 .read(true)
42 .write(true)
43 .share_mode(0)
44 .custom_flags(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE)
45 .open(path)
46 },
47 )
48 }
49
reopen(file: &File, _path: &Path) -> io::Result<File>50 pub fn reopen(file: &File, _path: &Path) -> io::Result<File> {
51 let handle = file.as_raw_handle();
52 unsafe {
53 let handle = ReOpenFile(
54 handle as HANDLE,
55 FILE_GENERIC_READ | FILE_GENERIC_WRITE,
56 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
57 0,
58 );
59 if handle == INVALID_HANDLE_VALUE {
60 Err(io::Error::last_os_error())
61 } else {
62 Ok(FromRawHandle::from_raw_handle(handle as RawHandle))
63 }
64 }
65 }
66
keep(path: &Path) -> io::Result<()>67 pub fn keep(path: &Path) -> io::Result<()> {
68 unsafe {
69 let path_w = to_utf16(path);
70 if SetFileAttributesW(path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 {
71 Err(io::Error::last_os_error())
72 } else {
73 Ok(())
74 }
75 }
76 }
77
persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()>78 pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
79 // TODO: We should probably do this in one-shot using SetFileInformationByHandle but the API is
80 // really painful.
81
82 unsafe {
83 let old_path_w = to_utf16(old_path);
84 let new_path_w = to_utf16(new_path);
85
86 // Don't succeed if this fails. We don't want to claim to have successfully persisted a file
87 // still marked as temporary because this file won't have the same consistency guarantees.
88 if SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 {
89 return Err(io::Error::last_os_error());
90 }
91
92 let mut flags = 0;
93
94 if overwrite {
95 flags |= MOVEFILE_REPLACE_EXISTING;
96 }
97
98 if MoveFileExW(old_path_w.as_ptr(), new_path_w.as_ptr(), flags) == 0 {
99 let e = io::Error::last_os_error();
100 // If this fails, the temporary file is now un-hidden and no longer marked temporary
101 // (slightly less efficient) but it will still work.
102 let _ = SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_TEMPORARY);
103 Err(e)
104 } else {
105 Ok(())
106 }
107 }
108 }
109