diff --git a/src/autoload.rs b/src/autoload.rs index 3568a1e17..323f70a7d 100644 --- a/src/autoload.rs +++ b/src/autoload.rs @@ -336,7 +336,19 @@ fn test_autoload() { } fn touch_file(path: &wstr) { - let fd = wopen_cloexec(path, O_RDWR | O_CREAT, 0o666).unwrap(); + use nix::sys::stat::Mode; + + let fd = wopen_cloexec( + path, + O_RDWR | O_CREAT, + Mode::S_IRUSR + | Mode::S_IWUSR + | Mode::S_IRGRP + | Mode::S_IWGRP + | Mode::S_IROTH + | Mode::S_IWOTH, + ) + .unwrap(); write_loop(&fd, "Hello".as_bytes()).unwrap(); unsafe { libc::close(fd) }; } diff --git a/src/builtins/cd.rs b/src/builtins/cd.rs index 4bbc7c7e7..ccb461ef7 100644 --- a/src/builtins/cd.rs +++ b/src/builtins/cd.rs @@ -9,6 +9,7 @@ use crate::{ }; use errno::{self, Errno}; use libc::{fchdir, EACCES, ELOOP, ENOENT, ENOTDIR, EPERM, O_RDONLY}; +use nix::sys::stat::Mode; use std::{os::fd::AsRawFd, sync::Arc}; // The cd builtin. Changes the current directory to the one specified or to $HOME if none is @@ -86,7 +87,7 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Optio errno::set_errno(Errno(0)); - let res = wopen_cloexec(&norm_dir, O_RDONLY, 0) + let res = wopen_cloexec(&norm_dir, O_RDONLY, Mode::empty()) .map(AutoCloseFd::new) .map_err(|err| err as i32); diff --git a/src/builtins/source.rs b/src/builtins/source.rs index 5e9dbf460..f3b6c5e95 100644 --- a/src/builtins/source.rs +++ b/src/builtins/source.rs @@ -8,6 +8,7 @@ use crate::{ reader::reader_read, }; use libc::{c_int, O_RDONLY, S_IFMT, S_IFREG}; +use nix::sys::stat::Mode; use super::prelude::*; @@ -50,7 +51,7 @@ pub fn source(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O func_filename = FilenameRef::new(L!("-").to_owned()); fd = streams.stdin_fd; } else { - let Ok(raw_fd) = wopen_cloexec(args[optind], O_RDONLY, 0) else { + let Ok(raw_fd) = wopen_cloexec(args[optind], O_RDONLY, Mode::empty()) else { let esc = escape(args[optind]); streams.err.append(wgettext_fmt!( "%ls: Error encountered while sourcing file '%ls':\n", diff --git a/src/env_universal_common.rs b/src/env_universal_common.rs index 875c2b5fe..0ca6f3034 100644 --- a/src/env_universal_common.rs +++ b/src/env_universal_common.rs @@ -20,6 +20,7 @@ use crate::wutil::{ }; use errno::{errno, Errno}; use libc::{EINTR, LOCK_EX, O_CREAT, O_RDONLY, O_RDWR}; +use nix::sys::stat::Mode; use std::collections::hash_map::Entry; use std::collections::HashSet; use std::ffi::CString; @@ -388,7 +389,7 @@ impl EnvUniversal { return true; } - let Ok(raw_fd) = open_cloexec(&self.narrow_vars_path, O_RDONLY, 0) else { + let Ok(raw_fd) = open_cloexec(&self.narrow_vars_path, O_RDONLY, Mode::empty()) else { return false; }; @@ -441,7 +442,11 @@ impl EnvUniversal { let mut res_fd = None; while res_fd.is_none() { - let raw = match wopen_cloexec(&self.vars_path, flags, 0o644) { + let raw = match wopen_cloexec( + &self.vars_path, + flags, + Mode::S_IRUSR | Mode::S_IWUSR | Mode::S_IRGRP | Mode::S_IROTH, + ) { Ok(raw) => raw, Err(err) => { if err == nix::Error::EINTR { diff --git a/src/exec.rs b/src/exec.rs index 9e6633b45..2d7088ebd 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -53,6 +53,7 @@ use libc::{ c_char, EACCES, ENOENT, ENOEXEC, ENOTDIR, EPIPE, EXIT_FAILURE, EXIT_SUCCESS, O_NOCTTY, O_RDONLY, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, }; +use nix::sys::stat; use std::ffi::CStr; use std::io::{Read, Write}; use std::os::fd::{FromRawFd, RawFd}; @@ -366,7 +367,7 @@ pub fn is_thompson_shell_script(path: &CStr) -> bool { } let e = errno(); let mut res = false; - let fd = open_cloexec(path, O_RDONLY | O_NOCTTY, 0); + let fd = open_cloexec(path, O_RDONLY | O_NOCTTY, stat::Mode::empty()); if let Ok(fd) = fd { let mut file = unsafe { std::fs::File::from_raw_fd(fd) }; let mut buf = [b'\0'; 256]; diff --git a/src/fds.rs b/src/fds.rs index 8c9761209..eee768e21 100644 --- a/src/fds.rs +++ b/src/fds.rs @@ -222,14 +222,17 @@ pub fn set_cloexec(fd: RawFd, should_set: bool /* = true */) -> c_int { /// Wide character version of open() that also sets the close-on-exec flag (atomically when /// possible). -pub fn wopen_cloexec(pathname: &wstr, flags: i32, mode: libc::c_int) -> nix::Result { +pub fn wopen_cloexec( + pathname: &wstr, + flags: i32, + mode: nix::sys::stat::Mode, +) -> nix::Result { open_cloexec(wcs2zstring(pathname).as_c_str(), flags, mode) } /// Narrow versions of wopen_cloexec. -pub fn open_cloexec(path: &CStr, flags: i32, mode: libc::c_int) -> nix::Result { +pub fn open_cloexec(path: &CStr, flags: i32, mode: nix::sys::stat::Mode) -> nix::Result { let flags = unsafe { OFlag::from_bits_unchecked(flags) }; - let mode = unsafe { nix::sys::stat::Mode::from_bits_unchecked(mode as u32) }; // Port note: the C++ version of this function had a fallback for platforms where // O_CLOEXEC is not supported, using fcntl. In 2023, this is no longer needed. let saved_errno = errno(); diff --git a/src/history.rs b/src/history.rs index 60491fa0e..f764bddc0 100644 --- a/src/history.rs +++ b/src/history.rs @@ -36,6 +36,7 @@ use libc::{ O_RDONLY, O_WRONLY, SEEK_SET, }; use lru::LruCache; +use nix::sys::stat::Mode; use rand::Rng; use crate::{ @@ -130,7 +131,7 @@ const HISTORY_SAVE_MAX: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1024 const HISTORY_OUTPUT_BUFFER_SIZE: usize = 64 * 1024; /// The file access mode we use for creating history files -const HISTORY_FILE_MODE: i32 = 0o600; +const HISTORY_FILE_MODE: Mode = Mode::S_IRUSR.union(Mode::S_IWUSR); /// How many times we retry to save /// Saving may fail if the file is modified in between our opening @@ -480,7 +481,7 @@ impl HistoryImpl { let _profiler = TimeProfiler::new("load_old"); if let Some(filename) = history_filename(&self.name, L!("")) { - let Ok(raw_fd) = wopen_cloexec(&filename, O_RDONLY, 0) else { + let Ok(raw_fd) = wopen_cloexec(&filename, O_RDONLY, Mode::empty()) else { return; }; @@ -697,7 +698,7 @@ impl HistoryImpl { // If the open fails, then proceed; this may be because there is no current history let mut new_file_id = INVALID_FILE_ID; - let target_fd_after = wopen_cloexec(&target_name, O_RDONLY, 0) + let target_fd_after = wopen_cloexec(&target_name, O_RDONLY, Mode::empty()) .map(|raw_fd| unsafe { OwnedFd::from_raw_fd(raw_fd) }); if let Ok(target_fd_after) = target_fd_after.as_ref() { @@ -814,7 +815,7 @@ impl HistoryImpl { // Limit our max tries so we don't do this forever. let mut history_fd = None; for _i in 0..MAX_SAVE_TRIES { - let Ok(fd) = wopen_cloexec(&history_path, O_WRONLY | O_APPEND, 0) else { + let Ok(fd) = wopen_cloexec(&history_path, O_WRONLY | O_APPEND, Mode::empty()) else { // can't open, we're hosed break; }; @@ -1105,7 +1106,7 @@ impl HistoryImpl { old_file.push_utfstr(&self.name); old_file.push_str("_history"); - let Ok(src_fd) = wopen_cloexec(&old_file, O_RDONLY, 0) else { + let Ok(src_fd) = wopen_cloexec(&old_file, O_RDONLY, Mode::empty()) else { return; }; diff --git a/src/io.rs b/src/io.rs index 5f3b8caeb..9be8a0683 100644 --- a/src/io.rs +++ b/src/io.rs @@ -18,6 +18,7 @@ use crate::wchar::prelude::*; use crate::wutil::{perror, perror_io, wdirname, wstat, wwrite_to_fd}; use errno::Errno; use libc::{EAGAIN, EINTR, ENOENT, ENOTDIR, EPIPE, EWOULDBLOCK, O_EXCL, STDOUT_FILENO}; +use nix::sys::stat::Mode; use std::cell::{RefCell, UnsafeCell}; use std::os::fd::RawFd; use std::sync::atomic::{AtomicU64, Ordering}; @@ -1006,7 +1007,12 @@ const FILE_ERROR: &wstr = L!("An error occurred while redirecting file '%ls'"); const NOCLOB_ERROR: &wstr = L!("The file '%ls' already exists"); /// Base open mode to pass to calls to open. -const OPEN_MASK: libc::c_int = 0o666; +const OPEN_MASK: Mode = Mode::S_IRUSR + .union(Mode::S_IWUSR) + .union(Mode::S_IRGRP) + .union(Mode::S_IWGRP) + .union(Mode::S_IROTH) + .union(Mode::S_IWOTH); /// Provide the fd monitor used for background fillthread operations. fn fd_monitor() -> &'static mut FdMonitor { diff --git a/src/parser.rs b/src/parser.rs index 1d80eb127..0a29cfaac 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -34,6 +34,7 @@ use crate::wchar::{wstr, WString, L}; use crate::wutil::{perror, wgettext, wgettext_fmt}; use libc::c_int; use libc::O_RDONLY; +use nix::sys::stat::Mode; use once_cell::sync::Lazy; use printf_compat::sprintf; use std::cell::{Ref, RefCell, RefMut}; @@ -358,7 +359,11 @@ impl Parser { global_event_blocks: AtomicU64::new(0), }); - match open_cloexec(CStr::from_bytes_with_nul(b".\0").unwrap(), O_RDONLY, 0) { + match open_cloexec( + CStr::from_bytes_with_nul(b".\0").unwrap(), + O_RDONLY, + Mode::empty(), + ) { Ok(raw_fd) => { result.libdata_mut().cwd_fd = Some(Arc::new(AutoCloseFd::new(raw_fd))); } diff --git a/src/reader.rs b/src/reader.rs index 68e646531..280ff848e 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -17,6 +17,7 @@ use libc::{ IXOFF, IXON, ONLCR, OPOST, O_NONBLOCK, O_RDONLY, SIGINT, SIGTTIN, STDIN_FILENO, STDOUT_FILENO, S_IFDIR, TCSANOW, VMIN, VQUIT, VSUSP, VTIME, _POSIX_VDISABLE, }; +use nix::sys::stat::Mode; use once_cell::sync::Lazy; use std::cell::UnsafeCell; use std::io::BufReader; @@ -4659,7 +4660,7 @@ impl ReaderData { var.map_or_else(|| L!("~/.bash_history").to_owned(), |var| var.as_string()); expand_tilde(&mut path, self.vars()); - let Ok(raw_fd) = wopen_cloexec(&path, O_RDONLY, 0) else { + let Ok(raw_fd) = wopen_cloexec(&path, O_RDONLY, Mode::empty()) else { return; }; diff --git a/src/tests/history.rs b/src/tests/history.rs index c5c038452..0e645715f 100644 --- a/src/tests/history.rs +++ b/src/tests/history.rs @@ -8,6 +8,7 @@ use crate::tests::string_escape::ESCAPE_TEST_CHAR; use crate::wchar::prelude::*; use crate::wcstringutil::{string_prefixes_string, string_prefixes_string_case_insensitive}; use libc::O_RDONLY; +use nix::sys::stat::Mode; use rand::random; use std::collections::VecDeque; use std::ffi::CString; @@ -594,8 +595,9 @@ fn test_history_formats() { "echo foo".into(), ]; let test_history_imported_from_bash = History::with_name(L!("bash_import")); - let file = - AutoCloseFd::new(wopen_cloexec(L!("tests/history_sample_bash"), O_RDONLY, 0).unwrap()); + let file = AutoCloseFd::new( + wopen_cloexec(L!("tests/history_sample_bash"), O_RDONLY, Mode::empty()).unwrap(), + ); test_history_imported_from_bash.populate_from_bash(BufReader::new(file)); assert_eq!(test_history_imported_from_bash.get_history(), expected); test_history_imported_from_bash.clear();