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};
cvt_err(result: c_int) -> io::Result<c_int>15 use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};
16 
17 use crate::util;
18 
19 fn to_utf16(s: &Path) -> Vec<u16> {
20     s.as_os_str().encode_wide().chain(iter::once(0)).collect()
21 }
22 
23 pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
24     open_options
cvt_err(result: Result<usize, syscall::Error>) -> io::Result<usize>25         .create_new(true)
26         .read(true)
27         .write(true)
28         .custom_flags(FILE_ATTRIBUTE_TEMPORARY)
29         .open(path)
30 }
31 
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 
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 {
create(dir: &Path) -> io::Result<File>62             Ok(FromRawHandle::from_raw_handle(handle as RawHandle))
63         }
64     }
65 }
66 
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 
78 pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
create(dir: &Path) -> io::Result<File>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