Skip to main content

nu_command/platform/
umask_.rs

1use std::ops::Deref;
2
3use nix::libc::mode_t;
4use nu_engine::command_prelude::*;
5use nu_system::get_umask;
6
7/// Wraps umask::Mode, providing conversions to and from mode_t regardless of its
8/// size.
9//
10// The nix::sys::stat::Mode struct (used for setting the umask) only provides
11// conversions to and from nix::libc::mode_t, the size of which is
12// platform-dependant. However, umask::Mode (used for parsing and formatting)
13// only provides conversions for u32, which causes problems on platforms where
14// mode_t is u16.
15struct Mode(umask::Mode);
16
17impl Deref for Mode {
18    type Target = umask::Mode;
19
20    fn deref(&self) -> &Self::Target {
21        &self.0
22    }
23}
24
25impl From<Mode> for mode_t {
26    #[allow(clippy::unnecessary_cast)]
27    fn from(mode: Mode) -> Self {
28        // This is "u32 as u16" or "u32 as u32", depending on platform.
29        u32::from(mode.0) as mode_t
30    }
31}
32
33impl From<mode_t> for Mode {
34    #[allow(clippy::unnecessary_cast)]
35    fn from(value: mode_t) -> Self {
36        // This is "u16 as u32" or "u32 as u32", depending on platform.
37        Self((value as u32).into())
38    }
39}
40
41#[derive(Clone)]
42pub struct UMask;
43
44impl Command for UMask {
45    fn name(&self) -> &str {
46        "umask"
47    }
48
49    fn description(&self) -> &str {
50        "Get or set default file creation permissions."
51    }
52
53    fn extra_description(&self) -> &str {
54        "When setting a new mask, the previous mask will be returned."
55    }
56
57    fn search_terms(&self) -> Vec<&str> {
58        vec!["permissions", "create", "file", "directory", "folder"]
59    }
60
61    fn signature(&self) -> Signature {
62        Signature::build("umask")
63            .input_output_types(vec![(Type::Nothing, Type::String)])
64            .optional(
65                "permissions",
66                SyntaxShape::String,
67                "The permissions to set on created files.",
68            )
69            .category(Category::Platform)
70    }
71
72    fn run(
73        &self,
74        engine_state: &EngineState,
75        stack: &mut Stack,
76        call: &Call,
77        _input: PipelineData,
78    ) -> Result<PipelineData, ShellError> {
79        let maybe_perms_val = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
80
81        let prev_mask_bits = if let Some(perms_val) = maybe_perms_val {
82            let perms = Mode(
83                perms_val
84                    .item
85                    .parse()
86                    .map_err(|err| ShellError::IncorrectValue {
87                        msg: format!("Invalid mode: {0}.", err),
88                        val_span: perms_val.span,
89                        call_span: call.head,
90                    })?,
91            );
92
93            // The `umask` syscall wants the bits to mask *out*, not *in*, so
94            // the mask needs inverted before passing it in.
95            let mask_bits = 0o777 ^ mode_t::from(perms);
96
97            let mask =
98                nix::sys::stat::Mode::from_bits(mask_bits).ok_or(ShellError::IncorrectValue {
99                    // Can't happen? The umask crate shouldn't ever set bits
100                    // which the nix crate doesn't recognize.
101                    msg: "Invalid mask; unrecognized permission bits.".into(),
102                    val_span: perms_val.span,
103                    call_span: call.head,
104                })?;
105
106            nix::sys::stat::umask(mask).bits()
107        } else {
108            get_umask() as mode_t
109        };
110
111        // The `umask` syscall wants the bits to mask *out*, not *in*, so
112        // the old mask needs uninverted before outputting it.
113        let prev_perms = Mode::from(0o777 ^ prev_mask_bits);
114
115        Ok(Value::string(prev_perms.to_string(), call.head).into_pipeline_data())
116    }
117
118    fn examples(&self) -> Vec<Example<'_>> {
119        vec![
120            Example {
121                description: "Print current default file creation permissions.",
122                example: "umask",
123                result: None,
124            },
125            Example {
126                description: "Make new files read-only to group and inaccessible to others.",
127                example: "umask rwxr-x---",
128                result: None,
129            },
130        ]
131    }
132}