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 Some(child_direntry) => match entries.get(&child_direntry.nodeid()) {
21 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 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}