nohup: create nohup.out with mode 0o600 regardless of umask

This commit is contained in:
Christopher Dryden 2025-12-17 14:54:19 +00:00
parent c085cd1c21
commit cb3611da84
4 changed files with 51 additions and 6 deletions

1
Cargo.lock generated
View file

@ -3603,6 +3603,7 @@ dependencies = [
"clap",
"fluent",
"libc",
"nix",
"thiserror 2.0.17",
"uucore",
]

View file

@ -20,6 +20,7 @@ path = "src/nohup.rs"
[dependencies]
clap = { workspace = true }
libc = { workspace = true }
nix = { workspace = true, features = ["fs"] }
uucore = { workspace = true, features = ["fs"] }
thiserror = { workspace = true }
fluent = { workspace = true }

View file

@ -7,9 +7,11 @@
use clap::{Arg, ArgAction, Command};
use libc::{SIG_IGN, SIGHUP, dup2, signal};
use nix::sys::stat::{Mode, umask};
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{Error, ErrorKind, IsTerminal};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::prelude::*;
use std::os::unix::process::CommandExt;
use std::path::{Path, PathBuf};
@ -123,17 +125,31 @@ fn replace_fds() -> UResult<()> {
Ok(())
}
/// Open nohup.out file with mode 0o600, temporarily clearing umask.
/// The umask is cleared to ensure the file is created with exactly 0o600 permissions.
fn open_nohup_file(path: &Path) -> std::io::Result<File> {
// Clear umask (set it to 0) and save the old value
let old_umask = umask(Mode::from_bits_truncate(0));
let result = OpenOptions::new()
.create(true)
.append(true)
.mode(0o600)
.open(path);
// Restore previous umask
umask(old_umask);
result
}
fn find_stdout() -> UResult<File> {
let internal_failure_code = match env::var("POSIXLY_CORRECT") {
Ok(_) => POSIX_NOHUP_FAILURE,
Err(_) => EXIT_CANCELED,
};
match OpenOptions::new()
.create(true)
.append(true)
.open(Path::new(NOHUP_OUT))
{
match open_nohup_file(Path::new(NOHUP_OUT)) {
Ok(t) => {
show_error!(
"{}",
@ -148,7 +164,7 @@ fn find_stdout() -> UResult<File> {
let mut homeout = PathBuf::from(home);
homeout.push(NOHUP_OUT);
let homeout_str = homeout.to_str().unwrap();
match OpenOptions::new().create(true).append(true).open(&homeout) {
match open_nohup_file(&homeout) {
Ok(t) => {
show_error!(
"{}",

View file

@ -3,9 +3,11 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore winsize Openpty openpty xpixel ypixel ptyprocess
use std::os::unix::fs::PermissionsExt;
use std::thread::sleep;
use uutests::at_and_ucmd;
use uutests::new_ucmd;
use uutests::util::TerminalSimulation;
use uutests::util::TestScenario;
use uutests::util_name;
@ -238,3 +240,28 @@ fn test_nohup_stderr_to_stdout() {
assert!(content.contains("stdout message"));
assert!(content.contains("stderr message"));
}
#[test]
fn test_nohup_file_permissions_ignore_umask_always_o600() {
for umask_val in [0o077, 0o000] {
let ts = TestScenario::new(util_name!());
ts.ucmd()
.terminal_sim_stdio(TerminalSimulation {
stdin: true,
stdout: true,
stderr: true,
size: None,
})
.umask(umask_val)
.args(&["echo", "test"])
.succeeds();
sleep(std::time::Duration::from_millis(10));
let mode = std::fs::metadata(ts.fixtures.plus_as_string("nohup.out"))
.unwrap()
.permissions()
.mode()
& 0o777;
assert_eq!(mode, 0o600, "with umask {umask_val:o}, got mode {mode:o}");
}
}