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 windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
10 use windows_sys::Win32::Storage::FileSystem::{
11 MoveFileExW, ReOpenFile, SetFileAttributesW, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY,
12 FILE_FLAG_DELETE_ON_CLOSE, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_SHARE_DELETE,
13 FILE_SHARE_READ, FILE_SHARE_WRITE, MOVEFILE_REPLACE_EXISTING,
14 };
15
16 use crate::util;
17
to_utf16(s: &Path) -> Vec<u16>18 fn to_utf16(s: &Path) -> Vec<u16> {
19 s.as_os_str().encode_wide().chain(iter::once(0)).collect()
20 }
21
not_supported<T>(msg: &str) -> io::Result<T>22 fn not_supported<T>(msg: &str) -> io::Result<T> {
23 Err(io::Error::new(io::ErrorKind::Other, msg))
24 }
25
create_named( path: &Path, open_options: &mut OpenOptions, permissions: Option<&std::fs::Permissions>, ) -> io::Result<File>26 pub fn create_named(
27 path: &Path,
28 open_options: &mut OpenOptions,
29 permissions: Option<&std::fs::Permissions>,
30 ) -> io::Result<File> {
31 if permissions.map_or(false, |p| p.readonly()) {
32 return not_supported("changing permissions is not supported on this platform");
33 }
34 open_options
35 .create_new(true)
36 .read(true)
37 .write(true)
38 .custom_flags(FILE_ATTRIBUTE_TEMPORARY)
39 .open(path)
40 }
41
create(dir: &Path) -> io::Result<File>42 pub fn create(dir: &Path) -> io::Result<File> {
43 util::create_helper(
44 dir,
45 OsStr::new(".tmp"),
46 OsStr::new(""),
47 crate::NUM_RAND_CHARS,
48 |path| {
49 OpenOptions::new()
50 .create_new(true)
51 .read(true)
52 .write(true)
53 .share_mode(0)
54 .custom_flags(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE)
55 .open(path)
56 },
57 )
58 }
59
reopen(file: &File, _path: &Path) -> io::Result<File>60 pub fn reopen(file: &File, _path: &Path) -> io::Result<File> {
61 let handle = file.as_raw_handle();
62 unsafe {
63 let handle = ReOpenFile(
64 handle as HANDLE,
65 FILE_GENERIC_READ | FILE_GENERIC_WRITE,
66 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
67 0,
68 );
69 if handle == INVALID_HANDLE_VALUE {
70 Err(io::Error::last_os_error())
71 } else {
72 Ok(FromRawHandle::from_raw_handle(handle as RawHandle))
73 }
74 }
75 }
76
keep(path: &Path) -> io::Result<()>77 pub fn keep(path: &Path) -> io::Result<()> {
78 unsafe {
79 let path_w = to_utf16(path);
80 if SetFileAttributesW(path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 {
81 Err(io::Error::last_os_error())
82 } else {
83 Ok(())
84 }
85 }
86 }
87
persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()>88 pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
89 unsafe {
90 let old_path_w = to_utf16(old_path);
91 let new_path_w = to_utf16(new_path);
92
93 // Don't succeed if this fails. We don't want to claim to have successfully persisted a file
94 // still marked as temporary because this file won't have the same consistency guarantees.
95 if SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 {
96 return Err(io::Error::last_os_error());
97 }
98
99 let mut flags = 0;
100
101 if overwrite {
102 flags |= MOVEFILE_REPLACE_EXISTING;
103 }
104
105 if MoveFileExW(old_path_w.as_ptr(), new_path_w.as_ptr(), flags) == 0 {
106 let e = io::Error::last_os_error();
107 // If this fails, the temporary file is now un-hidden and no longer marked temporary
108 // (slightly less efficient) but it will still work.
109 let _ = SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_TEMPORARY);
110 Err(e)
111 } else {
112 Ok(())
113 }
114 }
115 }
116