Skip to main content

mangadex_fs/fs/
mod.rs

1pub mod entry;
2
3pub struct MangaDexFS {
4    context: std::sync::Arc<crate::Context>
5}
6
7impl MangaDexFS {
8    pub fn new(context: std::sync::Arc<crate::Context>) -> MangaDexFS {
9        MangaDexFS {
10            context
11        }
12    }
13
14    async fn do_lookup(&self, op: &polyfuse::op::Lookup<'_>) -> std::io::Result<polyfuse::reply::ReplyEntry> {
15        let entries = self.context.entries.read().await;
16
17        let make_result = |directory: &entry::Directory| -> std::io::Result<polyfuse::reply::ReplyEntry> {
18            match directory.entries().into_iter().find(|direntry| direntry.name() == op.name()) {
19                // If child direntry is found, find its ino in entries
20                Some(child_direntry) => match entries.get(&child_direntry.nodeid()) {
21                    // If child inode is found
22                    Some(child_inode) => {
23                        let mut reply = polyfuse::reply::ReplyEntry::default();
24                        let attr = child_inode.get_attr();
25
26                        match attr {
27                            Some(attr) => {
28                                reply.ino(attr.ino());
29                                reply.attr(attr);
30                                reply.ttl_attr(std::time::Duration::from_secs(1u64));
31                                reply.ttl_entry(std::time::Duration::from_secs(1u64));
32
33                                Ok(reply)
34                            },
35                            None => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
36                        }
37                    },
38                    None => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
39                },
40                None => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
41            }
42        };
43
44        // Find parent entry from op parent
45        match entries.get(&op.parent()) {
46            Some(entry::Inode(entry::Entry::Root(directory), _)) => make_result(directory),
47            Some(entry::Inode(entry::Entry::Manga(_, directory), _)) => make_result(directory),
48            Some(entry::Inode(entry::Entry::Chapter(_, directory), _)) => make_result(directory),
49            Some(entry::Inode(entry::Entry::ChapterNotFetched(_), _)) => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
50            Some(entry::Inode(entry::Entry::Page(_), _)) => Err(std::io::Error::from_raw_os_error(libc::ENOTDIR)),
51            Some(entry::Inode(entry::Entry::Cover(_), _)) => Err(std::io::Error::from_raw_os_error(libc::ENOTDIR)),
52            Some(entry::Inode(entry::Entry::External(_), _)) => Err(std::io::Error::from_raw_os_error(libc::ENOTDIR)),
53            None => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
54        }
55    }
56
57    async fn do_getattr(&self, op: &polyfuse::op::Getattr<'_>) -> std::io::Result<polyfuse::reply::ReplyAttr> {
58        match self.context.entries.read().await.get(&op.ino()).and_then(|inode| inode.get_attr()) {
59            Some(file_attr) => Ok({                
60                let mut reply = polyfuse::reply::ReplyAttr::new(file_attr);
61                reply.ttl_attr(std::time::Duration::from_secs(1u64));
62                reply
63            }),
64            None => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
65        }
66    }
67
68    async fn do_read(&self, op: &polyfuse::op::Read<'_>) -> std::io::Result<Vec<u8>> {
69        let read_lock = self.context.entries.read().await;
70
71        match read_lock.get(&op.ino()) {
72            Some(entry::Inode(entry::Entry::Page(page_ref), _)) => match page_ref.upgrade() {
73                Some(page) => Ok(page.0[op.offset() as usize..std::cmp::min(op.offset() as usize + op.size() as usize, page.0.len())].into()),
74                None => Err(std::io::Error::from_raw_os_error(libc::EIO))
75            },
76            Some(entry::Inode(entry::Entry::Cover(cover_ref), _)) => match cover_ref.upgrade() {
77                Some(cover) => Ok(cover.0[op.offset() as usize..std::cmp::min(op.offset() as usize + op.size() as usize, cover.0.len())].into()),
78                None => Err(std::io::Error::from_raw_os_error(libc::EIO))
79            },
80            Some(entry::Inode(entry::Entry::External(bytes), _)) => Ok(bytes[op.offset() as usize..std::cmp::min(op.offset() as usize + op.size() as usize, bytes.len())].into()),
81            Some(_) => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
82            None => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
83        }
84    }
85
86    async fn do_readdir(&self, op: &polyfuse::op::Readdir<'_>) -> std::io::Result<Vec<u8>> {
87        let make_reply = |directory: &entry::Directory| -> Vec<u8> {
88            let entries = {
89                let mut entries = vec![
90                    polyfuse::DirEntry::dir(".", op.ino(), 1),
91                    polyfuse::DirEntry::dir("..", op.ino(), 2)
92                ];
93
94                for entry in directory.entries() {
95                    entries.push(entry);
96                }
97
98                entries
99            };
100
101            let mut entries_reply = vec![];
102            let mut total_len = 0usize;
103            let offset = op.offset() as usize;
104            let size = op.size() as usize;
105
106            for entry in entries.iter().skip(offset as usize) {
107                let entry = entry.as_ref();
108                
109                if total_len + entry.len() > size {
110                    break;
111                }
112
113                entries_reply.extend_from_slice(entry);
114                total_len += entry.len();
115            }
116
117            entries_reply
118        };
119
120        let read_lock = self.context.entries.read().await;
121
122        match read_lock.get(&op.ino()) {
123            Some(entry::Inode(entry::Entry::Root(directory), _)) => Ok(make_reply(directory)),
124            Some(entry::Inode(entry::Entry::Manga(_, directory), _)) => Ok(make_reply(directory)),
125            Some(entry::Inode(entry::Entry::Chapter(_, directory), _)) => Ok(make_reply(directory)),
126            Some(entry::Inode(entry::Entry::ChapterNotFetched(chapter_id_ref), _)) => {
127                let chapter_id = *chapter_id_ref;
128
129                drop(chapter_id_ref);
130                drop(read_lock);
131
132                debug!("chapter not fetched: {}", chapter_id);
133
134                match self.context.get_or_fetch_chapter(chapter_id).await {
135                    Ok(_) => {
136                        match self.context.entries.read().await.get(&op.ino()) {
137                            Some(entry::Inode(entry::Entry::Chapter(_, directory), _)) => Ok(make_reply(directory)),
138                            _ => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
139                        }
140                    },
141                    Err(error) => {
142                        debug!("chapter fetching error: {}", error);
143                        Err(std::io::Error::from_raw_os_error(libc::EIO))
144                    }
145                }
146            },
147            Some(entry::Inode(entry::Entry::Page(_), _)) => Err(std::io::Error::from_raw_os_error(libc::ENOTDIR)),
148            Some(entry::Inode(entry::Entry::Cover(_), _)) => Err(std::io::Error::from_raw_os_error(libc::ENOTDIR)),
149            Some(entry::Inode(entry::Entry::External(_), _)) => Err(std::io::Error::from_raw_os_error(libc::ENOTDIR)),
150            None => Err(std::io::Error::from_raw_os_error(libc::ENOENT))
151        }
152    }
153}
154
155
156
157#[polyfuse::async_trait]
158impl polyfuse::Filesystem for MangaDexFS {
159    async fn call<'a, 'cx, T: ?Sized>(
160        &'a self,
161        cx: &'a mut polyfuse::Context<'cx, T>,
162        op: polyfuse::Operation<'cx>,
163    ) -> std::io::Result<()>
164    where
165        T: polyfuse::io::Reader + polyfuse::io::Writer + Unpin + Send,
166    {
167        macro_rules! try_reply {
168            ($e:expr) => {
169                match ($e).await {
170                    Ok(reply) => {
171                        cx.reply(reply).await
172                    }
173                    Err(err) => {
174                        let errno = err.raw_os_error().unwrap_or(libc::EIO);
175                        cx.reply_err(errno).await
176                    }
177                }
178            };
179        }
180
181        match op {
182            polyfuse::Operation::Lookup(op) => try_reply!(self.do_lookup(&op)),
183            polyfuse::Operation::Getattr(op) => try_reply!(self.do_getattr(&op)),
184            polyfuse::Operation::Read(op) => try_reply!(self.do_read(&op)),
185            polyfuse::Operation::Readdir(op) => try_reply!(self.do_readdir(&op)),
186            _ => Ok(()),
187        }
188    }
189}