| use crate::os::windows::io::{ |
| AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, HandleOrInvalid, IntoRawHandle, |
| OwnedHandle, RawHandle, |
| }; |
| use crate::path::Path; |
| use crate::sys::api::{UnicodeStrRef, WinError}; |
| use crate::sys::fs::windows::debug_path_handle; |
| use crate::sys::fs::{File, OpenOptions}; |
| use crate::sys::handle::Handle; |
| use crate::sys::path::{WCStr, with_native_path}; |
| use crate::sys::{AsInner, FromInner, IntoInner, IoResult, c, to_u16s}; |
| use crate::{fmt, fs, io, ptr}; |
| |
| pub struct Dir { |
| handle: Handle, |
| } |
| |
| /// A wrapper around a raw NtCreateFile call. |
| /// |
| /// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers. |
| unsafe fn nt_create_file( |
| opts: &OpenOptions, |
| object_attributes: &c::OBJECT_ATTRIBUTES, |
| create_options: c::NTCREATEFILE_CREATE_OPTIONS, |
| ) -> io::Result<Handle> { |
| let mut handle = ptr::null_mut(); |
| let mut io_status = c::IO_STATUS_BLOCK::PENDING; |
| // SYNCHRONIZE is included in FILE_GENERIC_READ, but not GENERIC_READ, so we add it manually |
| let access = opts.get_access_mode()? | c::SYNCHRONIZE; |
| // one of FILE_SYNCHRONOUS_IO_{,NON}ALERT is required for later operations to succeed. |
| let options = create_options | c::FILE_SYNCHRONOUS_IO_NONALERT; |
| let status = unsafe { |
| c::NtCreateFile( |
| &mut handle, |
| access, |
| object_attributes, |
| &mut io_status, |
| ptr::null(), |
| c::FILE_ATTRIBUTE_NORMAL, |
| opts.share_mode, |
| opts.get_disposition()?, |
| options, |
| ptr::null(), |
| 0, |
| ) |
| }; |
| if c::nt_success(status) { |
| // SAFETY: nt_success guarantees that handle is no longer null |
| unsafe { Ok(Handle::from_raw_handle(handle)) } |
| } else { |
| Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) })).io_result() |
| } |
| } |
| |
| impl Dir { |
| pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<Self> { |
| with_native_path(path, &|path| Self::open_with_native(path, opts)) |
| } |
| |
| pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> { |
| // NtCreateFile will fail if given an absolute path and a non-null RootDirectory |
| if path.is_absolute() { |
| return File::open(path, opts); |
| } |
| let path = to_u16s(path)?; |
| let path = &path[..path.len() - 1]; // trim 0 byte |
| self.open_file_native(&path, opts).map(|handle| File { handle }) |
| } |
| |
| fn open_with_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> { |
| let creation = opts.get_creation_mode()?; |
| let sa = c::SECURITY_ATTRIBUTES { |
| nLength: size_of::<c::SECURITY_ATTRIBUTES>() as u32, |
| lpSecurityDescriptor: ptr::null_mut(), |
| bInheritHandle: opts.inherit_handle as c::BOOL, |
| }; |
| let handle = unsafe { |
| c::CreateFileW( |
| path.as_ptr(), |
| opts.get_access_mode()?, |
| opts.share_mode, |
| &raw const sa, |
| creation, |
| // FILE_FLAG_BACKUP_SEMANTICS is required to open a directory |
| opts.get_flags_and_attributes() | c::FILE_FLAG_BACKUP_SEMANTICS, |
| ptr::null_mut(), |
| ) |
| }; |
| match OwnedHandle::try_from(unsafe { HandleOrInvalid::from_raw_handle(handle) }) { |
| Ok(handle) => Ok(Self { handle: Handle::from_inner(handle) }), |
| Err(_) => Err(io::Error::last_os_error()), |
| } |
| } |
| |
| fn open_file_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> { |
| let name = UnicodeStrRef::from_slice(path); |
| let object_attributes = c::OBJECT_ATTRIBUTES { |
| RootDirectory: self.handle.as_raw_handle(), |
| ObjectName: name.as_ptr(), |
| ..c::OBJECT_ATTRIBUTES::with_length() |
| }; |
| unsafe { nt_create_file(opts, &object_attributes, c::FILE_NON_DIRECTORY_FILE) } |
| } |
| } |
| |
| impl fmt::Debug for Dir { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let mut b = debug_path_handle(self.handle.as_handle(), f, "Dir"); |
| b.finish() |
| } |
| } |
| |
| #[unstable(feature = "dirfd", issue = "120426")] |
| impl AsRawHandle for fs::Dir { |
| fn as_raw_handle(&self) -> RawHandle { |
| self.as_inner().handle.as_raw_handle() |
| } |
| } |
| |
| #[unstable(feature = "dirhandle", issue = "120426")] |
| impl IntoRawHandle for fs::Dir { |
| fn into_raw_handle(self) -> RawHandle { |
| self.into_inner().handle.into_raw_handle() |
| } |
| } |
| |
| #[unstable(feature = "dirhandle", issue = "120426")] |
| impl FromRawHandle for fs::Dir { |
| unsafe fn from_raw_handle(handle: RawHandle) -> Self { |
| Self::from_inner(Dir { handle: unsafe { FromRawHandle::from_raw_handle(handle) } }) |
| } |
| } |
| |
| #[unstable(feature = "dirhandle", issue = "120426")] |
| impl AsHandle for fs::Dir { |
| fn as_handle(&self) -> BorrowedHandle<'_> { |
| self.as_inner().handle.as_handle() |
| } |
| } |
| |
| #[unstable(feature = "dirhandle", issue = "120426")] |
| impl From<fs::Dir> for OwnedHandle { |
| fn from(value: fs::Dir) -> Self { |
| value.into_inner().handle.into_inner() |
| } |
| } |
| |
| #[unstable(feature = "dirhandle", issue = "120426")] |
| impl From<OwnedHandle> for fs::Dir { |
| fn from(value: OwnedHandle) -> Self { |
| Self::from_inner(Dir { handle: Handle::from_inner(value) }) |
| } |
| } |