Skip to content

Commit 616c4ce

Browse files
committed
fix windows functions, documentation
1 parent c666384 commit 616c4ce

File tree

2 files changed

+118
-59
lines changed

2 files changed

+118
-59
lines changed

library/std/src/fs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,8 @@ impl Dir {
15841584
///
15851585
/// # Errors
15861586
///
1587-
/// This function will return an error in these (and other) situations:
1587+
/// This function may return an error in these (and other) situations, depending on the
1588+
/// specified `opts`:
15881589
/// * The path doesn't exist
15891590
/// * The path doesn't specify a regular file
15901591
/// * The process doesn't have permission to read/write (according to `opts`) the directory

library/std/src/sys/fs/windows.rs

Lines changed: 116 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,30 @@ impl OpenOptions {
294294
})
295295
}
296296

297+
fn get_disposition(&self) -> io::Result<u32> {
298+
match (self.write, self.append) {
299+
(true, false) => {}
300+
(false, false) => {
301+
if self.truncate || self.create || self.create_new {
302+
return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
303+
}
304+
}
305+
(_, true) => {
306+
if self.truncate && !self.create_new {
307+
return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
308+
}
309+
}
310+
}
311+
312+
Ok(match (self.create, self.truncate, self.create_new) {
313+
(false, false, false) => c::FILE_OPEN,
314+
(true, false, false) => c::FILE_OPEN_IF,
315+
(false, true, false) => c::FILE_OVERWRITE,
316+
(true, true, false) => c::FILE_OVERWRITE_IF,
317+
(_, _, true) => c::FILE_CREATE,
318+
})
319+
}
320+
297321
fn get_flags_and_attributes(&self) -> u32 {
298322
self.custom_flags
299323
| self.attributes
@@ -856,20 +880,16 @@ impl File {
856880

857881
unsafe fn nt_create_file(
858882
access: u32,
883+
disposition: u32,
859884
object_attributes: &c::OBJECT_ATTRIBUTES,
860885
share: u32,
861886
dir: bool,
862887
) -> Result<Handle, WinError> {
863888
let mut handle = ptr::null_mut();
864889
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
865-
let disposition = match (access & c::GENERIC_READ > 0, access & c::GENERIC_WRITE > 0) {
866-
(true, true) => c::FILE_OPEN_IF,
867-
(true, false) => c::FILE_OPEN,
868-
(false, true) => c::FILE_CREATE,
869-
(false, false) => {
870-
return Err(WinError::new(c::ERROR_INVALID_PARAMETER));
871-
}
872-
};
890+
let access = access | c::SYNCHRONIZE;
891+
let options = if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE }
892+
| c::FILE_SYNCHRONOUS_IO_NONALERT;
873893
let status = unsafe {
874894
c::NtCreateFile(
875895
&mut handle,
@@ -880,7 +900,7 @@ unsafe fn nt_create_file(
880900
c::FILE_ATTRIBUTE_NORMAL,
881901
share,
882902
disposition,
883-
if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE },
903+
options,
884904
ptr::null(),
885905
0,
886906
)
@@ -911,38 +931,48 @@ fn run_path_with_wcstr<T, P: AsRef<Path>>(
911931
f(path)
912932
}
913933

934+
fn run_path_with_utf16<T, P: AsRef<Path>>(
935+
path: P,
936+
f: &dyn Fn(&[u16]) -> io::Result<T>,
937+
) -> io::Result<T> {
938+
let utf16: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
939+
f(&utf16)
940+
}
941+
914942
impl Dir {
915943
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
916944
let opts = OpenOptions::new();
917-
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
945+
Self::new_native(path.as_ref(), &opts)
918946
}
919947

920948
pub fn new_with<P: AsRef<Path>>(path: P, opts: &OpenOptions) -> io::Result<Self> {
921-
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
949+
Self::new_native(path.as_ref(), &opts)
922950
}
923951

924952
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
925953
let mut opts = OpenOptions::new();
954+
let path = path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>();
926955
opts.read(true);
927-
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
956+
Ok(File { handle: self.open_native(&path, &opts)? })
928957
}
929958

930959
pub fn open_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<File> {
931-
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
960+
let path = path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>();
961+
Ok(File { handle: self.open_native(&path, &opts)? })
932962
}
933963

934964
pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
935965
let mut opts = OpenOptions::new();
936966
opts.write(true);
937-
run_path_with_wcstr(path, &|path| self.create_dir_native(path, &opts).map(|_| ()))
967+
run_path_with_utf16(path, &|path| self.create_dir_native(path, &opts).map(|_| ()))
938968
}
939969

940970
pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
941-
run_path_with_wcstr(path, &|path| self.remove_native(path, false))
971+
run_path_with_utf16(path, &|path| self.remove_native(path, false))
942972
}
943973

944974
pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
945-
run_path_with_wcstr(path, &|path| self.remove_native(path, true))
975+
run_path_with_utf16(path, &|path| self.remove_native(path, true))
946976
}
947977

948978
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
@@ -951,36 +981,18 @@ impl Dir {
951981
to_dir: &Self,
952982
to: Q,
953983
) -> io::Result<()> {
954-
run_path_with_wcstr(from.as_ref(), &|from| {
955-
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from, to_dir, to))
956-
})
984+
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from.as_ref(), to_dir, to))
957985
}
958986

