Skip to main content

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;