easy_fuser/core/macros.rs
1macro_rules! handle_fuse_reply_entry {
2 ($reply_executor:expr, $handler:expr, $resolver:expr, $req:expr, $parent:expr, $name:expr, $reply:expr,
3 $function:ident, ($($args:expr),*)) => {
4 macro_rules! if_lookup {
5 (lookup, $choice1:tt, $choice2:tt) => {
6 $choice1
7 };
8 ($any:tt, $choice1:tt, $choice2:tt) => {
9 $choice2
10 };
11 }
12
13 let handler = $handler;
14 match handler.$function($($args),*) {
15 Ok(metadata) => {
16 let default_ttl = handler.get_default_ttl();
17 execute_reply_task!(
18 $reply_executor,
19 {
20 let (id, file_attr) = TId::extract_metadata(metadata);
21 let ino = $resolver.lookup($parent, $name, id, true);
22 let (fuse_attr, ttl, generation) = file_attr.to_fuse(ino);
23 $reply.entry(
24 &ttl.unwrap_or(default_ttl),
25 &fuse_attr,
26 generation.unwrap_or(get_random_generation()),
27 );
28 }
29 );
30 }
31 Err(e) => {
32 if_lookup!($function, {
33 if e.kind() == ErrorKind::FileNotFound {
34 // Lookup is preemptivly done in normal situations, we don't need to log an error
35 // eg: before creating a file
36 info!("{}: parent_ino {:x?}, [{}], {:?}", stringify!($function), $parent, e, $req);
37 } else {
38 warn!("{}: parent_ino {:x?}, [{}], {:?}", stringify!($function), $parent, e, $req);
39 };
40 }, {
41 warn!("{}: parent_ino {:x?}, [{}], {:?}", stringify!($function), $parent, e, $req);
42 });
43 execute_reply_task!(
44 $reply_executor,
45 {
46 $reply.error(e.raw_error())
47 }
48 );
49 }
50 }
51 };
52}
53
54macro_rules! handle_fuse_reply_attr {
55 ($reply_executor:expr, $handler:expr, $resolve:expr, $req:expr, $ino:expr, $reply:expr,
56 $function:ident, ($($args:expr),*)) => {
57 match $handler.$function($($args),*) {
58 Ok(file_attr) => {
59 let default_ttl = $handler.get_default_ttl();
60 execute_reply_task!(
61 $reply_executor,
62 {
63 let (fuse_attr, ttl, _) = file_attr.to_fuse($ino);
64 $reply.attr(&ttl.unwrap_or(default_ttl), &fuse_attr);
65 }
66 );
67 }
68 Err(e) => {
69 warn!("{}: ino {:x?}, [{}], {:?}", stringify!($function), $ino, e, $req);
70 execute_reply_task!(
71 $reply_executor,
72 {
73 $reply.error(e.raw_error())
74 }
75 );
76 }
77 }
78 };
79}
80
81/// Handles directory read operations for FUSE filesystem.//+
82/////+
83/// This macro implements the logic for reading directory contents, supporting both//+
84/// regular directory reads (`readdir`) and extended directory reads (`readdirplus`).//+
85/////+
86/// # Parameters//+
87/////+
88/// * `$self`: The current filesystem instance.//+
89/// * `$req`: The FUSE request object.//+
90/// * `$ino`: The inode number of the directory being read.//+
91/// * `$fh`: The file handle of the open directory.//+
92/// * `$offset`: The offset from which to start reading directory entries.//+
93/// * `$reply`: The FUSE reply object to send the response.//+
94/// * `$handler_method`: The method to call on the handler to retrieve directory entries.//+
95/// * `$unpack_method`: The method to unpack metadata for each directory entry.//+
96/// * `$get_iter_method`: The method to retrieve the directory iterator.//+
97/// * `$reply_type`: The type of reply (readdir or readdirplus).//+
98/////+
99/// # Returns//+
100/////+
101/// This macro doesn't return a value directly, but it populates the `$reply` object//+
102/// with directory entries or an error code.//
103macro_rules! handle_dir_read {
104 ($self:expr, $req:expr, $ino:expr, $fh:expr, $offset:expr, $reply:expr,
105 $handler_method:ident, $get_iter_method:ident, $reply_type:ty) => {{
106 // Inner macro to handle readdir vs readdirplus differences
107 macro_rules! if_readdir {
108 (readdir, $choice1:tt, $choice2:tt) => {
109 $choice1
110 };
111 (readdirplus, $choice1:tt, $choice2:tt) => {
112 $choice2
113 };
114 }
115
116 let req_info = RequestInfo::from($req);
117 let handler = $self.get_handler();
118 let resolver = $self.get_resolver();
119 let dirmap_iter = $self.$get_iter_method();
120 #[cfg_attr(feature = "serial", allow(unused_variables))]
121 let reply_executor = reply_executor!($self);
122
123 execute_task!($self, {
124 // Validate offset
125 if $offset < 0 {
126 error!("readdir called with a negative offset");
127 execute_reply_task!(reply_executor, {
128 $reply.error(ErrorKind::InvalidArgument.into());
129 });
130 return;
131 }
132
133 // ### Initialize directory iterator
134 let mut dir_iter = match $offset {
135 // First read: fetch children from handler
136 0 => match handler.$handler_method(&req_info, resolver.resolve_id($ino), unsafe {
137 BorrowedFileHandle::from_raw($fh)
138 }) {
139 Ok(children) => {
140 // Unpack and process children
141 let (child_list, attr_list): (Vec<_>, Vec<_>) = children
142 .into_iter()
143 .map(|item| {
144 let (child_id, child_attr) = if_readdir!(
145 $handler_method,
146 { TId::extract_minimal_metadata(item.1) },
147 { TId::extract_metadata(item.1) }
148 );
149 ((item.0, child_id), child_attr)
150 })
151 .unzip();
152
153 // Add children to resolver and create iterator
154 resolver
155 .add_children(
156 $ino,
157 child_list,
158 if_readdir!($handler_method, false, true),
159 )
160 .into_iter()
161 .zip(attr_list.into_iter())
162 .map(|((file_name, file_ino), file_attr)| {
163 (file_name, file_ino, file_attr)
164 })
165 .collect()
166 }
167 Err(e) => {
168 warn!("readdir {:?}: {:?}", req_info, e);
169 execute_reply_task!(reply_executor, {
170 $reply.error(e.raw_error());
171 });
172 return;
173 }
174 },
175 // Subsequent reads: retrieve saved iterator
176 _ => match { dirmap_iter.safe_borrow_mut().remove(&($ino, $offset)) } {
177 Some(dirmap_iter) => dirmap_iter,
178 None => {
179 // Case when fuse tries to read again after the final item
180 execute_reply_task!(reply_executor, {
181 $reply.ok();
182 });
183 return;
184 }
185 },
186 };
187
188 let mut new_offset = $offset;
189
190 // ### Process directory entries
191 execute_reply_task!(reply_executor, {
192 if_readdir!(
193 $handler_method,
194 {
195 // readdir: Add entries until buffer is full
196 while let Some((name, ino, kind)) = dir_iter.pop_front() {
197 if $reply.add(ino, new_offset, kind, &name) {
198 dir_iter.push_front((name, ino, kind));
199 dirmap_iter
200 .safe_borrow_mut()
201 .insert(($ino, new_offset - 1), dir_iter);
202 break;
203 }
204 new_offset += 1;
205 }
206 $reply.ok();
207 },
208 {
209 // readdirplus: Add entries with extended attributes
210 let default_ttl = handler.get_default_ttl();
211 while let Some((name, ino, file_attr)) = dir_iter.pop_front() {
212 let (fuse_attr, ttl, generation) = file_attr.clone().to_fuse(ino);
213 if $reply.add(
214 ino,
215 new_offset,
216 &name,
217 &ttl.unwrap_or(default_ttl),
218 &fuse_attr,
219 generation.unwrap_or(get_random_generation()),
220 ) {
221 dir_iter.push_front((name, ino, file_attr.clone()));
222 dirmap_iter
223 .safe_borrow_mut()
224 .insert((ino, new_offset - 1), dir_iter);
225 break;
226 }
227 new_offset += 1;
228 }
229 $reply.ok();
230 }
231 );
232 });
233 });
234 }};
235}
236
237pub(super) use handle_dir_read;
238pub(super) use handle_fuse_reply_attr;
239pub(super) use handle_fuse_reply_entry;