summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-12-13 02:27:12 +0000
committerbors <bors@rust-lang.org>2023-12-13 02:27:12 +0000
commit77d16997566dfb2384bcbc504e2579c7ae973666 (patch)
treee4350dc1e78ad58b62a310734ee680c326a4e1b5 /library/std/src/sys
parent3340d49d22b1aba425779767278c40781826c2f5 (diff)
parentc6f7aa0eea9af919ade663bb3a4bb4b6761c3f83 (diff)
downloadrust-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.rs33
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())