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).
This commit is contained in:
commit
77d1699756
@ -22,7 +22,7 @@ use crate::os::unix::fs::symlink as symlink_file;
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use crate::os::unix::fs::symlink as symlink_junction;
|
use crate::os::unix::fs::symlink as symlink_junction;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::os::windows::fs::{symlink_dir, symlink_file};
|
use crate::os::windows::fs::{symlink_dir, symlink_file, OpenOptionsExt};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::sys::fs::symlink_junction;
|
use crate::sys::fs::symlink_junction;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@ -1793,3 +1793,28 @@ fn windows_unix_socket_exists() {
|
|||||||
assert_eq!(socket_path.try_exists().unwrap(), true);
|
assert_eq!(socket_path.try_exists().unwrap(), true);
|
||||||
assert_eq!(socket_path.metadata().is_ok(), true);
|
assert_eq!(socket_path.metadata().is_ok(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn test_hidden_file_truncation() {
|
||||||
|
// Make sure that File::create works on an existing hidden file. See #115745.
|
||||||
|
let tmpdir = tmpdir();
|
||||||
|
let path = tmpdir.join("hidden_file.txt");
|
||||||
|
|
||||||
|
// Create a hidden file.
|
||||||
|
const FILE_ATTRIBUTE_HIDDEN: u32 = 2;
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.attributes(FILE_ATTRIBUTE_HIDDEN)
|
||||||
|
.open(&path)
|
||||||
|
.unwrap();
|
||||||
|
file.write("hidden world!".as_bytes()).unwrap();
|
||||||
|
file.flush().unwrap();
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
// Create a new file by truncating the existing one.
|
||||||
|
let file = File::create(&path).unwrap();
|
||||||
|
let metadata = file.metadata().unwrap();
|
||||||
|
assert_eq!(metadata.len(), 0);
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::os::windows::prelude::*;
|
use crate::os::windows::prelude::*;
|
||||||
|
|
||||||
use crate::borrow::Cow;
|
use crate::borrow::Cow;
|
||||||
use crate::ffi::OsString;
|
use crate::ffi::{c_void, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
|
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
|
||||||
use crate::mem::{self, MaybeUninit};
|
use crate::mem::{self, MaybeUninit};
|
||||||
@ -16,8 +16,6 @@ use crate::sys::{c, cvt, Align8};
|
|||||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||||
use crate::thread;
|
use crate::thread;
|
||||||
|
|
||||||
use core::ffi::c_void;
|
|
||||||
|
|
||||||
use super::path::maybe_verbatim;
|
use super::path::maybe_verbatim;
|
||||||
use super::{api, to_u16s, IoResult};
|
use super::{api, to_u16s, IoResult};
|
||||||
|
|
||||||
@ -273,7 +271,9 @@ impl OpenOptions {
|
|||||||
(false, false, false) => c::OPEN_EXISTING,
|
(false, false, false) => c::OPEN_EXISTING,
|
||||||
(true, false, false) => c::OPEN_ALWAYS,
|
(true, false, false) => c::OPEN_ALWAYS,
|
||||||
(false, true, false) => c::TRUNCATE_EXISTING,
|
(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,
|
(_, _, true) => c::CREATE_NEW,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -289,19 +289,40 @@ impl OpenOptions {
|
|||||||
impl File {
|
impl File {
|
||||||
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
||||||
let path = maybe_verbatim(path)?;
|
let path = maybe_verbatim(path)?;
|
||||||
|
let creation = opts.get_creation_mode()?;
|
||||||
let handle = unsafe {
|
let handle = unsafe {
|
||||||
c::CreateFileW(
|
c::CreateFileW(
|
||||||
path.as_ptr(),
|
path.as_ptr(),
|
||||||
opts.get_access_mode()?,
|
opts.get_access_mode()?,
|
||||||
opts.share_mode,
|
opts.share_mode,
|
||||||
opts.security_attributes,
|
opts.security_attributes,
|
||||||
opts.get_creation_mode()?,
|
creation,
|
||||||
opts.get_flags_and_attributes(),
|
opts.get_flags_and_attributes(),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
|
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) })
|
Ok(File { handle: Handle::from_inner(handle) })
|
||||||
} else {
|
} else {
|
||||||
Err(Error::last_os_error())
|
Err(Error::last_os_error())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user