diff options
| author | bors <bors@rust-lang.org> | 2023-12-13 02:27:12 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-12-13 02:27:12 +0000 |
| commit | 77d16997566dfb2384bcbc504e2579c7ae973666 (patch) | |
| tree | e4350dc1e78ad58b62a310734ee680c326a4e1b5 /library/std/src/sys | |
| parent | 3340d49d22b1aba425779767278c40781826c2f5 (diff) | |
| parent | c6f7aa0eea9af919ade663bb3a4bb4b6761c3f83 (diff) | |
| download | rust-77d16997566dfb2384bcbc504e2579c7ae973666.tar.gz rust-77d16997566dfb2384bcbc504e2579c7ae973666.zip | |
Auto merge of #116438 - ChrisDenton:truncate, r=thomcc
Windows: Allow `File::create` to work on hidden files This makes `OpenOptions::new().write(true).create(true).truncate(true).open(&path)` work if the path exists and is a hidden file. Previously it would fail with access denied. This makes it consistent with `OpenOptions::new().write(true).truncate(true).open(&path)` (note the lack of `create`) which does not have this restriction. It's also more consistent with other platforms. Fixes #115745 (see that issue for more details).
Diffstat (limited to 'library/std/src/sys')
| -rw-r--r-- | library/std/src/sys/windows/fs.rs | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index f57034cc8d5..09dfb0caeec 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -1,7 +1,7 @@ use crate::os::windows::prelude::*; use crate::borrow::Cow; -use crate::ffi::OsString; +use crate::ffi::{c_void, OsString}; use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, MaybeUninit}; @@ -16,8 +16,6 @@ use crate::sys::{c, cvt, Align8}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::thread; -use core::ffi::c_void; - use super::path::maybe_verbatim; use super::{api, to_u16s, IoResult}; @@ -273,7 +271,9 @@ impl OpenOptions { (false, false, false) => c::OPEN_EXISTING, (true, false, false) => c::OPEN_ALWAYS, (false, true, false) => c::TRUNCATE_EXISTING, - (true, true, false) => c::CREATE_ALWAYS, + // `CREATE_ALWAYS` has weird semantics so we emulate it using + // `OPEN_ALWAYS` and a manual truncation step. See #115745. + (true, true, false) => c::OPEN_ALWAYS, (_, _, true) => c::CREATE_NEW, }) } @@ -289,19 +289,40 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { let path = maybe_verbatim(path)?; + let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( path.as_ptr(), opts.get_access_mode()?, opts.share_mode, opts.security_attributes, - opts.get_creation_mode()?, + creation, opts.get_flags_and_attributes(), ptr::null_mut(), ) }; let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) }; - if let Ok(handle) = handle.try_into() { + if let Ok(handle) = OwnedHandle::try_from(handle) { + // Manual truncation. See #115745. + if opts.truncate + && creation == c::OPEN_ALWAYS + && unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS + { + unsafe { + // Setting the allocation size to zero also sets the + // EOF position to zero. + let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 }; + let result = c::SetFileInformationByHandle( + handle.as_raw_handle(), + c::FileAllocationInfo, + ptr::addr_of!(alloc).cast::<c_void>(), + mem::size_of::<c::FILE_ALLOCATION_INFO>() as u32, + ); + if result == 0 { + return Err(io::Error::last_os_error()); + } + } + } Ok(File { handle: Handle::from_inner(handle) }) } else { Err(Error::last_os_error()) |
