1use crate::common::fileutils::{
2 windows::{get_file_information_by_name, FILE_INFO_BY_NAME_CLASS},
3 StatStruct,
4};
5use crate::{
6 convert::{ToPyObject, ToPyResult},
7 stdlib::os::errno_err,
8 PyObjectRef, PyResult, TryFromObject, VirtualMachine,
9};
10use std::{ffi::OsStr, time::SystemTime};
11use windows::Win32::Foundation::HANDLE;
12use windows_sys::Win32::Foundation::{BOOL, HANDLE as RAW_HANDLE, INVALID_HANDLE_VALUE};
13
14pub(crate) trait WindowsSysResultValue {
15 type Ok: ToPyObject;
16 fn is_err(&self) -> bool;
17 fn into_ok(self) -> Self::Ok;
18}
19
20impl WindowsSysResultValue for RAW_HANDLE {
21 type Ok = HANDLE;
22 fn is_err(&self) -> bool {
23 *self == INVALID_HANDLE_VALUE
24 }
25 fn into_ok(self) -> Self::Ok {
26 HANDLE(self)
27 }
28}
29
30impl WindowsSysResultValue for BOOL {
31 type Ok = ();
32 fn is_err(&self) -> bool {
33 *self == 0
34 }
35 fn into_ok(self) -> Self::Ok {}
36}
37
38pub(crate) struct WindowsSysResult<T>(pub T);
39
40impl<T: WindowsSysResultValue> WindowsSysResult<T> {
41 pub fn is_err(&self) -> bool {
42 self.0.is_err()
43 }
44 pub fn into_pyresult(self, vm: &VirtualMachine) -> PyResult<T::Ok> {
45 if self.is_err() {
46 Err(errno_err(vm))
47 } else {
48 Ok(self.0.into_ok())
49 }
50 }
51}
52
53impl<T: WindowsSysResultValue> ToPyResult for WindowsSysResult<T> {
54 fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
55 let ok = self.into_pyresult(vm)?;
56 Ok(ok.to_pyobject(vm))
57 }
58}
59
60type HandleInt = usize; impl TryFromObject for HANDLE {
63 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
64 let handle = HandleInt::try_from_object(vm, obj)?;
65 Ok(HANDLE(handle as isize))
66 }
67}
68
69impl ToPyObject for HANDLE {
70 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
71 (self.0 as HandleInt).to_pyobject(vm)
72 }
73}
74
75pub fn init_winsock() {
76 static WSA_INIT: parking_lot::Once = parking_lot::Once::new();
77 WSA_INIT.call_once(|| unsafe {
78 let mut wsa_data = std::mem::MaybeUninit::uninit();
79 let _ = windows_sys::Win32::Networking::WinSock::WSAStartup(0x0101, wsa_data.as_mut_ptr());
80 })
81}
82
83pub fn win32_xstat(path: &OsStr, traverse: bool) -> std::io::Result<StatStruct> {
85 let mut result = win32_xstat_impl(path, traverse)?;
86 result.st_ctime = result.st_birthtime;
88 result.st_ctime_nsec = result.st_birthtime_nsec;
89 Ok(result)
90}
91
92fn is_reparse_tag_name_surrogate(tag: u32) -> bool {
93 (tag & 0x20000000) > 0
94}
95
96fn win32_xstat_impl(path: &OsStr, traverse: bool) -> std::io::Result<StatStruct> {
97 use windows_sys::Win32::{Foundation, Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT};
98
99 let stat_info =
100 get_file_information_by_name(path, FILE_INFO_BY_NAME_CLASS::FileStatBasicByNameInfo);
101 match stat_info {
102 Ok(stat_info) => {
103 if (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT == 0)
104 || (!traverse && is_reparse_tag_name_surrogate(stat_info.ReparseTag))
105 {
106 let mut result =
107 crate::common::fileutils::windows::stat_basic_info_to_stat(&stat_info);
108 result.update_st_mode_from_path(path, stat_info.FileAttributes);
109 return Ok(result);
110 }
111 }
112 Err(e) => {
113 if let Some(errno) = e.raw_os_error() {
114 if matches!(
115 errno as u32,
116 Foundation::ERROR_FILE_NOT_FOUND
117 | Foundation::ERROR_PATH_NOT_FOUND
118 | Foundation::ERROR_NOT_READY
119 | Foundation::ERROR_BAD_NET_NAME
120 ) {
121 return Err(e);
122 }
123 }
124 }
125 }
126
127 meta_to_stat(
129 &crate::stdlib::os::fs_metadata(path, traverse)?,
130 file_id(path)?,
131 )
132}
133
134fn file_id(path: &OsStr) -> std::io::Result<u64> {
138 use std::os::windows::{fs::OpenOptionsExt, io::AsRawHandle};
139 use windows_sys::Win32::{
140 Foundation::HANDLE,
141 Storage::FileSystem::{
142 GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, FILE_FLAG_BACKUP_SEMANTICS,
143 },
144 };
145
146 let file = std::fs::OpenOptions::new()
147 .read(true)
148 .custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
149 .open(path)?;
150
151 let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() };
152 let ret = unsafe { GetFileInformationByHandle(file.as_raw_handle() as HANDLE, &mut info) };
155 if ret == 0 {
156 return Err(std::io::Error::last_os_error());
157 };
158
159 Ok(((info.nFileIndexHigh as u64) << 32) | (info.nFileIndexLow as u64))
160}
161
162fn meta_to_stat(meta: &std::fs::Metadata, file_id: u64) -> std::io::Result<StatStruct> {
163 let st_mode = {
164 let mut m = 0;
166 if meta.is_dir() {
167 m |= libc::S_IFDIR | 0o111; } else {
169 m |= libc::S_IFREG;
170 }
171 if meta.is_symlink() {
172 m |= 0o100000;
173 }
174 if meta.permissions().readonly() {
175 m |= 0o444;
176 } else {
177 m |= 0o666;
178 }
179 m as _
180 };
181 let (atime, mtime, ctime) = (meta.accessed()?, meta.modified()?, meta.created()?);
182 let sec = |systime: SystemTime| match systime.duration_since(SystemTime::UNIX_EPOCH) {
183 Ok(d) => d.as_secs() as libc::time_t,
184 Err(e) => -(e.duration().as_secs() as libc::time_t),
185 };
186 let nsec = |systime: SystemTime| match systime.duration_since(SystemTime::UNIX_EPOCH) {
187 Ok(d) => d.subsec_nanos() as i32,
188 Err(e) => -(e.duration().subsec_nanos() as i32),
189 };
190 Ok(StatStruct {
191 st_dev: 0,
192 st_ino: file_id,
193 st_mode,
194 st_nlink: 0,
195 st_uid: 0,
196 st_gid: 0,
197 st_size: meta.len(),
198 st_atime: sec(atime),
199 st_mtime: sec(mtime),
200 st_birthtime: sec(ctime),
201 st_atime_nsec: nsec(atime),
202 st_mtime_nsec: nsec(mtime),
203 st_birthtime_nsec: nsec(ctime),
204 ..Default::default()
205 })
206}