Skip to main content

std\sys\fs\windows/
dir.rs

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
27/// A wrapper around a raw NtCreateFile call.
28///
29/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers.
30unsafe 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    // SYNCHRONIZE is included in FILE_GENERIC_READ, but not GENERIC_READ, so we add it manually
38    let access = opts.get_access_mode()? | c::SYNCHRONIZE;
39    // one of FILE_SYNCHRONOUS_IO_{,NON}ALERT is required for later operations to succeed.
40    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        // SAFETY: nt_success guarantees that handle is no longer null
58        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        // NtCreateFile will fail if given an absolute path and a non-null RootDirectory
71        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                // FILE_FLAG_BACKUP_SEMANTICS is required to open a directory
105                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        // Calculate the layout of the `FILE_RENAME_INFORMATION` we pass to `NtSetInformationFile`
148        // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
149        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        // SAFETY: We allocate enough memory for a full FILE_RENAME_INFORMATION struct and the filename.
163        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            // Don't include the NULL in the size
175            (&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            // SAFETY: nt_success guarantees that handle is no longer null
195            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        // Reuse the implementation for files, which should work for all handles.
204        let handle = self.handle.as_raw_handle();
205        let f = core::mem::ManuallyDrop::new(File {
206            // SAFETY: we borrowed `self` so the handle will not be closed while this function runs.
207            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}