squashfs_async/
squashfuse.rs

1//! Implementation of `fuse_async::Filesystem` on `SquashFs`.
2use std::collections::BTreeSet;
3use std::time::UNIX_EPOCH;
4
5use fuser_async::Error as ErrorFuse;
6use fuser_async::{utils::BLOCK_SIZE, DirEntry};
7
8use crate::{Error, SquashFs};
9
10impl From<&super::directory_table::Entry> for DirEntry {
11    fn from(e: &super::directory_table::Entry) -> Self {
12        DirEntry {
13            inode: e.inode as u64,
14            name: e.name.clone(),
15            file_type: if e.is_dir() {
16                fuser::FileType::Directory
17            } else {
18                fuser::FileType::RegularFile
19            },
20        }
21    }
22}
23
24impl<R: deadpool::managed::Manager> SquashFs<R> {
25    /// Remapping to ensure that the root inode is `fuser::FUSE_ROOT_ID`
26    fn ino_from_fuse(&self, ino: u64) -> Result<u32, Error> {
27        if ino == fuser::FUSE_ROOT_ID {
28            Ok(self.root_inode)
29        } else if ino == self.inode_extra as u64 {
30            let fuse_root: u32 = fuser::FUSE_ROOT_ID.try_into().unwrap();
31            Ok(fuse_root)
32        } else {
33            ino.try_into().map_err(|_| Error::InvalidInode)
34        }
35    }
36    /// Remapping to ensure that the root inode is `fuser::FUSE_ROOT_ID`
37    pub fn ino_to_fuse(&self, ino: u32) -> u64 {
38        let fuse_root: u32 = fuser::FUSE_ROOT_ID.try_into().unwrap();
39        if ino == self.root_inode {
40            fuser::FUSE_ROOT_ID
41        } else if ino == fuse_root {
42            self.inode_extra as u64
43        } else {
44            ino as u64
45        }
46    }
47    fn getattr_inode(&self, ino: u32) -> Result<fuser::FileAttr, Error> {
48        if let Some(f) = self.inode_table.files.get(&ino) {
49            Ok(fuser_async::utils::file_attr(
50                self.ino_to_fuse(ino),
51                f.file_size(),
52                UNIX_EPOCH,
53            ))
54        } else {
55            let directory = self
56                .inode_table
57                .directories
58                .get(&ino)
59                .ok_or(Error::DirectoryNotFound)?;
60            Ok(fuser::FileAttr {
61                ino: self.ino_to_fuse(ino),
62                size: 0,
63                blocks: 0,
64                // TODO: Set these.
65                atime: UNIX_EPOCH,
66                mtime: UNIX_EPOCH,
67                ctime: UNIX_EPOCH,
68                crtime: UNIX_EPOCH,
69                kind: fuser::FileType::Directory,
70                perm: 0o755,
71                nlink: directory.hard_link_count(),
72                uid: 501,
73                gid: 20,
74                rdev: 0,
75                flags: 0,
76                blksize: BLOCK_SIZE,
77            })
78        }
79    }
80}
81
82#[async_trait::async_trait]
83impl<
84        T: crate::AsyncSeekBufRead,
85        R: deadpool::managed::Manager<Type = T, Error = tokio::io::Error> + Send + Sync,
86    > fuser_async::Filesystem for SquashFs<R>
87{
88    type Error = Error;
89    async fn inodes(&self) -> Result<BTreeSet<u64>, Error> {
90        Ok(self.inodes().map(|ino| self.ino_to_fuse(ino)).collect())
91    }
92
93    async fn open(&self, _ino: u64, flags: i32) -> Result<u64, Self::Error> {
94        let mut handles = self.handles.write().await;
95        let fh = handles.keys().last().copied().unwrap_or_default() + 1;
96        handles.insert(fh, flags);
97        Ok(fh)
98    }
99    async fn release(&self, _ino: u64, fh: u64) -> Result<(), Self::Error> {
100        let mut handles = self.handles.write().await;
101        handles
102            .remove(&fh)
103            .ok_or(Error::Fuse(fuser_async::Error::BadFileDescriptor))?;
104        Ok(())
105    }
106
107    async fn lookup(&self, parent: u64, name: &std::ffi::OsStr) -> Result<fuser::FileAttr, Error> {
108        let ino = self.ino_from_fuse(parent)?;
109        let d = self
110            .directory_tables
111            .get(&ino)
112            .ok_or(Error::DirectoryNotFound)?;
113        let name = name.to_str().ok_or(Error::Encoding)?;
114        let f = d
115            .find(name)
116            .ok_or_else(|| Error::FileNotFound(Some(name.into())))?;
117        Ok(self.getattr_inode(f.inode)?)
118    }
119    async fn getattr(&self, ino_fuse: u64) -> Result<fuser::FileAttr, Error> {
120        let ino = self.ino_from_fuse(ino_fuse)?;
121        self.getattr_inode(ino)
122    }
123    async fn setattr(
124        &mut self,
125        _ino: u64,
126        _size: Option<u64>,
127    ) -> Result<fuser::FileAttr, Self::Error> {
128        Err(ErrorFuse::Unimplemented.into())
129    }
130    async fn readdir(
131        &self,
132        ino_fuse: u64,
133        offset: u64,
134    ) -> Result<Box<dyn Iterator<Item = fuser_async::DirEntry> + Send + Sync + '_>, Error> {
135        let ino = self.ino_from_fuse(ino_fuse).unwrap();
136        let d = self
137            .directory_tables
138            .get(&ino)
139            .ok_or(Error::DirectoryNotFound)?;
140        Ok(Box::new(
141            d.entries
142                .iter()
143                .skip(offset as usize)
144                .map(fuser_async::DirEntry::from)
145                .map(|mut e| {
146                    e.inode = self.ino_to_fuse(e.inode as u32);
147                    e
148                }),
149        ))
150    }
151    async fn read(
152        &self,
153        ino_fuse: u64,
154        fh: u64,
155        offset: i64,
156        size: u32,
157    ) -> Result<bytes::Bytes, Error> {
158        let ino = self.ino_from_fuse(ino_fuse)?;
159        let flags = {
160            let handles = self.handles.read().await;
161            *handles
162                .get(&fh)
163                .ok_or(Error::Fuse(fuser_async::Error::BadFileDescriptor))?
164        };
165        Ok(self
166            .read_file(
167                ino,
168                offset as usize,
169                size as usize,
170                flags,
171                self.superblock.compression,
172            )
173            .await?)
174    }
175    async fn write(
176        &self,
177        _ino: u64,
178        _fh: u64,
179        _data: bytes::Bytes,
180        _offset: i64,
181    ) -> Result<u32, Self::Error> {
182        Err(ErrorFuse::ReadOnly.into())
183    }
184    async fn create(
185        &mut self,
186        _parent: u64,
187        _name: std::ffi::OsString,
188        _mode: u32,
189        _umask: u32,
190        _flags: i32,
191    ) -> Result<(fuser::FileAttr, u64), Self::Error> {
192        Err(ErrorFuse::ReadOnly.into())
193    }
194    async fn mkdir(
195        &mut self,
196        _parent: u64,
197        _name: std::ffi::OsString,
198    ) -> Result<fuser::FileAttr, Self::Error> {
199        Err(ErrorFuse::ReadOnly.into())
200    }
201}