vfsi_core/api/
metadata.rs1#[bitfields::bitfield(u8)]
4#[derive(PartialEq, Eq)]
5struct AttrsFlags {
6 #[bits(default = true)]
7 follow: bool,
8 #[bits(7)]
9 _reserved: u8,
10}
11
12#[derive(Clone, Copy, Debug)]
15pub struct AttrsOptions {
16 fields: crate::api::Attributes,
17 flags: AttrsFlags,
18}
19impl Default for AttrsOptions {
20 fn default() -> Self {
21 Self {
22 fields: crate::api::Attributes::stat(),
23 flags: AttrsFlags::new(),
24 }
25 }
26}
27impl AttrsOptions {
28 pub fn new() -> Self {
30 Self::default()
31 }
32 pub fn fields(mut self, fields: crate::api::Attributes) -> Self {
34 self.fields = fields;
35 self
36 }
37 pub fn follow_symlinks(mut self, follow: bool) -> Self {
39 self.flags.set_follow(follow);
40 self
41 }
42 pub fn requested_attributes(self) -> crate::api::Attributes {
44 self.fields
45 }
46 pub fn follows_symlinks(self) -> bool {
48 self.flags.follow()
49 }
50}
51
52#[derive(Clone, Copy, Debug)]
58pub struct SetAttrsOp<T> {
59 target: T,
60 permissions: Option<crate::Permissions>,
61 uid: Option<u32>,
62 gid: Option<u32>,
63 len: Option<u64>,
64 accessed: Option<std::time::SystemTime>,
65 modified: Option<std::time::SystemTime>,
66 flags: AttrsFlags,
67}
68
69impl<T> SetAttrsOp<T> {
70 pub fn new(target: T) -> Self {
72 Self {
73 target,
74 permissions: None,
75 uid: None,
76 gid: None,
77 len: None,
78 accessed: None,
79 modified: None,
80 flags: AttrsFlags::new(),
81 }
82 }
83
84 pub fn follow_symlinks(mut self, follow: bool) -> Self {
87 self.flags.set_follow(follow);
88 self
89 }
90
91 pub fn target(&self) -> &T {
93 &self.target
94 }
95
96 pub fn permissions(mut self, permissions: crate::Permissions) -> Self {
98 self.permissions = Some(permissions);
99 self
100 }
101
102 pub fn uid(mut self, uid: u32) -> Self {
104 self.uid = Some(uid);
105 self
106 }
107
108 pub fn gid(mut self, gid: u32) -> Self {
110 self.gid = Some(gid);
111 self
112 }
113
114 pub fn len(mut self, len: u64) -> Self {
116 self.len = Some(len);
117 self
118 }
119
120 pub fn accessed(mut self, accessed: std::time::SystemTime) -> Self {
122 self.accessed = Some(accessed);
123 self
124 }
125
126 pub fn modified(mut self, modified: std::time::SystemTime) -> Self {
128 self.modified = Some(modified);
129 self
130 }
131
132 pub fn with_target<U>(&self, target: U) -> SetAttrsOp<U> {
135 SetAttrsOp {
136 target,
137 permissions: self.permissions,
138 uid: self.uid,
139 gid: self.gid,
140 len: self.len,
141 accessed: self.accessed,
142 modified: self.modified,
143 flags: self.flags,
144 }
145 }
146
147 pub fn requested_permissions(&self) -> Option<crate::Permissions> {
149 self.permissions
150 }
151 pub fn requested_uid(&self) -> Option<u32> {
153 self.uid
154 }
155 pub fn requested_gid(&self) -> Option<u32> {
157 self.gid
158 }
159 pub fn requested_len(&self) -> Option<u64> {
161 self.len
162 }
163 pub fn requested_accessed(&self) -> Option<std::time::SystemTime> {
165 self.accessed
166 }
167 pub fn requested_modified(&self) -> Option<std::time::SystemTime> {
169 self.modified
170 }
171
172 pub fn follows_symlinks(&self) -> bool {
174 self.flags.follow()
175 }
176}
177
178impl<'a, F> SetAttrsOp<super::Target<'a, F>> {
179 pub fn file(file: &'a F) -> Self {
190 Self::new(super::Target::File(file))
191 }
192}
193
194#[cfg(test)]
195mod option_layout_tests {
196 use super::*;
197 #[test]
198 fn retargeting_preserves_all_changes_without_cloning_the_target() {
199 struct NonClone;
200 let permissions = crate::Permissions::from_mode(0o640);
201 let accessed = std::time::UNIX_EPOCH + std::time::Duration::from_secs(123);
202 let modified = std::time::UNIX_EPOCH - std::time::Duration::from_secs(456);
203 let empty = SetAttrsOp::new(NonClone);
204 assert!(empty.follows_symlinks());
205 assert_eq!(empty.requested_permissions(), None);
206 assert_eq!(empty.requested_len(), None);
207 assert_eq!(empty.requested_uid(), None);
208 assert_eq!(empty.requested_gid(), None);
209 assert_eq!(empty.requested_accessed(), None);
210 assert_eq!(empty.requested_modified(), None);
211 let op = empty
212 .permissions(permissions)
213 .len(0)
214 .uid(42)
215 .gid(43)
216 .accessed(accessed)
217 .modified(modified)
218 .follow_symlinks(false);
219 for path in ["/first", "/second"] {
220 let rebound = op.with_target(path);
221 assert_eq!(*rebound.target(), path);
222 assert_eq!(rebound.requested_permissions(), Some(permissions));
223 assert_eq!(rebound.requested_len(), Some(0));
224 assert_eq!(rebound.requested_uid(), Some(42));
225 assert_eq!(rebound.requested_gid(), Some(43));
226 assert_eq!(rebound.requested_accessed(), Some(accessed));
227 assert_eq!(rebound.requested_modified(), Some(modified));
228 assert!(!rebound.follows_symlinks());
229 }
230 let replaced = op
231 .with_target("/third")
232 .len(10)
233 .uid(44)
234 .follow_symlinks(true);
235 assert_eq!(replaced.requested_len(), Some(10));
236 assert_eq!(replaced.requested_uid(), Some(44));
237 assert!(replaced.follows_symlinks());
238 assert_eq!(op.requested_len(), Some(0));
239 assert_eq!(op.requested_uid(), Some(42));
240 assert!(!op.follows_symlinks());
241 }
242 #[test]
243 fn packed_follow_flag_preserves_selected_fields() {
244 assert_eq!(std::mem::size_of::<AttrsFlags>(), 1);
245 assert!(AttrsOptions::default().follows_symlinks());
246 let op = SetAttrsOp::new("/file").len(0).uid(42);
247 assert_eq!(*op.target(), "/file");
248 assert!(op.follows_symlinks());
249 let op = op.follow_symlinks(false);
250 assert!(!op.follows_symlinks());
251 assert_eq!(op.requested_len(), Some(0));
252 assert_eq!(op.requested_uid(), Some(42));
253 let fields = crate::api::Attributes::SIZE | crate::api::Attributes::CHANGE;
254 let options = AttrsOptions::new().fields(fields);
255 for follow in [false, true, false] {
256 let changed = options.follow_symlinks(follow);
257 assert_eq!(changed.follows_symlinks(), follow);
258 assert_eq!(changed.requested_attributes(), fields);
259 assert!(options.follows_symlinks());
260 }
261 }
262}