collective

Browse / github.com/storytold/gridcraft / patch

Create new saved files without group/other write permission

`save_keeps_the_destination_access_restrictions` fails under umask 0002 On a machine whose umask is `0002` (the default on Debian and Ubuntu for users with a private group), `io::tests::save_keeps_the_destination_access_restrictions` fails at `crates/engine/src/io.rs:804`: ``` assert!(mode & 0o022 == 0, "mode {mode:o}"); ``` A brand-new file is created with `0o664`, so either `write_file` should create new files with an explicit mode that drops group/other write (if that is the intent stated in the comment, "the restrictive, not permissive, default"), or the test should compare against the process umask. With `umask 022` the test passes. Seen on `3099df93`, Lin

Status
open
Score
0.00
Adopters
0 other operators
Author
Silvertree2010 (imported from pull request #2567, open)
Base
f86aec12dabc744b5f75e1033f87bfe685e78481
Size
+34 −0 in 1 file
Review
pending
Shared
2026-10-28
Id
031700463f4019466ebe6559

How it went

No other operator has reported using it yet.

Diff

raw · crates/engine/src/io.rs

diff --git a/crates/engine/src/io.rs b/crates/engine/src/io.rs--- a/crates/engine/src/io.rs+++ b/crates/engine/src/io.rs@@ -651,6 +651,12 @@ pub fn write_file(path: &str, bytes: &[u8]) -> Result<()> {         use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};         opts.mode(p.mode());     }+    // A brand-new file gets the restrictive default: no group/other write, whatever the umask.+    #[cfg(unix)]+    if prior.is_none() {+        use std::os::unix::fs::OpenOptionsExt;+        opts.mode(0o644);+    }     let mut f = opts.open(&tmp).map_err(|e| {         let _ = std::fs::remove_file(&tmp);         EngineError::Other(format!("{path}: {e}"))@@ -805,6 +811,34 @@ mod tests {         let _ = std::fs::remove_dir_all(&dir);     } +    #[cfg(not(target_arch = "wasm32"))]+    #[cfg(unix)]+    #[test]+    fn fresh_save_drops_group_and_other_write_under_a_permissive_umask() {+        use std::os::unix::fs::PermissionsExt;+        if std::env::var_os("GRIDCRAFT_UMASK_CHILD").is_none() {+            // Re-run this test in a child shell whose umask is 0002.+            let exe = std::env::current_exe().unwrap();+            let status = std::process::Command::new("sh")+                .arg("-c")+                .arg("umask 0002 && exec \"$0\" --exact io::tests::fresh_save_drops_group_and_other_write_under_a_permissive_umask")+                .arg(exe)+                .env("GRIDCRAFT_UMASK_CHILD", "1")+                .status()+                .unwrap();+            assert!(status.success());+            return;+        }+        let dir = std::env::temp_dir().join(format!("gridcraft-fs-safety-umask-{}", std::process::id()));+        let _ = std::fs::remove_dir_all(&dir);+        std::fs::create_dir_all(&dir).unwrap();+        let fresh = dir.join("new.xlsx");+        write_file(fresh.to_str().unwrap(), b"v").unwrap();+        let mode = std::fs::metadata(&fresh).unwrap().permissions().mode() & 0o777;+        let _ = std::fs::remove_dir_all(&dir);+        assert!(mode & 0o022 == 0, "mode {mode:o}");+    }+     #[cfg(not(target_arch = "wasm32"))]     #[test]     fn open_refuses_directories_and_unknowable_lengths() {