Skip to content

Commit d7c07cc

Browse files
committed
fix windows functions, documentation
1 parent 79de220 commit d7c07cc

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
@@ -1589,7 +1589,8 @@ impl Dir {
15891589
///
15901590
/// # Errors
15911591
///
1592-
/// This function will return an error in these (and other) situations:
1592+
/// This function may return an error in these (and other) situations, depending on the
1593+
/// specified `opts`:
15931594
/// * The path doesn't exist
15941595
/// * The path doesn't specify a regular file
15951596
/// * 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
@@ -861,20 +885,16 @@ impl File {
861885

862886
unsafe fn nt_create_file(
863887
access: u32,
888+
disposition: u32,
864889
object_attributes: &c::OBJECT_ATTRIBUTES,
865890
share: u32,
866891
dir: bool,
867892
) -> Result<Handle, WinError> {
868893
let mut handle = ptr::null_mut();
869894
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
870-
let disposition = match (access & c::GENERIC_READ > 0, access & c::GENERIC_WRITE > 0) {
871-
(true, true) => c::FILE_OPEN_IF,
872-
(true, false) => c::FILE_OPEN,
873-
(false, true) => c::FILE_CREATE,
874-
(false, false) => {
875-
return Err(WinError::new(c::ERROR_INVALID_PARAMETER));
876-
}
877-
};
895+
let access = access | c::SYNCHRONIZE;
896+
let options = if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE }
897+
| c::FILE_SYNCHRONOUS_IO_NONALERT;
878898
let status = unsafe {
879899
c::NtCreateFile(
880900
&mut handle,
@@ -885,7 +905,7 @@ unsafe fn nt_create_file(
885905
c::FILE_ATTRIBUTE_NORMAL,
886906
share,
887907
disposition,
888-
if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE },
908+
options,
889909
ptr::null(),
890910
0,
891911
)
@@ -916,38 +936,48 @@ fn run_path_with_wcstr<T, P: AsRef<Path>>(
916936
f(path)
917937
}
918938

939+
fn run_path_with_utf16<T, P: AsRef<Path>>(
940+
path: P,
941+
f: &dyn Fn(&[u16]) -> io::Result<T>,
942+
) -> io::Result<T> {
943+
let utf16: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
944+
f(&utf16)
945+
}
946+
919947
impl Dir {
920948
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
921949
let opts = OpenOptions::new();
922-
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
950+
Self::new_native(path.as_ref(), &opts)
923951
}
924952

925953
pub fn new_with<P: AsRef<Path>>(path: P, opts: &OpenOptions) -> io::Result<Self> {
926-
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
954+
Self::new_native(path.as_ref(), &opts)
927955
}
928956

929957
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
930958
let mut opts = OpenOptions::new();
959+
let path = path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>();
931960
opts.read(true);
932-
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
961+
Ok(File { handle: self.open_native(&path, &opts)? })
933962
}
934963

935964
pub fn open_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<File> {
936-
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
965+
let path = path.as_ref().as_os_str().encode_wide().collect::<Vec<_>>();
966+
Ok(File { handle: self.open_native(&path, &opts)? })
937967
}
938968

939969
pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
940970
let mut opts = OpenOptions::new();
941971
opts.write(true);
942-
run_path_with_wcstr(path, &|path| self.create_dir_native(path, &opts).map(|_| ()))
972+
run_path_with_utf16(path, &|path| self.create_dir_native(path, &opts).map(|_| ()))
943973
}
944974

945975
pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
946-
run_path_with_wcstr(path, &|path| self.remove_native(path, false))
976+
run_path_with_utf16(path, &|path| self.remove_native(path, false))
947977
}
948978

949979
pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
950-
run_path_with_wcstr(path, &|path| self.remove_native(path, true))
980+
run_path_with_utf16(path, &|path| self.remove_native(path, true))
951981
}
952982

953983
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
@@ -956,36 +986,18 @@ impl Dir {
956986
to_dir: &Self,
957987
to: Q,
958988
) -> io::Result<()> {
959-
run_path_with_wcstr(from.as_ref(), &|from| {
960-
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from, to_dir, to))
961-
})
989+
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from.as_ref(), to_dir, to))
962990
}
963991

964-
fn new_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
965-
let name = c::UNICODE_STRING {
966-
Length: path.count_bytes() as _,
967-
MaximumLength: path.count_bytes() as _,
968-
Buffer: path.as_ptr() as *mut _,
969-
};
970-
let object_attributes = c::OBJECT_ATTRIBUTES {
971-
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
972-
RootDirectory: ptr::null_mut(),
973-
ObjectName: &name,
974-
Attributes: 0,
975-
SecurityDescriptor: ptr::null(),
976-
SecurityQualityOfService: ptr::null(),
977-
};
978-
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
979-
let handle =
980-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
981-
.io_result()?;
992+
fn new_native(path: &Path, opts: &OpenOptions) -> io::Result<Self> {
993+
let handle = File::open(path, opts)?.into_inner();
982994
Ok(Self { handle })
983995
}
984996

985-
fn open_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
997+
fn open_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
986998
let name = c::UNICODE_STRING {
987-
Length: path.count_bytes() as _,
988-
MaximumLength: path.count_bytes() as _,
999+
Length: path.len() as _,
1000+
MaximumLength: path.len() as _,
9891001
Buffer: path.as_ptr() as *mut _,
9901002
};
9911003
let object_attributes = c::OBJECT_ATTRIBUTES {
@@ -997,14 +1009,22 @@ impl Dir {
9971009
SecurityQualityOfService: ptr::null(),
9981010
};
9991011
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
1000-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, false) }
1001-
.io_result()
1012+
unsafe {
1013+
nt_create_file(
1014+
opts.get_access_mode()?,
1015+
opts.get_disposition()?,
1016+
&object_attributes,
1017+
share,
1018+
false,
1019+
)
1020+
}
1021+
.io_result()
10021022
}
10031023

1004-
fn create_dir_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
1024+
fn create_dir_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result<Handle> {
10051025
let name = c::UNICODE_STRING {
1006-
Length: path.count_bytes() as _,
1007-
MaximumLength: path.count_bytes() as _,
1026+
Length: path.len() as _,
1027+
MaximumLength: path.len() as _,
10081028
Buffer: path.as_ptr() as *mut _,
10091029
};
10101030
let object_attributes = c::OBJECT_ATTRIBUTES {
@@ -1016,11 +1036,19 @@ impl Dir {
10161036
SecurityQualityOfService: ptr::null(),
10171037
};
10181038
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
1019-
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
1020-
.io_result()
1039+
unsafe {
1040+
nt_create_file(
1041+
opts.get_access_mode()?,
1042+
opts.get_disposition()?,
1043+
&object_attributes,
1044+
share,
1045+
true,
1046+
)
1047+
}
1048+
.io_result()
10211049
}
10221050

1023-
fn remove_native(&self, path: &WCStr, dir: bool) -> io::Result<()> {
1051+
fn remove_native(&self, path: &[u16], dir: bool) -> io::Result<()> {
10241052
let mut opts = OpenOptions::new();
10251053
opts.access_mode(c::GENERIC_WRITE);
10261054
let handle =
@@ -1037,24 +1065,54 @@ impl Dir {
10371065
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
10381066
}
10391067

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

0 commit comments

Comments
 (0)