Skip to main content

std\sys\fs\windows/
dir.rs

1use crate::os::windows::io::{
2    AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, HandleOrInvalid, IntoRawHandle,
3    OwnedHandle, RawHandle,
4};
5use crate::path::Path;
6use crate::sys::api::{UnicodeStrRef, WinError};
7use crate::sys::fs::windows::debug_path_handle;
8use crate::sys::fs::{File, OpenOptions};
9use crate::sys::handle::Handle;
10use crate::sys::path::{WCStr, with_native_path};
11use crate::sys::{AsInner, FromInner, IntoInner, IoResult, c, to_u16s};
12use crate::{fmt, fs, io, ptr};
13
14pub struct Dir {
15    handle: Handle,
16}
17
18/// A wrapper around a raw NtCreateFile call.
19///
20/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers.
21unsafe fn nt_create_file(
22    opts: &OpenOptions,
23    object_attributes: &c::OBJECT_ATTRIBUTES,
24    create_options: c::NTCREATEFILE_CREATE_OPTIONS,
25) -> io::Result<Handle> {
26    let mut handle = ptr::null_mut();
27    let mut io_status = c::IO_STATUS_BLOCK::PENDING;
28    // SYNCHRONIZE is included in FILE_GENERIC_READ, but not GENERIC_READ, so we add it manually
29    let access = opts.get_access_mode()? | c::SYNCHRONIZE;
30    // one of FILE_SYNCHRONOUS_IO_{,NON}ALERT is required for later operations to succeed.
31    let options = create_options | c::FILE_SYNCHRONOUS_IO_NONALERT;
32    let status = unsafe {
33        c::NtCreateFile(
34            &mut handle,
35            access,
36            object_attributes,
37            &mut io_status,
38            ptr::null(),
39            c::FILE_ATTRIBUTE_NORMAL,
40            opts.share_mode,
41            opts.get_disposition()?,
42            options,
43            ptr::null(),
44            0,
45        )
46    };
47    if c::nt_success(status) {
48        // SAFETY: nt_success guarantees that handle is no longer null
49        unsafe { Ok(Handle::from_raw_handle(handle)) }
50    } else {
51        Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) })).io_result()
52    }
53}
54
55impl Dir {
56    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
57        with_native_path(path, &|path| Self::open_with_native(path, opts))
58    }
59
60    pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
61        // NtCreateFile will fail if given an absolute path and a non-null RootDirectory
62        if path.is_absolute() {
63            return File::open(path, opts);
64        }
65        let path = to_u16s(path)?;
66        let path = &path[..path.len() - 1]; // trim 0 byte
67        self.open_file_native(&path, opts).map(|handle| File { handle })
68    }
69
70    fn open_with_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
71        let creation = opts.get_creation_mode()?;
72        let sa = c::SECURITY_ATTRIBUTES {
73            nLength: size_of::<c::SECURITY_ATTRIBUTES>() as u32,
74            lpSecurityDescriptor: ptr::null_mut(),
75            bInheritHandle: opts.inherit_handle as c::BOOL,
76        };
77        let handle = unsafe {
78            c::CreateFileW(
79                path.as_ptr(),
80                opts.get_access_mode()?,
81                opts.share_mode,
82                &raw const sa,
83                creation,
84                // FILE_FLAG_BACKUP_SEMANTICS is required to open a directory
85                opts.get_flags_and_attributes() | c::FILE_FLAG_BACKUP_SEMANTICS,
86                ptr::null_mut(),
87            )
88        };
89        match OwnedHandle::try_from(unsafe { HandleOrInvalid::from_raw_handle(handle) }) {
90            Ok(handle) => Ok(Self { handle: Handle::from_inner(handle) }),
91            Err(_) => Err(io::Error::last_os_error()),
92        }
93    }
94
95    fn open_file_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
96        let name = UnicodeStrRef::from_slice(path);
97        let object_attributes = c::OBJECT_ATTRIBUTES {
98            RootDirectory: self.handle.as_raw_handle(),
99            ObjectName: name.as_ptr(),
100            ..c::OBJECT_ATTRIBUTES::with_length()
101        };
102        unsafe { nt_create_file(opts, &object_attributes, c::FILE_NON_DIRECTORY_FILE) }
103    }
104}
105
106impl fmt::Debug for Dir {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        let mut b = debug_path_handle(self.handle.as_handle(), f, "Dir");
109        b.finish()
110    }
111}
112
113#[unstable(feature = "dirfd", issue = "120426")]
114impl AsRawHandle for fs::Dir {
115    fn as_raw_handle(&self) -> RawHandle {
116        self.as_inner().handle.as_raw_handle()
117    }
118}
119
120#[unstable(feature = "dirhandle", issue = "120426")]
121impl IntoRawHandle for fs::Dir {
122    fn into_raw_handle(self) -> RawHandle {
123        self.into_inner().handle.into_raw_handle()
124    }
125}
126
127#[unstable(feature = "dirhandle", issue = "120426")]
128impl FromRawHandle for fs::Dir {
129    unsafe fn from_raw_handle(handle: RawHandle) -> Self {
130        Self::from_inner(Dir { handle: unsafe { FromRawHandle::from_raw_handle(handle) } })
131    }
132}
133
134#[unstable(feature = "dirhandle", issue = "120426")]
135impl AsHandle for fs::Dir {
136    fn as_handle(&self) -> BorrowedHandle<'_> {
137        self.as_inner().handle.as_handle()
138    }
139}
140
141#[unstable(feature = "dirhandle", issue = "120426")]
142impl From<fs::Dir> for OwnedHandle {
143    fn from(value: fs::Dir) -> Self {
144        value.into_inner().handle.into_inner()
145    }
146}
147
148#[unstable(feature = "dirhandle", issue = "120426")]
149impl From<OwnedHandle> for fs::Dir {
150    fn from(value: OwnedHandle) -> Self {
151        Self::from_inner(Dir { handle: Handle::from_inner(value) })
152    }
153}