Skip to content

Commit 4890e60

Browse files
committed
fix windows functions, documentation
1 parent c1c4fb1 commit 4890e60

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
@@ -1568,7 +1568,8 @@ impl Dir {
15681568
///
15691569
/// # Errors
15701570
///
1571-
/// This function will return an error in these (and other) situations:
1571+
/// This function may return an error in these (and other) situations, depending on the
1572+
/// specified `opts`:
15721573
/// * The path doesn't exist
15731574
/// * The path doesn't specify a regular file
15741575
/// * 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
@@ -293,6 +293,30 @@ impl OpenOptions {
293293
})
294294
}
295295

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

856880
unsafe fn nt_create_file(
857881
access: u32,
882+
disposition: u32,
858883
object_attributes: &c::OBJECT_ATTRIBUTES,
859884
share: u32,
860885
dir: bool,
861886
) -> Result<Handle, WinError> {
862887
let mut handle = ptr::null_mut();
863888
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
864-
let disposition = match (access & c::GENERIC_READ > 0, access & c::GENERIC_WRITE > 0) {
865-
(true, true) => c::FILE_OPEN_IF,
866-
(true, false) => c::FILE_OPEN,
867-
(false, true) => c::FILE_CREATE,
868-
(false, false) => {
869-
return Err(WinError::new(c::ERROR_INVALID_PARAMETER));
870-
}
871-
};
889+
let access = access | c::SYNCHRONIZE;
890+
let options = if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE }
891+
| c::FILE_SYNCHRONOUS_IO_NONALERT;
872892
let status = unsafe {
873893
c::NtCreateFile(
874894
&mut handle,
@@ -879,7 +899,7 @@ unsafe fn nt_create_file(
879899
c::FILE_ATTRIBUTE_NORMAL,
880900
share,
881901
disposition,
882-
if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE },
902+
options,
883903
ptr::null(),
884904
0,
885905
)
@@ -910,38 +930,48 @@ fn run_path_with_wcstr<T, P: AsRef<Path>>(
910930
f(path)
911931
}
912932

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

919947
pub fn new_with<P: AsRef<Path>>(path: P, opts: &OpenOptions) -> io::Result<Self> {
920-
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
948+
Self::new_native(path.as_ref(), &opts)
921949
}
922950

923951
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
924952
let mut opts = OpenOptions::new();
953+
let path = path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>();
925954
opts.read(true);
926-
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
955+
Ok(File { handle: self.open_native(&path, &opts)? })
927956
}
928957

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

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

939969
pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
940-
run_path_with_wcstr(path, &|path| self.remove_native(path, false))
970+
run_path_with_utf16(path, &|path| self.remove_native(path, false))
941971
}
942972

943973
pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
944-
run_path_with_wcstr(path, &|path| self.remove_native(path, true))
974+
run_path_with_utf16(path, &|path| self.remove_native(path, true))
945975
}
946976

947977
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
@@ -950,36 +980,18 @@ impl Dir {
950980
to_dir: &Self,
951981
to: Q,
952982
) -> io::Result<()> {
953-
run_path_with_wcstr(from.as_ref(), &|from| {
954-
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from, to_dir, to))
955-
})
983+
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from.as_ref(), to_dir, to))
956984
}
957985

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

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

998-
fn create_dir_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
1018+
fn create_dir_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
9991019
let name = c::UNICODE_STRING {
1000-
Length: path.count_bytes() as _,
1001-
MaximumLength: path.count_bytes() as _,
1020+
Length: path.len() as _,
1021+
MaximumLength: path.len() as _,
10021022
Buffer: path.as_ptr() as *mut _,
10031023
};
10041024
let object_attributes = c::OBJECT_ATTRIBUTES {
@@ -1010,11 +1030,19 @@ impl Dir {
10101030
SecurityQualityOfService: ptr::null(),
10111031
};
10121032
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
1013-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
1014-
.io_result()
1033+
unsafe {
1034+
nt_create_file(
1035+
opts.get_access_mode()?,
1036+
opts.get_disposition()?,
1037+
&object_attributes,
1038+
share,
1039+
true,
1040+
)
1041+
}
1042+
.io_result()
10151043
}
10161044

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

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

0 commit comments

Comments
 (0)