fuser_async/
remapping.rs

1//! Utility to remap inodes of a [`Filesystem`]
2use std::collections::BTreeSet;
3use std::ffi::{OsStr, OsString};
4use std::ops::Range;
5
6use bimap::BiBTreeMap;
7use tracing::*;
8
9use crate::{DirEntry, Filesystem};
10
11#[async_trait::async_trait]
12pub trait InodeAllocator {
13    type Error;
14    /// Reserve a continuous number of inodes, returning the start.
15    /// The range is disjoint from the previously allocated ones.
16    async fn allocate(&mut self, n: usize) -> Result<u64, Self::Error>;
17}
18#[async_trait::async_trait]
19impl<F: Filesystem + Send + Sync> InodeAllocator for F {
20    type Error = F::Error;
21    async fn allocate(&mut self, _n: usize) -> Result<u64, Self::Error> {
22        Ok(self
23            .inodes()
24            .await?
25            .iter()
26            .last()
27            .copied()
28            .unwrap_or(fuser::FUSE_ROOT_ID)
29            + 1)
30    }
31}
32
33#[async_trait::async_trait]
34impl InodeAllocator for () {
35    type Error = crate::Error;
36    async fn allocate(&mut self, _n: usize) -> Result<u64, Self::Error> {
37        Err(crate::Error::NoInodes)
38    }
39}
40#[async_trait::async_trait]
41// The start of the range denotes the next available inode
42impl InodeAllocator for Range<u64> {
43    type Error = crate::Error;
44    async fn allocate(&mut self, n: usize) -> Result<u64, Self::Error> {
45        let Range { start, end } = *self;
46        let remaining = end - start;
47        if remaining < n as u64 {
48            Err(crate::Error::NoInodes)
49        } else {
50            *self = (start + n as u64)..end;
51            Ok(start)
52        }
53    }
54}
55#[async_trait::async_trait]
56impl<A: InodeAllocator + Send + Sync> InodeAllocator for std::sync::Arc<tokio::sync::Mutex<A>> {
57    type Error = A::Error;
58    async fn allocate(&mut self, n: usize) -> Result<u64, Self::Error> {
59        let mut x = self.lock().await;
60        x.allocate(n).await
61    }
62}
63
64/// Wraps a [`Filesystem`] and remaps its inodes.
65pub struct RemappedFilesystem<F, A> {
66    pub inner: F,
67    pub mapping: BiBTreeMap<u64 /* external */, u64 /* internal */>,
68    pub allocator: A,
69}
70impl<F, A: InodeAllocator> RemappedFilesystem<F, A> {
71    pub fn new(inner: F, mapping: BiBTreeMap<u64, u64>, allocator: A) -> Self {
72        // TODO: Check if the mapping is valid
73        Self {
74            inner,
75            mapping,
76            allocator,
77        }
78    }
79    pub fn ext_to_int<E>(&self, ext: u64) -> Result<u64, Error<E>> {
80        self.mapping
81            .get_by_left(&ext)
82            .ok_or(Error::NotFound(ext, "ext"))
83            .copied()
84    }
85    pub fn int_to_ext<E>(&self, int: u64) -> Result<u64, Error<E>> {
86        self.mapping
87            .get_by_right(&int)
88            .ok_or(Error::NotFound(int, "int"))
89            .copied()
90    }
91    pub async fn next_ext_ino<E>(&mut self) -> Result<u64, Error<E>> {
92        self.allocator
93            .allocate(1)
94            .await
95            .map_err(|_| Error::NoExtInodes)
96    }
97}
98#[derive(thiserror::Error, Debug)]
99pub enum RefreshError<R, E> {
100    #[error("{0}")]
101    Remapping(#[from] Error<E>),
102    #[error("{0}")]
103    Refresh(R),
104}
105#[async_trait::async_trait]
106impl<A: InodeAllocator + Send + Sync, F: Filesystem + crate::Refresh + Send + Sync> crate::Refresh
107    for RemappedFilesystem<F, A>
108{
109    type Error = RefreshError<<F as crate::Refresh>::Error, <F as Filesystem>::Error>;
110
111    async fn cleanup(&self) -> Result<(), Self::Error> {
112        self.inner.cleanup().await.map_err(RefreshError::Refresh)
113    }
114    async fn refresh(&mut self) -> Result<(), Self::Error> {
115        self.inner.refresh().await.map_err(RefreshError::Refresh)?;
116        let current_int_inodes: BTreeSet<u64> = self.mapping.right_values().copied().collect();
117        let all_int_inodes = self
118            .inner
119            .inodes()
120            .await
121            .map_err(|e| RefreshError::Remapping(Error::Base(e)))?;
122
123        let new = all_int_inodes.len() - current_int_inodes.len();
124
125        debug!(
126            "{} base inodes tracked, {} base inodes in total, {} new",
127            current_int_inodes.len(),
128            all_int_inodes.len(),
129            new
130        );
131        if new > 0 {
132            let next_ext_ino = self
133                .allocator
134                .allocate(new)
135                .await
136                .map_err(|_| Error::NoExtInodes)?;
137            self.mapping.extend(
138                all_int_inodes
139                    .difference(&current_int_inodes)
140                    .enumerate()
141                    .map(|(i, ino)| (next_ext_ino + i as u64, *ino)),
142            );
143        }
144
145        Ok(())
146    }
147}
148
149#[derive(thiserror::Error, Debug)]
150pub enum Error<E> {
151    #[error("{0}")]
152    Base(#[from] E),
153    #[error("Inode {0} not found in direction {1}")]
154    NotFound(u64, &'static str),
155    #[error("No more external inodes available")]
156    NoExtInodes,
157}
158impl<E: Into<crate::Error>> From<Error<E>> for crate::Error {
159    fn from(source: Error<E>) -> Self {
160        match source {
161            Error::NotFound(_, _) => crate::Error::NoFileDir,
162            Error::NoExtInodes => crate::Error::NoInodes,
163            Error::Base(b) => b.into(),
164        }
165    }
166}
167
168#[async_trait::async_trait]
169impl<A: InodeAllocator + Send + Sync, F: Filesystem + Send + Sync> Filesystem
170    for RemappedFilesystem<F, A>
171{
172    type Error = Error<F::Error>;
173    async fn inodes(&self) -> Result<BTreeSet<u64>, Self::Error> {
174        Ok(self.mapping.left_values().copied().collect())
175    }
176    async fn destroy(&mut self) -> Result<(), Self::Error> {
177        self.inner.destroy().await.map_err(Error::Base)
178    }
179    async fn lookup(&self, parent: u64, name: &OsStr) -> Result<fuser::FileAttr, Self::Error> {
180        let parent = self.ext_to_int(parent)?;
181        let mut attr = self.inner.lookup(parent, name).await?;
182        attr.ino = self.int_to_ext(attr.ino)?;
183        Ok(attr)
184    }
185    async fn open(&self, ino: u64, flags: i32) -> Result<u64, Self::Error> {
186        let ino = self.ext_to_int(ino)?;
187        Ok(self.inner.open(ino, flags).await?)
188    }
189    async fn release(&self, ino: u64, fh: u64) -> Result<(), Self::Error> {
190        let ino = self.ext_to_int(ino)?;
191        Ok(self.inner.release(ino, fh).await?)
192    }
193    async fn getattr(&self, ino_ext: u64) -> Result<fuser::FileAttr, Self::Error> {
194        let ino_int = self.ext_to_int(ino_ext)?;
195        let mut attr = self.inner.getattr(ino_int).await?;
196        attr.ino = ino_ext;
197        Ok(attr)
198    }
199    async fn setattr(
200        &mut self,
201        ino_ext: u64,
202        size: Option<u64>,
203    ) -> Result<fuser::FileAttr, Self::Error> {
204        let ino_int = self.ext_to_int(ino_ext)?;
205        let mut attr = self.inner.setattr(ino_int, size).await?;
206        attr.ino = ino_ext;
207        Ok(attr)
208    }
209    async fn readdir(
210        &self,
211        ino: u64,
212        offset: u64,
213    ) -> Result<Box<dyn Iterator<Item = DirEntry> + Send + Sync + '_>, Self::Error> {
214        let ino = self.ext_to_int(ino)?;
215        #[allow(clippy::needless_collect)]
216        let dir: Result<Vec<DirEntry>, _> = self
217            .inner
218            .readdir(ino, offset)
219            .await?
220            .map(move |mut e| {
221                e.inode = self.int_to_ext(e.inode)?;
222                Ok::<_, Self::Error>(e)
223            })
224            .collect();
225        Ok(Box::new(dir?.into_iter()))
226    }
227    async fn read(
228        &self,
229        ino: u64,
230        fh: u64,
231        offset: i64,
232        size: u32,
233    ) -> Result<bytes::Bytes, Self::Error> {
234        let ino = self.ext_to_int(ino)?;
235        Ok(self.inner.read(ino, fh, offset, size).await?)
236    }
237    async fn write(
238        &self,
239        ino: u64,
240        fh: u64,
241        data: bytes::Bytes,
242        offset: i64,
243    ) -> Result<u32, Self::Error> {
244        let ino = self.ext_to_int(ino)?;
245        Ok(self.inner.write(ino, fh, data, offset).await?)
246    }
247    async fn create(
248        &mut self,
249        parent: u64,
250        name: std::ffi::OsString,
251        mode: u32,
252        umask: u32,
253        flags: i32,
254    ) -> Result<(fuser::FileAttr, u64), Self::Error> {
255        let parent = self.ext_to_int(parent)?;
256        let (mut attr, fh) = self.inner.create(parent, name, mode, umask, flags).await?;
257        let ext_ino = self.next_ext_ino().await?;
258        self.mapping.insert(ext_ino, attr.ino);
259        attr.ino = ext_ino;
260        Ok((attr, fh))
261    }
262    async fn mkdir(&mut self, parent: u64, name: OsString) -> Result<fuser::FileAttr, Self::Error> {
263        let parent = self.ext_to_int(parent)?;
264        let mut attr = self.inner.mkdir(parent, name).await?;
265        let ext_ino = self.next_ext_ino().await?;
266        self.mapping.insert(ext_ino, attr.ino);
267        attr.ino = ext_ino;
268        Ok(attr)
269    }
270}