When extracting an archive with files that have group-write permissions (e.g. 0o775 or 0o777 after sanitization), the extracted files lose group-write bits because create_file_with_mode uses OpenOptions::mode(), which is subject to the process umask.
Reproduction
Archive with public.txt at mode 0o777:
cargo run --package exarch-cli -- extract world_writable.tar /dest
stat /dest/public.txt # mode: 0o755 (expected 0o775 after world-write strip)
SecurityConfig::default() strips 0o002 (other-write):
- Expected:
0o777 → 0o775
- Actual:
0o777 → 0o755 (because process umask 022 additionally strips group-write 0o020)
Documentation comment in permissions.rs explicitly documents 0o777 → 0o775, but this promise is not kept.
Root cause
common.rs:create_file_with_mode uses OpenOptions::mode(m), which creates the file with mode & !umask. The process umask (typically 022) strips group-write even when the sanitized mode intends to preserve it.
Fix
After creating the file, call fs::set_permissions(path, Permissions::from_mode(m)) to enforce the exact sanitized mode, bypassing the umask. This is the standard pattern for archive extractors that need to honor explicit permissions.
Affects all extracted files — any mode bit covered by the umask is silently lost.
When extracting an archive with files that have group-write permissions (e.g.
0o775or0o777after sanitization), the extracted files lose group-write bits becausecreate_file_with_modeusesOpenOptions::mode(), which is subject to the process umask.Reproduction
Archive with
public.txtat mode0o777:cargo run --package exarch-cli -- extract world_writable.tar /dest stat /dest/public.txt # mode: 0o755 (expected 0o775 after world-write strip)SecurityConfig::default()strips0o002(other-write):0o777 → 0o7750o777 → 0o755(because process umask022additionally strips group-write0o020)Documentation comment in
permissions.rsexplicitly documents0o777 → 0o775, but this promise is not kept.Root cause
common.rs:create_file_with_modeusesOpenOptions::mode(m), which creates the file withmode & !umask. The process umask (typically022) strips group-write even when the sanitized mode intends to preserve it.Fix
After creating the file, call
fs::set_permissions(path, Permissions::from_mode(m))to enforce the exact sanitized mode, bypassing the umask. This is the standard pattern for archive extractors that need to honor explicit permissions.Affects all extracted files — any mode bit covered by the umask is silently lost.