Static release carry: explicit 0444 under UMask 0027 #9
1 changed files with 42 additions and 1 deletions
|
|
@ -762,7 +762,7 @@ impl StaticReleasePort for FilesystemStaticRelease {
|
|||
binding: &StaticReleaseBinding,
|
||||
release_id: &str,
|
||||
) -> Result<StaticFlip> {
|
||||
use std::os::unix::fs::{OpenOptionsExt, symlink};
|
||||
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt, symlink};
|
||||
|
||||
// Everything up to and including the rename can refuse, and only that
|
||||
// refuses: the link has not moved when `FlipRefused` is returned.
|
||||
|
|
@ -823,6 +823,10 @@ impl StaticReleasePort for FilesystemStaticRelease {
|
|||
.create_new(true)
|
||||
.mode(0o444)
|
||||
.open(&temporary_carry)?;
|
||||
// The creation mode is masked by the service's umask
|
||||
// (UMask=0027 yields 0440), and the web server reads this file
|
||||
// as "other"; say 0444 on the open handle.
|
||||
file.set_permissions(fs::Permissions::from_mode(0o444))?;
|
||||
file.write_all(&bytes)?;
|
||||
file.sync_all()?;
|
||||
fs::rename(&temporary_carry, &carried)?;
|
||||
|
|
@ -17045,6 +17049,43 @@ processes 9
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// The service runs with UMask=0027, which masks the create mode to 0440
|
||||
/// and hides the carry from the web user. The umask is process-global, so
|
||||
/// the proof runs in a child copy of this test binary and no sibling test
|
||||
/// ever sees it.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn static_release_carry_is_0444_under_the_service_umask() -> Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
const CHILD: &str = "IDUNN_TEST_CARRY_UMASK_CHILD";
|
||||
const NAME: &str = "static_release_carry_is_0444_under_the_service_umask";
|
||||
if std::env::var_os(CHILD).is_none() {
|
||||
let path = format!("{}::{NAME}", module_path!().split_once("::").map_or("", |(_, rest)| rest));
|
||||
let output = std::process::Command::new(std::env::current_exe()?)
|
||||
.args(["--exact", path.as_str(), "--nocapture", "--test-threads=1"])
|
||||
.env(CHILD, "1")
|
||||
.output()?;
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"child under umask 0027 failed:\n{}\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
assert!(
|
||||
String::from_utf8_lossy(&output.stdout).contains("1 passed"),
|
||||
"the child filter matched no test:\n{}",
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
unsafe { libc::umask(0o027) };
|
||||
let site = stand_in_site()?;
|
||||
FilesystemStaticRelease.carry_and_flip(&site.binding, RELEASE_A)?;
|
||||
let carried = site.binding.current_link.join("config.json");
|
||||
assert_eq!(fs::metadata(&carried)?.permissions().mode() & 0o777, 0o444);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn static_release_missing_site_config_fails_before_the_link_moves() -> Result<()> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue