1use crate::alloc::{Layout, alloc, dealloc};
2use crate::ffi::c_void;
3use crate::mem::offset_of;
4use crate::os::windows::io::{
5 AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, HandleOrInvalid, IntoRawHandle,
6 OwnedHandle, RawHandle,
7};
8use crate::path::Path;
9use crate::sys::api::{self, SetFileInformation, UnicodeStrRef, WinError};
10use crate::sys::fs::windows::debug_path_handle;
11use crate::sys::fs::{File, FileAttr, OpenOptions};
12use crate::sys::handle::Handle;
13use crate::sys::path::{WCStr, with_native_path};
14use crate::sys::{AsInner, FromInner, IntoInner, IoResult, c, to_u16s};
15use crate::{fmt, fs, io, ptr};
16
17pub struct Dir {
18 handle: Handle,
19}
20
21fn to_u16s_without_nul(path: &Path) -> io::Result<Vec<u16>> {
22 let mut path = to_u16s(path)?;
23 path.pop();
24 Ok(path)
25}
26
27unsafe fn nt_create_file(
31 opts: &OpenOptions,
32 object_attributes: &c::OBJECT_ATTRIBUTES,
33 create_options: c::NTCREATEFILE_CREATE_OPTIONS,
34) -> io::Result<Handle> {
35 let mut handle = ptr::null_mut();
36 let mut io_status = c::IO_STATUS_BLOCK::PENDING;
37 let access = opts.get_access_mode()? | c::SYNCHRONIZE;
39 let options = create_options | c::FILE_SYNCHRONOUS_IO_NONALERT;
41 let status = unsafe {
42 c::NtCreateFile(
43 &mut handle,
44 access,
45 object_attributes,
46 &mut io_status,
47 ptr::null(),
48 c::FILE_ATTRIBUTE_NORMAL,
49 opts.share_mode,
50 opts.get_disposition()?,
51 options,
52 ptr::null(),
53 0,
54 )
55 };
56 if c::nt_success(status) {
57 unsafe { Ok(Handle::from_raw_handle(handle)) }
59 } else {
60 Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) })).io_result()
61 }
62}
63
64impl Dir {
65 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
66 with_native_path(path, &|path| Self::open_with_native(path, opts))
67 }
68
69 pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
70 if path.is_absolute() {
72 return File::open(path, opts);
73 }
74 let path = to_u16s_without_nul(path)?;
75 self.open_file_native(&path, opts, false).map(|handle| File { handle })
76 }
77
78 pub fn remove_file(&self, path: &Path) -> io::Result<()> {
79 let path = to_u16s_without_nul(path)?;
80 self.remove_native(&path, false)
81 }
82
83 pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> {
84 let is_dir = from.is_dir();
85 let from = to_u16s_without_nul(from)?;
86 let to = to_u16s_without_nul(to)?;
87 self.rename_native(&from, to_dir, &to, is_dir)
88 }
89
90 fn open_with_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
91 let creation = opts.get_creation_mode()?;
92 let sa = c::SECURITY_ATTRIBUTES {
93 nLength: size_of::<c::SECURITY_ATTRIBUTES>() as u32,
94 lpSecurityDescriptor: ptr::null_mut(),
95 bInheritHandle: opts.inherit_handle as c::BOOL,
96 };
97 let handle = unsafe {
98 c::CreateFileW(
99 path.as_ptr(),
100 opts.get_access_mode()?,
101 opts.share_mode,
102 &raw const sa,
103 creation,
104 opts.get_flags_and_attributes() | c::FILE_FLAG_BACKUP_SEMANTICS,
106 ptr::null_mut(),
107 )
108 };
109 match OwnedHandle::try_from(unsafe { HandleOrInvalid::from_raw_handle(handle) }) {
110 Ok(handle) => Ok(Self { handle: Handle::from_inner(handle) }),
111 Err(_) => Err(io::Error::last_os_error()),
112 }
113 }
114
115 fn open_file_native(&self, path: &[u16], opts: &OpenOptions, dir: bool) -> io::Result<Handle> {
116 let name = UnicodeStrRef::from_slice(path);
117 let object_attributes = c::OBJECT_ATTRIBUTES {
118 RootDirectory: self.handle.as_raw_handle(),
119 ObjectName: name.as_ptr(),
120 ..c::OBJECT_ATTRIBUTES::with_length()
121 };
122 let create_opt = if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE };
123 unsafe { nt_create_file(opts, &object_attributes, create_opt) }
124 }
125
126 fn remove_native(&self, path: &[u16], dir: bool) -> io::Result<()> {
127 let mut opts = OpenOptions::new();
128 opts.access_mode(c::DELETE);
129 let handle = self.open_file_native(path, &opts, dir)?;
130 let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE };
131 let result = unsafe {
132 c::SetFileInformationByHandle(
133 handle.as_raw_handle(),
134 c::FileDispositionInfoEx,
135 (&info).as_ptr(),
136 size_of::<c::FILE_DISPOSITION_INFO_EX>() as _,
137 )
138 };
139 if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
140 }
141
142 fn rename_native(&self, from: &[u16], to_dir: &Self, to: &[u16], dir: bool) -> io::Result<()> {
143 let mut opts = OpenOptions::new();
144 opts.access_mode(c::DELETE);
145 opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
146 let handle = self.open_file_native(from, &opts, dir)?;
147 const too_long_err: io::Error =
150 io::const_error!(io::ErrorKind::InvalidFilename, "Filename too long");
151 let struct_size = to
152 .len()
153 .checked_mul(2)
154 .and_then(|x| x.checked_add(offset_of!(c::FILE_RENAME_INFORMATION, FileName)))
155 .ok_or(too_long_err)?;
156 let layout = Layout::from_size_align(struct_size, align_of::<c::FILE_RENAME_INFORMATION>())
157 .map_err(|_| too_long_err)?;
158 let struct_size = u32::try_from(struct_size).map_err(|_| too_long_err)?;
159 let to_byte_len = u32::try_from(to.len() * 2).map_err(|_| too_long_err)?;
160
161 let file_rename_info;
162 unsafe {
164 file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFORMATION>();
165 if file_rename_info.is_null() {
166 return Err(io::ErrorKind::OutOfMemory.into());
167 }
168
169 (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFORMATION_0 {
170 Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS,
171 });
172
173 (&raw mut (*file_rename_info).RootDirectory).write(to_dir.handle.as_raw_handle());
174 (&raw mut (*file_rename_info).FileNameLength).write(to_byte_len);
176
177 to.as_ptr().copy_to_nonoverlapping(
178 (&raw mut (*file_rename_info).FileName).cast::<u16>(),
179 to.len(),
180 );
181 }
182
183 let status = unsafe {
184 c::NtSetInformationFile(
185 handle.as_raw_handle(),
186 &mut c::IO_STATUS_BLOCK::default(),
187 file_rename_info.cast::<c_void>(),
188 struct_size,
189 c::FileRenameInformation,
190 )
191 };
192 unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };
193 if c::nt_success(status) {
194 Ok(())
196 } else {
197 Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) }))
198 }
199 .io_result()
200 }
201
202 pub fn metadata(&self) -> io::Result<FileAttr> {
203 let handle = self.handle.as_raw_handle();
205 let f = core::mem::ManuallyDrop::new(File {
206 handle: unsafe { Handle::from_raw_handle(handle) },
208 });
209 f.file_attr()
210 }
211}
212
213impl fmt::Debug for Dir {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 let mut b = debug_path_handle(self.handle.as_handle(), f, "Dir");
216 b.finish()
217 }
218}
219
220#[unstable(feature = "dirfd", issue = "120426")]
221impl AsRawHandle for fs::Dir {
222 fn as_raw_handle(&self) -> RawHandle {
223 self.as_inner().handle.as_raw_handle()
224 }
225}
226
227#[unstable(feature = "dirfd", issue = "120426")]
228impl IntoRawHandle for fs::Dir {
229 fn into_raw_handle(self) -> RawHandle {
230 self.into_inner().handle.into_raw_handle()
231 }
232}
233
234#[unstable(feature = "dirfd", issue = "120426")]
235impl FromRawHandle for fs::Dir {
236 unsafe fn from_raw_handle(handle: RawHandle) -> Self {
237 Self::from_inner(Dir { handle: unsafe { FromRawHandle::from_raw_handle(handle) } })
238 }
239}
240
241#[unstable(feature = "dirfd", issue = "120426")]
242impl AsHandle for fs::Dir {
243 fn as_handle(&self) -> BorrowedHandle<'_> {
244 self.as_inner().handle.as_handle()
245 }
246}
247
248#[unstable(feature = "dirfd", issue = "120426")]
249impl From<fs::Dir> for OwnedHandle {
250 fn from(value: fs::Dir) -> Self {
251 value.into_inner().handle.into_inner()
252 }
253}
254
255#[unstable(feature = "dirfd", issue = "120426")]
256impl From<OwnedHandle> for fs::Dir {
257 fn from(value: OwnedHandle) -> Self {
258 Self::from_inner(Dir { handle: Handle::from_inner(value) })
259 }
260}