959-
fn new_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
960-
let name = c::UNICODE_STRING {
961-
Length: path.count_bytes() as _,
962-
MaximumLength: path.count_bytes() as _,
963-
Buffer: path.as_ptr() as *mut _,
964-
};
965-
let object_attributes = c::OBJECT_ATTRIBUTES {
966-
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
967-
RootDirectory: ptr::null_mut(),
968-
ObjectName: &name,
969-
Attributes: 0,
970-
SecurityDescriptor: ptr::null(),
971-
SecurityQualityOfService: ptr::null(),
972-
};
973-
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
974-
let handle =
975-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
976-
.io_result()?;
987+
fn new_native(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
988+
let handle = File::open(path, opts)?.into_inner();
977989
Ok(Self { handle })
978990
}
979991

980-
fn open_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
992+
fn open_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
981993
let name = c::UNICODE_STRING {
982-
Length: path.count_bytes() as _,
983-
MaximumLength: path.count_bytes() as _,
994+
Length: path.len() as _,
995+
MaximumLength: path.len() as _,
984996
Buffer: path.as_ptr() as *mut _,
985997
};
986998
let object_attributes = c::OBJECT_ATTRIBUTES {
@@ -992,14 +1004,22 @@ impl Dir {
9921004
SecurityQualityOfService: ptr::null(),
9931005
};
9941006
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
995-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, false) }
996-
.io_result()
1007+
unsafe {
1008+
nt_create_file(
1009+
opts.get_access_mode()?,
1010+
opts.get_disposition()?,
1011+
&object_attributes,
1012+
share,
1013+
false,
1014+
)
1015+
}
1016+
.io_result()
9971017
}
9981018

999-
fn create_dir_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
1019+
fn create_dir_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
10001020
let name = c::UNICODE_STRING {
1001-
Length: path.count_bytes() as _,
1002-
MaximumLength: path.count_bytes() as _,
1021+
Length: path.len() as _,
1022+
MaximumLength: path.len() as _,
10031023
Buffer: path.as_ptr() as *mut _,
10041024
};
10051025
let object_attributes = c::OBJECT_ATTRIBUTES {
@@ -1011,11 +1031,19 @@ impl Dir {
10111031
SecurityQualityOfService: ptr::null(),
10121032
};
10131033
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
1014-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
1015-
.io_result()
1034+
unsafe {
1035+
nt_create_file(
1036+
opts.get_access_mode()?,
1037+
opts.get_disposition()?,
1038+
&object_attributes,
1039+
share,
1040+
true,
1041+
)
1042+
}
1043+
.io_result()
10161044
}
10171045

1018-
fn remove_native(&self, path: &WCStr, dir: bool) -> io::Result<()> {
1046+
fn remove_native(&self, path: &[u16], dir: bool) -> io::Result<()> {
10191047
let mut opts = OpenOptions::new();
10201048
opts.access_mode(c::GENERIC_WRITE);
10211049
let handle =
@@ -1032,24 +1060,54 @@ impl Dir {
10321060
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
10331061
}
10341062

1035-
fn rename_native(&self, from: &WCStr, to_dir: &Self, to: &WCStr) -> io::Result<()> {
1063+
fn rename_native(&self, from: &Path, to_dir: &Self, to: &WCStr) -> io::Result<()> {
10361064
let mut opts = OpenOptions::new();
10371065
opts.access_mode(c::GENERIC_WRITE);
1038-
let handle = self.open_native(from, &opts)?;
1039-
let info = c::FILE_RENAME_INFO {
1040-
Anonymous: c::FILE_RENAME_INFO_0 { ReplaceIfExists: true },
1041-
RootDirectory: to_dir.handle.as_raw_handle(),
1042-
FileNameLength: to.count_bytes() as _,
1043-
FileName: [to.as_ptr() as u16],
1066+
let handle = run_path_with_utf16(from, &|u| self.open_native(u, &opts))?;
1067+
// Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
1068+
// This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
1069+
let Ok(new_len_without_nul_in_bytes): Result<u32, _> =
1070+
((to.count_bytes() - 1) * 2).try_into()
1071+
else {
1072+
return Err(io::Error::new(io::ErrorKind::InvalidFilename, "Filename too long"));
10441073
};
1074+
let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap();
1075+
let struct_size = offset + new_len_without_nul_in_bytes + 2;
1076+
let layout =
1077+
Layout::from_size_align(struct_size as usize, align_of::<c::FILE_RENAME_INFO>())
1078+
.unwrap();
1079+
1080+
// SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
1081+
let file_rename_info;
1082+
unsafe {
1083+
file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFO>();
1084+
if file_rename_info.is_null() {
1085+
return Err(io::ErrorKind::OutOfMemory.into());
1086+
}
1087+
1088+
(&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 {
1089+
Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS,
1090+
});
1091+
1092+
(&raw mut (*file_rename_info).RootDirectory).write(to_dir.handle.as_raw_handle());
1093+
// Don't include the NULL in the size
1094+
(&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes);
1095+
1096+
to.as_ptr().copy_to_nonoverlapping(
1097+
(&raw mut (*file_rename_info).FileName).cast::<u16>(),
1098+
run_path_with_wcstr(from, &|s| Ok(s.count_bytes())).unwrap(),
1099+
);
1100+
}
1101+
10451102
let result = unsafe {
10461103
c::SetFileInformationByHandle(
10471104
handle.as_raw_handle(),
1048-
c::FileRenameInfo,
1049-
ptr::addr_of!(info) as _,
1050-
size_of::<c::FILE_RENAME_INFO>() as _,
1105+
c::FileRenameInfoEx,
1106+
file_rename_info.cast::<c_void>(),
1107+
struct_size,
10511108
)
10521109
};
1110+
unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };
10531111
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
10541112
}
10551113
}

0 commit comments

Comments
 (0)