Skip to content

Commit f6ddc9b

Browse files
Merge pull request #274 from haboustak/273-rust-parse-octal-umask
Parse SCUBAINIT_UMASK as an octal-encoded string
2 parents 54a4017 + be0ceae commit f6ddc9b

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

scubainit/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::process::{Command, ExitCode};
1010
use stderrlog::{self, LogLevelNum};
1111

1212
use scubainit::util::{libc_result, make_executable, open_read_append};
13-
use scubainit::util::{pop_env_bool, pop_env_str, pop_env_uint};
13+
use scubainit::util::{pop_env_bool, pop_env_octal, pop_env_str, pop_env_uint};
1414
use scubainit::{groups, passwd, shadow};
1515

1616
const SCUBAINIT_EXIT_FAIL: u8 = 99;
@@ -346,7 +346,7 @@ fn process_envvars() -> Result<Context> {
346346
user_info: process_envvars_user_info()?,
347347

348348
// Optional vars
349-
umask: pop_env_uint(SCUBAINIT_UMASK)?,
349+
umask: pop_env_octal(SCUBAINIT_UMASK)?,
350350

351351
// SCUBAINIT_VERBOSE is popped in setup_logging().
352352

scubainit/src/util.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub fn pop_env_bool(name: &str) -> bool {
5959
result
6060
}
6161

62-
/// Gets and unsets a uint environment variable `name`, or Ok(None) if it is not set.
62+
/// Gets and unsets a decimal uint environment variable `name`, or Ok(None) if it is not set.
6363
///
6464
/// # Errors
6565
///
@@ -71,10 +71,24 @@ pub fn pop_env_uint(name: &str) -> Result<Option<u32>> {
7171
};
7272
let value: u32 = value_str
7373
.parse()
74-
.context(format!("Parsing integer variable {name}=\"{value_str}\""))?;
74+
.with_context(|| format!("Parsing decimal integer variable {name}=\"{value_str}\""))?;
7575
Ok(Some(value))
7676
}
7777

78+
/// Gets and unsets an octal uint environment variable `name`, or Ok(None) if it is not set.
79+
///
80+
/// # Errors
81+
///
82+
/// This function will return an error if the variable is set, but is not a valid integer string.
83+
pub fn pop_env_octal(name: &str) -> Result<Option<u32>> {
84+
let value_str = match pop_env_str(name) {
85+
None => return Ok(None),
86+
Some(s) => s,
87+
};
88+
let value = u32::from_str_radix(&value_str, 8)
89+
.with_context(|| format!("Parsing octal integer variable {name}=\"{value_str}\""))?;
90+
Ok(Some(value))
91+
}
7892
pub fn make_executable(path: &str) -> std::io::Result<()> {
7993
let mut perms = fs::metadata(path)?.permissions();
8094
let mut mode = perms.mode();
@@ -177,4 +191,32 @@ mod tests {
177191
assert!(not_set(VAR_NAME));
178192
});
179193
}
194+
195+
#[test]
196+
fn pop_env_octal_handles_unset() {
197+
temp_env::with_var_unset(VAR_NAME, || {
198+
assert!(not_set(VAR_NAME));
199+
assert_eq!(pop_env_octal(VAR_NAME).unwrap(), None);
200+
});
201+
}
202+
203+
#[test]
204+
fn pop_env_octal_handles_invalid() {
205+
temp_env::with_var(VAR_NAME, Some("99"), || {
206+
assert!(pop_env_octal(VAR_NAME).is_err());
207+
assert!(not_set(VAR_NAME));
208+
});
209+
}
210+
211+
#[test]
212+
fn pop_env_octal_works() {
213+
temp_env::with_var(VAR_NAME, Some("0777"), || {
214+
assert_eq!(pop_env_octal(VAR_NAME).unwrap(), Some(511));
215+
assert!(not_set(VAR_NAME));
216+
});
217+
temp_env::with_var(VAR_NAME, Some("0022"), || {
218+
assert_eq!(pop_env_octal(VAR_NAME).unwrap(), Some(18));
219+
assert!(not_set(VAR_NAME));
220+
});
221+
}
180222
}

tests/test_main.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import scuba.__main__ as main
1818
import scuba.dockerutil
19+
import scuba.utils
1920

2021
from .const import DOCKER_IMAGE
2122
from .utils import (
@@ -1186,3 +1187,26 @@ def test_volumes_named(self) -> None:
11861187
# Invoke scuba again: Verify the file is still there
11871188
out, _ = run_scuba(["/bin/sh", "-c", f"cat {test_path}"])
11881189
assert_str_equalish(out, test_str)
1190+
1191+
1192+
class TestMainUmask(MainTest):
1193+
def test_umask(self) -> None:
1194+
"""Verify umask is set properly"""
1195+
SCUBA_YML.write_text(f"image: {DOCKER_IMAGE}")
1196+
1197+
FAKE_UMASK = 0o123 # unlikely
1198+
1199+
def mocked_get_umask() -> int:
1200+
return FAKE_UMASK
1201+
1202+
# http://alexmarandon.com/articles/python_mock_gotchas/#patching-in-the-wrong-place
1203+
# http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
1204+
with mock.patch("scuba.scuba.get_umask", side_effect=mocked_get_umask) as m:
1205+
args = ["/bin/sh", "-c", "umask"]
1206+
out, _ = run_scuba(args)
1207+
1208+
m.assert_called_once()
1209+
1210+
assert out.startswith("0")
1211+
result_umask = int(out, 8)
1212+
assert result_umask == FAKE_UMASK

0 commit comments

Comments
 (0)