Skip to content

Commit 9332c13

Browse files
committed
std: clarify OpenOptions error for create without write access
Fixes #140621
1 parent 3acb261 commit 9332c13

File tree

4 files changed

+85
-14
lines changed

4 files changed

+85
-14
lines changed

library/std/src/fs.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,10 @@ impl OpenOptions {
10311031
/// See also [`std::fs::write()`][self::write] for a simple function to
10321032
/// create a file with a given data.
10331033
///
1034+
/// # Errors
1035+
///
1036+
/// If `.create(true)` is set without `.write(true)` or `.append(true)`,
1037+
/// calling [`.open()`] will fail with [`InvalidInput`] error.
10341038
/// # Examples
10351039
///
10361040
/// ```no_run
@@ -1097,7 +1101,8 @@ impl OpenOptions {
10971101
/// * [`AlreadyExists`]: `create_new` was specified and the file already
10981102
/// exists.
10991103
/// * [`InvalidInput`]: Invalid combinations of open options (truncate
1100-
/// without write access, no access mode set, etc.).
1104+
/// without write access, create without write or append access,
1105+
/// no access mode set, etc.).
11011106
///
11021107
/// The following errors don't match any existing [`io::ErrorKind`] at the moment:
11031108
/// * One of the directory components of the specified file path

library/std/src/fs/tests.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::mem::MaybeUninit;
77
use crate::path::Path;
88
use crate::str;
99
use crate::sync::Arc;
10-
use crate::sys_common::io::test::{tmpdir, TempDir};
10+
use crate::sys_common::io::test::{TempDir, tmpdir};
1111
use crate::thread;
1212
use crate::time::{Duration, Instant, SystemTime};
1313

@@ -1091,12 +1091,7 @@ fn open_flavors() {
10911091
let mut ra = OO::new();
10921092
ra.read(true).append(true);
10931093

1094-
#[cfg(windows)]
1095-
let invalid_options = 87; // ERROR_INVALID_PARAMETER
1096-
#[cfg(all(unix, not(target_os = "vxworks")))]
1097-
let invalid_options = "Invalid argument";
1098-
#[cfg(target_os = "vxworks")]
1099-
let invalid_options = "invalid argument";
1094+
let invalid_options = "creating or truncating a file requires write or append access";
11001095

11011096
// Test various combinations of creation modes and access modes.
11021097
//
@@ -1793,3 +1788,34 @@ fn windows_unix_socket_exists() {
17931788
assert_eq!(socket_path.try_exists().unwrap(), true);
17941789
assert_eq!(socket_path.metadata().is_ok(), true);
17951790
}
1791+
1792+
#[test]
1793+
fn test_open_options_invalid_combinations() {
1794+
// create without write access
1795+
let result = OpenOptions::new().create(true).read(true).open("nonexistent.txt");
1796+
assert!(result.is_err());
1797+
let err = result.unwrap_err();
1798+
assert_eq!(err.kind(), ErrorKind::InvalidInput);
1799+
assert_eq!(err.to_string(), "creating or truncating a file requires write or append access");
1800+
1801+
// create_new without write access
1802+
let result = OpenOptions::new().create_new(true).read(true).open("nonexistent.txt");
1803+
assert!(result.is_err());
1804+
let err = result.unwrap_err();
1805+
assert_eq!(err.kind(), ErrorKind::InvalidInput);
1806+
assert_eq!(err.to_string(), "creating or truncating a file requires write or append access");
1807+
1808+
// truncate without write access
1809+
let result = OpenOptions::new().truncate(true).read(true).open("nonexistent.txt");
1810+
assert!(result.is_err());
1811+
let err = result.unwrap_err();
1812+
assert_eq!(err.kind(), ErrorKind::InvalidInput);
1813+
assert_eq!(err.to_string(), "creating or truncating a file requires write or append access");
1814+
1815+
// no access mode at all
1816+
let result = OpenOptions::new().open("nonexistent.txt");
1817+
assert!(result.is_err());
1818+
let err = result.unwrap_err();
1819+
assert_eq!(err.kind(), ErrorKind::InvalidInput);
1820+
assert_eq!(err.to_string(), "must specify at least one of read, write, or append access");
1821+
}

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,21 @@ impl OpenOptions {
10871087
(true, true, false) => Ok(libc::O_RDWR),
10881088
(false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
10891089
(true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
1090-
(false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
1090+
(false, false, false) => {
1091+
// If no access mode is set, check if any creation flags are set
1092+
// to provide a more descriptive error message
1093+
if self.create || self.create_new || self.truncate {
1094+
Err(io::const_io_error!(
1095+
io::ErrorKind::InvalidInput,
1096+
"creating or truncating a file requires write or append access",
1097+
))
1098+
} else {
1099+
Err(io::const_io_error!(
1100+
io::ErrorKind::InvalidInput,
1101+
"must specify at least one of read, write, or append access",
1102+
))
1103+
}
1104+
}
10911105
}
10921106
}
10931107

@@ -1096,12 +1110,18 @@ impl OpenOptions {
10961110
(true, false) => {}
10971111
(false, false) => {
10981112
if self.truncate || self.create || self.create_new {
1099-
return Err(Error::from_raw_os_error(libc::EINVAL));
1113+
return Err(io::const_io_error!(
1114+
io::ErrorKind::InvalidInput,
1115+
"creating or truncating a file requires write or append access",
1116+
));
11001117
}
11011118
}
11021119
(_, true) => {
11031120
if self.truncate && !self.create_new {
1104-
return Err(Error::from_raw_os_error(libc::EINVAL));
1121+
return Err(io::const_io_error!(
1122+
io::ErrorKind::InvalidInput,
1123+
"creating or truncating a file requires write or append access",
1124+
));
11051125
}
11061126
}
11071127
}

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,21 @@ impl OpenOptions {
248248
(true, _, true, None) => {
249249
Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA))
250250
}
251-
(false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)),
251+
(false, false, false, None) => {
252+
// If no access mode is set, check if any creation flags are set
253+
// to provide a more descriptive error message
254+
if self.create || self.create_new || self.truncate {
255+
Err(io::const_io_error!(
256+
io::ErrorKind::InvalidInput,
257+
"creating or truncating a file requires write or append access",
258+
))
259+
} else {
260+
Err(io::const_io_error!(
261+
io::ErrorKind::InvalidInput,
262+
"must specify at least one of read, write, or append access",
263+
))
264+
}
265+
}
252266
}
253267
}
254268

@@ -259,12 +273,18 @@ impl OpenOptions {
259273
(true, false) => {}
260274
(false, false) => {
261275
if self.truncate || self.create || self.create_new {
262-
return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
276+
return Err(io::const_io_error!(
277+
io::ErrorKind::InvalidInput,
278+
"creating or truncating a file requires write or append access",
279+
));
263280
}
264281
}
265282
(_, true) => {
266283
if self.truncate && !self.create_new {
267-
return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
284+
return Err(io::const_io_error!(
285+
io::ErrorKind::InvalidInput,
286+
"creating or truncating a file requires write or append access",
287+
));
268288
}
269289
}
270290
}

0 commit comments

Comments
 (0)