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}