rivia/sys/fs/
chown.rs

1use std::path::PathBuf;
2
3use crate::errors::RvResult;
4
5/// Provides a builder pattern for flexibly changing file ownership
6///
7/// Use the Vfs functions `chown_b` to create a new instance followed by one or more options and
8/// complete the operation by calling `exec`.
9///
10/// ```
11/// use rivia::prelude::*;
12///
13/// let vfs = Memfs::new();
14/// let file1 = vfs.root().mash("file1");
15/// let file2 = vfs.root().mash("file2");
16/// //assert_vfs_write_all!(vfs, &file1, "this is a test");
17/// //assert!(vfs.copy_b(&file1, &file2).unwrap().exec().is_ok());
18/// //assert_eq!(vfs.read_all(&file2).unwrap(), "this is a test");
19/// ```
20pub struct Chown
21{
22    pub(crate) opts: ChownOpts,
23    pub(crate) exec: Box<dyn Fn(ChownOpts) -> RvResult<()>>, // provider callback
24}
25
26// Internal type used to encapsulate just the options. This separates the provider implementation
27// from the options allowing for sharing options between different vfs providers.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub(crate) struct ChownOpts
30{
31    pub(crate) path: PathBuf,    // path to chown
32    pub(crate) uid: Option<u32>, // uid to use
33    pub(crate) gid: Option<u32>, // uid to use
34    pub(crate) follow: bool,     // follow links
35    pub(crate) recursive: bool,  // chown recursiveily
36}
37
38impl Chown
39{
40    /// Set user id to use for ownership for the given path
41    ///
42    /// ### Examples
43    /// ```
44    /// use rivia::prelude::*;
45    ///
46    /// let vfs = Memfs::new();
47    /// let file1 = vfs.root().mash("file1");
48    /// assert_vfs_mkfile!(vfs, &file1);
49    /// assert!(vfs.chown_b(&file1).unwrap().uid(5).exec().is_ok());
50    /// assert_eq!(vfs.uid(&file1).unwrap(), 5);
51    /// assert_eq!(vfs.gid(&file1).unwrap(), 1000);
52    /// ```
53    pub fn uid(mut self, uid: u32) -> Self
54    {
55        self.opts.uid = Some(uid);
56        self
57    }
58
59    /// Set group id to use for ownership for the given path
60    ///
61    /// ### Examples
62    /// ```
63    /// use rivia::prelude::*;
64    ///
65    /// let vfs = Memfs::new();
66    /// let file1 = vfs.root().mash("file1");
67    /// assert_vfs_mkfile!(vfs, &file1);
68    /// assert!(vfs.chown_b(&file1).unwrap().gid(5).exec().is_ok());
69    /// assert_eq!(vfs.uid(&file1).unwrap(), 1000);
70    /// assert_eq!(vfs.gid(&file1).unwrap(), 5);
71    /// ```
72    pub fn gid(mut self, gid: u32) -> Self
73    {
74        self.opts.gid = Some(gid);
75        self
76    }
77
78    /// Set user id and group id to use for ownership for the given path
79    ///
80    /// ### Examples
81    /// ```
82    /// use rivia::prelude::*;
83    ///
84    /// let vfs = Memfs::new();
85    /// let file1 = vfs.root().mash("file1");
86    /// assert_vfs_mkfile!(vfs, &file1);
87    /// assert!(vfs.chown_b(&file1).unwrap().owner(5, 5).exec().is_ok());
88    /// assert_eq!(vfs.uid(&file1).unwrap(), 5);
89    /// assert_eq!(vfs.gid(&file1).unwrap(), 5);
90    /// ```
91    pub fn owner(mut self, uid: u32, gid: u32) -> Self
92    {
93        self.opts.uid = Some(uid);
94        self.opts.gid = Some(gid);
95        self
96    }
97
98    /// Follow links so that the path they point to are also affected
99    ///
100    /// * Default: false
101    ///
102    /// ### Examples
103    /// ```
104    /// use rivia::prelude::*;
105    ///
106    /// let vfs = Memfs::new();
107    /// let file = vfs.root().mash("file");
108    /// let link = vfs.root().mash("link");
109    /// assert_vfs_mkfile!(vfs, &file);
110    /// assert_vfs_symlink!(vfs, &link, &file);
111    /// assert_eq!(vfs.uid(&file).unwrap(), 1000);
112    /// assert_eq!(vfs.uid(&link).unwrap(), 1000);
113    /// assert!(vfs.chown_b(&link).unwrap().uid(5).follow().exec().is_ok());
114    /// assert_eq!(vfs.uid(&file).unwrap(), 5);
115    /// assert_eq!(vfs.uid(&link).unwrap(), 1000);
116    /// ```
117    pub fn follow(mut self) -> Self
118    {
119        self.opts.follow = true;
120        self
121    }
122
123    /// Follow paths recursively when set to true
124    ///
125    /// * Default: true
126    ///
127    /// ### Examples
128    /// ```
129    /// use rivia::prelude::*;
130    ///
131    /// let vfs = Memfs::new();
132    /// let dir = vfs.root().mash("dir");
133    /// let file = dir.mash("file");
134    /// assert_vfs_mkdir_p!(vfs, &dir);
135    /// assert_vfs_mkfile!(vfs, &file);
136    /// assert_eq!(vfs.uid(&file).unwrap(), 1000);
137    /// assert_eq!(vfs.uid(&dir).unwrap(), 1000);
138    /// assert!(vfs.chown_b(&dir).unwrap().uid(5).recurse(false).exec().is_ok());
139    /// assert_eq!(vfs.uid(&dir).unwrap(), 5);
140    /// assert_eq!(vfs.uid(&file).unwrap(), 1000);
141    /// ```
142    pub fn recurse(mut self, yes: bool) -> Self
143    {
144        self.opts.recursive = yes;
145        self
146    }
147
148    /// Execute the [`Chown`] options against the path provided during construction with the Vfs
149    /// `chown_b` functions.
150    ///
151    /// ### Examples
152    /// ```
153    /// use rivia::prelude::*;
154    ///
155    /// let vfs = Memfs::new();
156    /// let file1 = vfs.root().mash("file1");
157    /// assert_vfs_mkfile!(vfs, &file1);
158    /// assert!(vfs.chown_b(&file1).unwrap().owner(5, 5).exec().is_ok());
159    /// ```
160    pub fn exec(&self) -> RvResult<()>
161    {
162        (self.exec)(self.opts.clone())
163    }
164}
165
166// Unit tests
167// -------------------------------------------------------------------------------------------------
168#[cfg(test)]
169mod tests
170{
171    use crate::prelude::*;
172
173    #[test]
174    fn test_vfs_chown()
175    {
176        test_chown(assert_vfs_setup!(Vfs::memfs()));
177        test_chown(assert_vfs_setup!(Vfs::stdfs()));
178    }
179    fn test_chown((vfs, tmpdir): (Vfs, PathBuf))
180    {
181        let dir1 = tmpdir.mash("dir1");
182        let file1 = tmpdir.mash("file1");
183        let dir1file1 = dir1.mash("dir1file1");
184
185        assert_vfs_mkfile!(vfs, &file1);
186        assert_vfs_mkdir_p!(vfs, &dir1);
187        assert_vfs_mkfile!(vfs, &dir1file1);
188        let (uid, gid) = vfs.owner(&file1).unwrap();
189
190        // chown single file
191        assert!(vfs.chown(&file1, uid, gid).is_ok());
192        assert_eq!(vfs.uid(&file1).unwrap(), uid);
193        assert_eq!(vfs.gid(&file1).unwrap(), gid);
194
195        // recurse
196        assert!(vfs.chown(&dir1, uid, gid).is_ok());
197        assert_eq!(vfs.owner(&dir1).unwrap(), (uid, gid));
198        assert_eq!(vfs.owner(&dir1file1).unwrap(), (uid, gid));
199
200        assert_vfs_remove_all!(vfs, &tmpdir);
201    }
202
203    #[test]
204    fn test_vfs_chown_follow()
205    {
206        test_chown_follow(assert_vfs_setup!(Vfs::memfs()));
207        test_chown_follow(assert_vfs_setup!(Vfs::stdfs()));
208    }
209    fn test_chown_follow((vfs, tmpdir): (Vfs, PathBuf))
210    {
211        let dir1 = tmpdir.mash("dir1");
212        let dir1file1 = dir1.mash("dir1file1");
213        let link1 = tmpdir.mash("link1");
214
215        assert_vfs_mkdir_p!(vfs, &dir1);
216        assert_vfs_mkfile!(vfs, &dir1file1);
217        assert_vfs_symlink!(vfs, &link1, &dir1);
218        let (uid, gid) = vfs.owner(&dir1file1).unwrap();
219
220        // no follow
221        assert!(vfs.chown_b(&link1).unwrap().owner(uid, gid).exec().is_ok());
222        assert_eq!(vfs.owner(&dir1).unwrap(), (uid, gid));
223        assert_eq!(vfs.owner(&dir1file1).unwrap(), (uid, gid));
224
225        // follow
226        assert!(vfs.chown_b(&link1).unwrap().owner(uid, gid).exec().is_ok());
227        assert_eq!(vfs.owner(&dir1).unwrap(), (uid, gid));
228        assert_eq!(vfs.owner(&dir1file1).unwrap(), (uid, gid));
229
230        assert_vfs_remove_all!(vfs, &tmpdir);
231    }
232}