fuse_backend_rs/api/vfs/
async_io.rs

1// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::io;
5
6use async_trait::async_trait;
7
8use super::*;
9
10#[async_trait]
11impl AsyncFileSystem for Vfs {
12    async fn async_lookup(
13        &self,
14        ctx: &Context,
15        parent: <Self as FileSystem>::Inode,
16        name: &CStr,
17    ) -> Result<Entry> {
18        // Don't use is_safe_path_component(), allow "." and ".." for NFS export support
19        if name.to_bytes_with_nul().contains(&SLASH_ASCII) {
20            return Err(io::Error::from_raw_os_error(libc::EINVAL));
21        }
22
23        match self.get_real_rootfs(parent)? {
24            (Left(fs), idata) => self.lookup_pseudo(fs, idata, ctx, name),
25            (Right(fs), idata) => {
26                // parent is in an underlying rootfs
27                let mut entry = fs.async_lookup(ctx, idata.ino(), name).await?;
28                // lookup success, hash it to a real fuse inode
29                self.convert_entry(idata.fs_idx(), entry.inode, &mut entry)
30            }
31        }
32    }
33
34    async fn async_getattr(
35        &self,
36        ctx: &Context,
37        inode: <Self as FileSystem>::Inode,
38        handle: Option<<Self as FileSystem>::Handle>,
39    ) -> Result<(libc::stat64, Duration)> {
40        match self.get_real_rootfs(inode)? {
41            (Left(fs), idata) => fs.getattr(ctx, idata.ino(), handle),
42            (Right(fs), idata) => fs.async_getattr(ctx, idata.ino(), handle).await,
43        }
44    }
45
46    async fn async_setattr(
47        &self,
48        ctx: &Context,
49        inode: <Self as FileSystem>::Inode,
50        attr: libc::stat64,
51        handle: Option<<Self as FileSystem>::Handle>,
52        valid: SetattrValid,
53    ) -> Result<(libc::stat64, Duration)> {
54        match self.get_real_rootfs(inode)? {
55            (Left(fs), idata) => fs.setattr(ctx, idata.ino(), attr, handle, valid),
56            (Right(fs), idata) => {
57                fs.async_setattr(ctx, idata.ino(), attr, handle, valid)
58                    .await
59            }
60        }
61    }
62
63    async fn async_open(
64        &self,
65        ctx: &Context,
66        inode: <Self as FileSystem>::Inode,
67        flags: u32,
68        fuse_flags: u32,
69    ) -> Result<(Option<<Self as FileSystem>::Handle>, OpenOptions)> {
70        if self.opts.load().no_open {
71            Err(Error::from_raw_os_error(libc::ENOSYS))
72        } else {
73            match self.get_real_rootfs(inode)? {
74                (Left(fs), idata) => fs
75                    .open(ctx, idata.ino(), flags, fuse_flags)
76                    .map(|(a, b, _)| (a, b)),
77                (Right(fs), idata) => fs
78                    .async_open(ctx, idata.ino(), flags, fuse_flags)
79                    .await
80                    .map(|(h, opt)| (h.map(Into::into), opt)),
81            }
82        }
83    }
84
85    async fn async_create(
86        &self,
87        ctx: &Context,
88        parent: <Self as FileSystem>::Inode,
89        name: &CStr,
90        args: CreateIn,
91    ) -> Result<(Entry, Option<<Self as FileSystem>::Handle>, OpenOptions)> {
92        validate_path_component(name)?;
93
94        match self.get_real_rootfs(parent)? {
95            (Left(fs), idata) => fs
96                .create(ctx, idata.ino(), name, args)
97                .map(|(a, b, c, _)| (a, b, c)),
98            (Right(fs), idata) => {
99                fs.async_create(ctx, idata.ino(), name, args)
100                    .await
101                    .map(|(mut a, b, c)| {
102                        self.convert_entry(idata.fs_idx(), a.inode, &mut a)?;
103                        Ok((a, b, c))
104                    })?
105            }
106        }
107    }
108
109    #[allow(clippy::too_many_arguments)]
110    async fn async_read(
111        &self,
112        ctx: &Context,
113        inode: <Self as FileSystem>::Inode,
114        handle: <Self as FileSystem>::Handle,
115        w: &mut (dyn AsyncZeroCopyWriter + Send),
116        size: u32,
117        offset: u64,
118        lock_owner: Option<u64>,
119        flags: u32,
120    ) -> Result<usize> {
121        match self.get_real_rootfs(inode)? {
122            (Left(_fs), _idata) => Err(io::Error::from_raw_os_error(libc::ENOSYS)),
123            (Right(fs), idata) => {
124                fs.async_read(ctx, idata.ino(), handle, w, size, offset, lock_owner, flags)
125                    .await
126            }
127        }
128    }
129
130    #[allow(clippy::too_many_arguments)]
131    async fn async_write(
132        &self,
133        ctx: &Context,
134        inode: <Self as FileSystem>::Inode,
135        handle: <Self as FileSystem>::Handle,
136        r: &mut (dyn AsyncZeroCopyReader + Send),
137        size: u32,
138        offset: u64,
139        lock_owner: Option<u64>,
140        delayed_write: bool,
141        flags: u32,
142        fuse_flags: u32,
143    ) -> Result<usize> {
144        match self.get_real_rootfs(inode)? {
145            (Left(_fs), _idata) => Err(io::Error::from_raw_os_error(libc::ENOSYS)),
146            (Right(fs), idata) => {
147                fs.async_write(
148                    ctx,
149                    idata.ino(),
150                    handle,
151                    r,
152                    size,
153                    offset,
154                    lock_owner,
155                    delayed_write,
156                    flags,
157                    fuse_flags,
158                )
159                .await
160            }
161        }
162    }
163
164    async fn async_fsync(
165        &self,
166        ctx: &Context,
167        inode: <Self as FileSystem>::Inode,
168        datasync: bool,
169        handle: <Self as FileSystem>::Handle,
170    ) -> Result<()> {
171        match self.get_real_rootfs(inode)? {
172            (Left(fs), idata) => fs.fsync(ctx, idata.ino(), datasync, handle),
173            (Right(fs), idata) => fs.async_fsync(ctx, idata.ino(), datasync, handle).await,
174        }
175    }
176
177    async fn async_fallocate(
178        &self,
179        ctx: &Context,
180        inode: <Self as FileSystem>::Inode,
181        handle: <Self as FileSystem>::Handle,
182        mode: u32,
183        offset: u64,
184        length: u64,
185    ) -> Result<()> {
186        match self.get_real_rootfs(inode)? {
187            (Left(fs), idata) => fs.fallocate(ctx, idata.ino(), handle, mode, offset, length),
188            (Right(fs), idata) => {
189                fs.async_fallocate(ctx, idata.ino(), handle, mode, offset, length)
190                    .await
191            }
192        }
193    }
194
195    async fn async_fsyncdir(
196        &self,
197        ctx: &Context,
198        inode: <Self as FileSystem>::Inode,
199        datasync: bool,
200        handle: <Self as FileSystem>::Handle,
201    ) -> Result<()> {
202        match self.get_real_rootfs(inode)? {
203            (Left(fs), idata) => fs.fsyncdir(ctx, idata.ino(), datasync, handle),
204            (Right(fs), idata) => fs.async_fsyncdir(ctx, idata.ino(), datasync, handle).await,
205        }
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::super::tests::FakeFileSystemOne;
212    use super::*;
213    use crate::api::Vfs;
214
215    use std::ffi::CString;
216
217    #[tokio::test]
218    async fn test_vfs_async_lookup() {
219        let vfs = Vfs::new(VfsOptions::default());
220        let fs = FakeFileSystemOne {};
221        let ctx = Context {
222            uid: 0,
223            gid: 0,
224            pid: 0,
225        };
226
227        assert!(vfs.mount(Box::new(fs), "/x/y").is_ok());
228
229        let handle = tokio::spawn(async move {
230            // Lookup inode on pseudo file system.
231            let name = CString::new("x").unwrap();
232            let future = vfs.async_lookup(&ctx, ROOT_ID.into(), name.as_c_str());
233            let entry1 = future.await.unwrap();
234            assert_eq!(entry1.inode, 0x2);
235
236            // Lookup inode on mounted file system.
237            let entry2 = vfs
238                .async_lookup(
239                    &ctx,
240                    entry1.inode.into(),
241                    CString::new("y").unwrap().as_c_str(),
242                )
243                .await
244                .unwrap();
245            assert_eq!(entry2.inode, 0x100_0000_0000_0001);
246
247            // lookup for negative result.
248            let entry3 = vfs
249                .async_lookup(
250                    &ctx,
251                    entry2.inode.into(),
252                    CString::new("z").unwrap().as_c_str(),
253                )
254                .await
255                .unwrap();
256            assert_eq!(entry3.inode, 0);
257        });
258        handle.await.unwrap();
259    }
260}