Skip to content

Commit 922502d

Browse files
committed
fix windows functions, documentation
1 parent 3fab5f4 commit 922502d

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
@@ -1517,7 +1517,8 @@ impl Dir {
15171517
///
15181518
/// # Errors
15191519
///
1520-
/// This function will return an error in these (and other) situations:
1520+
/// This function may return an error in these (and other) situations, depending on the
1521+
/// specified `opts`:
15211522
/// * The path doesn't exist
15221523
/// * The path doesn't specify a regular file
15231524
/// * 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
@@ -853,20 +877,16 @@ impl File {
853877

854878
unsafe fn nt_create_file(
855879
access: u32,
880+
disposition: u32,
856881
object_attributes: &c::OBJECT_ATTRIBUTES,
857882
share: u32,
858883
dir: bool,
859884
) -> Result<Handle, WinError> {
860885
let mut handle = ptr::null_mut();
861886
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
862-
let disposition = match (access & c::GENERIC_READ > 0, access & c::GENERIC_WRITE > 0) {
863-
(true, true) => c::FILE_OPEN_IF,
864-
(true, false) => c::FILE_OPEN,
865-
(false, true) => c::FILE_CREATE,
866-
(false, false) => {
867-
return Err(WinError::new(c::ERROR_INVALID_PARAMETER));
868-
}
869-
};
887+
let access = access | c::SYNCHRONIZE;
888+
let options = if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE }
889+
| c::FILE_SYNCHRONOUS_IO_NONALERT;
870890
let status = unsafe {
871891
c::NtCreateFile(
872892
&mut handle,
@@ -877,7 +897,7 @@ unsafe fn nt_create_file(
877897
c::FILE_ATTRIBUTE_NORMAL,
878898
share,
879899
disposition,
880-
if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE },
900+
options,
881901
ptr::null(),
882902
0,
883903
)
@@ -908,38 +928,48 @@ fn run_path_with_wcstr<T, P: AsRef<Path>>(
908928
f(path)
909929
}
910930

931+
fn run_path_with_utf16<T, P: AsRef<Path>>(
932+
path: P,
933+
f: &dyn Fn(&[u16]) -> io::Result<T>,
934+
) -> io::Result<T> {
935+
let utf16: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
936+
f(&utf16)
937+
}
938+
911939
impl Dir {
912940
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
913941
let opts = OpenOptions::new();
914-
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
942+
Self::new_native(path.as_ref(), &opts)
915943
}
916944

917945
pub fn new_with<P: AsRef<Path>>(path: P, opts: &OpenOptions) -> io::Result<Self> {
918-
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
946+
Self::new_native(path.as_ref(), &opts)
919947
}
920948

921949
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
922950
let mut opts = OpenOptions::new();
951+
let path = path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>();
923952
opts.read(true);
924-
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
953+
Ok(File { handle: self.open_native(&path, &opts)? })
925954
}
926955

927956
pub fn open_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<File> {
928-
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
957+
let path = path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>();
958+
Ok(File { handle: self.open_native(&path, &opts)? })
929959
}
930960

931961
pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
932962
let mut opts = OpenOptions::new();
933963
opts.write(true);
934-
run_path_with_wcstr(path, &|path| self.create_dir_native(path, &opts).map(|_| ()))
964+
run_path_with_utf16(path, &|path| self.create_dir_native(path, &opts).map(|_| ()))
935965
}
936966

937967
pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
938-
run_path_with_wcstr(path, &|path| self.remove_native(path, false))
968+
run_path_with_utf16(path, &|path| self.remove_native(path, false))
939969
}
940970

941971
pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
942-
run_path_with_wcstr(path, &|path| self.remove_native(path, true))
972+
run_path_with_utf16(path, &|path| self.remove_native(path, true))
943973
}
944974

945975
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
@@ -948,36 +978,18 @@ impl Dir {
948978
to_dir: &Self,
949979
to: Q,
950980
) -> io::Result<()> {
951-
run_path_with_wcstr(from.as_ref(), &|from| {
952-
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from, to_dir, to))
953-
})
981+
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from.as_ref(), to_dir, to))
954982
}
955983

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

977-
fn open_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
989+
fn open_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
978990
let name = c::UNICODE_STRING {
979-
Length: path.count_bytes() as _,
980-
MaximumLength: path.count_bytes() as _,
991+
Length: path.len() as _,
992+
MaximumLength: path.len() as _,
981993
Buffer: path.as_ptr() as *mut _,
982994
};
983995
let object_attributes = c::OBJECT_ATTRIBUTES {
@@ -989,14 +1001,22 @@ impl Dir {
9891001
SecurityQualityOfService: ptr::null(),
9901002
};
9911003
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
992-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, false) }
993-
.io_result()
1004+
unsafe {
1005+
nt_create_file(
1006+
opts.get_access_mode()?,
1007+
opts.get_disposition()?,
1008+
&object_attributes,
1009+
share,
1010+
false,
1011+
)
1012+
}
1013+
.io_result()
9941014
}
9951015

996-
fn create_dir_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
1016+
fn create_dir_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
9971017
let name = c::UNICODE_STRING {
998-
Length: path.count_bytes() as _,
999-
MaximumLength: path.count_bytes() as _,
1018+
Length: path.len() as _,
1019+
MaximumLength: path.len() as _,
10001020
Buffer: path.as_ptr() as *mut _,
10011021
};
10021022
let object_attributes = c::OBJECT_ATTRIBUTES {
@@ -1008,11 +1028,19 @@ impl Dir {
10081028
SecurityQualityOfService: ptr::null(),
10091029
};
10101030
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
1011-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
1012-
.io_result()
1031+
unsafe {
1032+
nt_create_file(
1033+
opts.get_access_mode()?,
1034+
opts.get_disposition()?,
1035+
&object_attributes,
1036+
share,
1037+
true,
1038+
)
1039+
}
1040+
.io_result()
10131041
}
10141042

1015-
fn remove_native(&self, path: &WCStr, dir: bool) -> io::Result<()> {
1043+
fn remove_native(&self, path: &[u16], dir: bool) -> io::Result<()> {
10161044
let mut opts = OpenOptions::new();
10171045
opts.access_mode(c::GENERIC_WRITE);
10181046
let handle =
@@ -1029,24 +1057,54 @@ impl Dir {
10291057
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
10301058
}
10311059

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

0 commit comments

Comments
 (0)