fuse_backend_rs/api/vfs/
sync_io.rs

1// Copyright 2020 Ant Financial. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::sync::Arc;
5
6use super::*;
7use crate::abi::fuse_abi::{stat64, statvfs64};
8#[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
9use crate::abi::virtio_fs;
10#[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
11use crate::transport::FsCacheReqHandler;
12
13impl FileSystem for Vfs {
14    type Inode = VfsInode;
15    type Handle = VfsHandle;
16
17    fn init(&self, opts: FsOptions) -> Result<FsOptions> {
18        if self.initialized() {
19            error!("vfs is already initialized");
20            return Err(Error::from_raw_os_error(libc::EINVAL));
21        }
22        let mut n_opts = *self.opts.load().deref().deref();
23        #[cfg(target_os = "linux")]
24        {
25            if n_opts.no_open {
26                n_opts.no_open = !(opts & FsOptions::ZERO_MESSAGE_OPEN).is_empty();
27                // We can't support FUSE_ATOMIC_O_TRUNC with no_open
28                n_opts.out_opts.remove(FsOptions::ATOMIC_O_TRUNC);
29            } else {
30                n_opts.out_opts.remove(FsOptions::ZERO_MESSAGE_OPEN);
31            }
32            if n_opts.no_opendir {
33                n_opts.no_opendir = !(opts & FsOptions::ZERO_MESSAGE_OPENDIR).is_empty();
34            } else {
35                n_opts.out_opts.remove(FsOptions::ZERO_MESSAGE_OPENDIR);
36            }
37            if n_opts.no_writeback {
38                n_opts.out_opts.remove(FsOptions::WRITEBACK_CACHE);
39            }
40            if !n_opts.killpriv_v2 {
41                n_opts.out_opts.remove(FsOptions::HANDLE_KILLPRIV_V2);
42            }
43        }
44        n_opts.in_opts = opts;
45
46        n_opts.out_opts &= opts;
47        self.opts.store(Arc::new(n_opts));
48        {
49            // Serialize mount operations. Do not expect poisoned lock here.
50            // Ensure that every backend fs only get init()ed once.
51            let _guard = self.lock.lock().unwrap();
52            let superblocks = self.superblocks.load();
53
54            for fs in superblocks.iter().flatten() {
55                fs.init(n_opts.out_opts)?;
56            }
57            self.initialized.store(true, Ordering::Release);
58        }
59
60        Ok(n_opts.out_opts)
61    }
62
63    fn destroy(&self) {
64        if self.initialized() {
65            let superblocks = self.superblocks.load();
66
67            for fs in superblocks.iter().flatten() {
68                fs.destroy();
69            }
70
71            self.initialized.store(false, Ordering::Release);
72        }
73    }
74
75    fn lookup(&self, ctx: &Context, parent: VfsInode, name: &CStr) -> Result<Entry> {
76        // Don't use is_safe_path_component(), allow "." and ".." for NFS export support
77        if name.to_bytes_with_nul().contains(&SLASH_ASCII) {
78            return Err(io::Error::from_raw_os_error(libc::EINVAL));
79        }
80
81        match self.get_real_rootfs(parent)? {
82            (Left(fs), idata) => self.lookup_pseudo(fs, idata, ctx, name),
83            (Right(fs), idata) => {
84                // parent is in an underlying rootfs
85                let mut entry = fs.lookup(ctx, idata.ino(), name)?;
86                // lookup success, hash it to a real fuse inode
87                self.convert_entry(idata.fs_idx(), entry.inode, &mut entry)
88            }
89        }
90    }
91
92    fn forget(&self, ctx: &Context, inode: VfsInode, count: u64) {
93        match self.get_real_rootfs(inode) {
94            Ok(real_rootfs) => match real_rootfs {
95                (Left(fs), idata) => fs.forget(ctx, idata.ino(), count),
96                (Right(fs), idata) => fs.forget(ctx, idata.ino(), count),
97            },
98            Err(e) => {
99                // When a directory is umount and invalidate entry, a forget request is triggered,
100                // which cannot be forwarded to the backend file system.
101                // this situation is reasonable, so it is changed to warn here
102                warn!(
103                    "vfs::forget: failed to get_real_rootfs {:?}, inode: {:?}, 
104                       maybe it is possible that the vfs submount was dropped by umount, 
105                       which is reasonable.",
106                    e, inode
107                );
108            }
109        }
110    }
111
112    fn getattr(
113        &self,
114        ctx: &Context,
115        inode: VfsInode,
116        handle: Option<VfsHandle>,
117    ) -> Result<(stat64, Duration)> {
118        match self.get_real_rootfs(inode)? {
119            (Left(fs), idata) => fs.getattr(ctx, idata.ino(), handle),
120            (Right(fs), idata) => {
121                fs.getattr(ctx, idata.ino(), handle)
122                    .map(|(mut attr, duration)| {
123                        attr.st_ino = idata.into();
124                        self.remap_attr_id(true, &mut attr);
125                        (attr, duration)
126                    })
127            }
128        }
129    }
130
131    fn setattr(
132        &self,
133        ctx: &Context,
134        inode: VfsInode,
135        attr: stat64,
136        handle: Option<u64>,
137        valid: SetattrValid,
138    ) -> Result<(stat64, Duration)> {
139        match self.get_real_rootfs(inode)? {
140            (Left(fs), idata) => fs.setattr(ctx, idata.ino(), attr, handle, valid),
141            (Right(fs), idata) => {
142                let mut attr = attr;
143                self.remap_attr_id(false, &mut attr);
144                fs.setattr(ctx, idata.ino(), attr, handle, valid)
145                    .map(|(mut attr, duration)| {
146                        attr.st_ino = idata.into();
147                        self.remap_attr_id(true, &mut attr);
148                        (attr, duration)
149                    })
150            }
151        }
152    }
153
154    fn readlink(&self, ctx: &Context, inode: VfsInode) -> Result<Vec<u8>> {
155        match self.get_real_rootfs(inode)? {
156            (Left(fs), idata) => fs.readlink(ctx, idata.ino()),
157            (Right(fs), idata) => fs.readlink(ctx, idata.ino()),
158        }
159    }
160
161    fn symlink(
162        &self,
163        ctx: &Context,
164        linkname: &CStr,
165        parent: VfsInode,
166        name: &CStr,
167    ) -> Result<Entry> {
168        validate_path_component(name)?;
169
170        match self.get_real_rootfs(parent)? {
171            (Left(fs), idata) => fs.symlink(ctx, linkname, idata.ino(), name),
172            (Right(fs), idata) => fs
173                .symlink(ctx, linkname, idata.ino(), name)
174                .map(|mut e| self.convert_entry(idata.fs_idx(), e.inode, &mut e))?,
175        }
176    }
177
178    fn mknod(
179        &self,
180        ctx: &Context,
181        inode: VfsInode,
182        name: &CStr,
183        mode: u32,
184        rdev: u32,
185        umask: u32,
186    ) -> Result<Entry> {
187        validate_path_component(name)?;
188
189        match self.get_real_rootfs(inode)? {
190            (Left(fs), idata) => fs.mknod(ctx, idata.ino(), name, mode, rdev, umask),
191            (Right(fs), idata) => fs
192                .mknod(ctx, idata.ino(), name, mode, rdev, umask)
193                .map(|mut e| self.convert_entry(idata.fs_idx(), e.inode, &mut e))?,
194        }
195    }
196
197    fn mkdir(
198        &self,
199        ctx: &Context,
200        parent: VfsInode,
201        name: &CStr,
202        mode: u32,
203        umask: u32,
204    ) -> Result<Entry> {
205        validate_path_component(name)?;
206
207        match self.get_real_rootfs(parent)? {
208            (Left(fs), idata) => fs.mkdir(ctx, idata.ino(), name, mode, umask),
209            (Right(fs), idata) => fs
210                .mkdir(ctx, idata.ino(), name, mode, umask)
211                .map(|mut e| self.convert_entry(idata.fs_idx(), e.inode, &mut e))?,
212        }
213    }
214
215    fn unlink(&self, ctx: &Context, parent: VfsInode, name: &CStr) -> Result<()> {
216        validate_path_component(name)?;
217
218        match self.get_real_rootfs(parent)? {
219            (Left(fs), idata) => fs.unlink(ctx, idata.ino(), name),
220            (Right(fs), idata) => fs.unlink(ctx, idata.ino(), name),
221        }
222    }
223
224    fn rmdir(&self, ctx: &Context, parent: VfsInode, name: &CStr) -> Result<()> {
225        validate_path_component(name)?;
226
227        match self.get_real_rootfs(parent)? {
228            (Left(fs), idata) => fs.rmdir(ctx, idata.ino(), name),
229            (Right(fs), idata) => fs.rmdir(ctx, idata.ino(), name),
230        }
231    }
232
233    fn rename(
234        &self,
235        ctx: &Context,
236        olddir: VfsInode,
237        oldname: &CStr,
238        newdir: VfsInode,
239        newname: &CStr,
240        flags: u32,
241    ) -> Result<()> {
242        validate_path_component(oldname)?;
243        validate_path_component(newname)?;
244
245        let (root, idata_old) = self.get_real_rootfs(olddir)?;
246        let (_, idata_new) = self.get_real_rootfs(newdir)?;
247
248        if idata_old.fs_idx() != idata_new.fs_idx() {
249            return Err(Error::from_raw_os_error(libc::EINVAL));
250        }
251
252        match root {
253            Left(fs) => fs.rename(
254                ctx,
255                idata_old.ino(),
256                oldname,
257                idata_new.ino(),
258                newname,
259                flags,
260            ),
261            Right(fs) => fs.rename(
262                ctx,
263                idata_old.ino(),
264                oldname,
265                idata_new.ino(),
266                newname,
267                flags,
268            ),
269        }
270    }
271
272    fn link(
273        &self,
274        ctx: &Context,
275        inode: VfsInode,
276        newparent: VfsInode,
277        newname: &CStr,
278    ) -> Result<Entry> {
279        validate_path_component(newname)?;
280
281        let (root, idata_old) = self.get_real_rootfs(inode)?;
282        let (_, idata_new) = self.get_real_rootfs(newparent)?;
283
284        if idata_old.fs_idx() != idata_new.fs_idx() {
285            return Err(Error::from_raw_os_error(libc::EINVAL));
286        }
287
288        match root {
289            Left(fs) => fs.link(ctx, idata_old.ino(), idata_new.ino(), newname),
290            Right(fs) => fs
291                .link(ctx, idata_old.ino(), idata_new.ino(), newname)
292                .map(|mut e| self.convert_entry(idata_new.fs_idx(), e.inode, &mut e))?,
293        }
294    }
295
296    fn open(
297        &self,
298        ctx: &Context,
299        inode: VfsInode,
300        flags: u32,
301        fuse_flags: u32,
302    ) -> Result<(Option<u64>, OpenOptions, Option<u32>)> {
303        #[cfg(target_os = "linux")]
304        if self.opts.load().no_open {
305            return Err(Error::from_raw_os_error(libc::ENOSYS));
306        }
307        match self.get_real_rootfs(inode)? {
308            (Left(fs), idata) => fs.open(ctx, idata.ino(), flags, fuse_flags),
309            (Right(fs), idata) => fs.open(ctx, idata.ino(), flags, fuse_flags),
310        }
311    }
312
313    fn create(
314        &self,
315        ctx: &Context,
316        parent: VfsInode,
317        name: &CStr,
318        args: CreateIn,
319    ) -> Result<(Entry, Option<u64>, OpenOptions, Option<u32>)> {
320        validate_path_component(name)?;
321
322        match self.get_real_rootfs(parent)? {
323            (Left(fs), idata) => fs.create(ctx, idata.ino(), name, args),
324            (Right(fs), idata) => {
325                fs.create(ctx, idata.ino(), name, args)
326                    .map(|(mut a, b, c, d)| {
327                        self.convert_entry(idata.fs_idx(), a.inode, &mut a)?;
328                        Ok((a, b, c, d))
329                    })?
330            }
331        }
332    }
333
334    fn read(
335        &self,
336        ctx: &Context,
337        inode: VfsInode,
338        handle: u64,
339        w: &mut dyn ZeroCopyWriter,
340        size: u32,
341        offset: u64,
342        lock_owner: Option<u64>,
343        flags: u32,
344    ) -> Result<usize> {
345        match self.get_real_rootfs(inode)? {
346            (Left(fs), idata) => {
347                fs.read(ctx, idata.ino(), handle, w, size, offset, lock_owner, flags)
348            }
349            (Right(fs), idata) => {
350                fs.read(ctx, idata.ino(), handle, w, size, offset, lock_owner, flags)
351            }
352        }
353    }
354
355    fn write(
356        &self,
357        ctx: &Context,
358        inode: VfsInode,
359        handle: u64,
360        r: &mut dyn ZeroCopyReader,
361        size: u32,
362        offset: u64,
363        lock_owner: Option<u64>,
364        delayed_write: bool,
365        flags: u32,
366        fuse_flags: u32,
367    ) -> Result<usize> {
368        match self.get_real_rootfs(inode)? {
369            (Left(fs), idata) => fs.write(
370                ctx,
371                idata.ino(),
372                handle,
373                r,
374                size,
375                offset,
376                lock_owner,
377                delayed_write,
378                flags,
379                fuse_flags,
380            ),
381            (Right(fs), idata) => fs.write(
382                ctx,
383                idata.ino(),
384                handle,
385                r,
386                size,
387                offset,
388                lock_owner,
389                delayed_write,
390                flags,
391                fuse_flags,
392            ),
393        }
394    }
395
396    fn flush(&self, ctx: &Context, inode: VfsInode, handle: u64, lock_owner: u64) -> Result<()> {
397        match self.get_real_rootfs(inode)? {
398            (Left(fs), idata) => fs.flush(ctx, idata.ino(), handle, lock_owner),
399            (Right(fs), idata) => fs.flush(ctx, idata.ino(), handle, lock_owner),
400        }
401    }
402
403    fn fsync(&self, ctx: &Context, inode: VfsInode, datasync: bool, handle: u64) -> Result<()> {
404        match self.get_real_rootfs(inode)? {
405            (Left(fs), idata) => fs.fsync(ctx, idata.ino(), datasync, handle),
406            (Right(fs), idata) => fs.fsync(ctx, idata.ino(), datasync, handle),
407        }
408    }
409
410    fn fallocate(
411        &self,
412        ctx: &Context,
413        inode: VfsInode,
414        handle: u64,
415        mode: u32,
416        offset: u64,
417        length: u64,
418    ) -> Result<()> {
419        match self.get_real_rootfs(inode)? {
420            (Left(fs), idata) => fs.fallocate(ctx, idata.ino(), handle, mode, offset, length),
421            (Right(fs), idata) => fs.fallocate(ctx, idata.ino(), handle, mode, offset, length),
422        }
423    }
424
425    fn release(
426        &self,
427        ctx: &Context,
428        inode: VfsInode,
429        flags: u32,
430        handle: u64,
431        flush: bool,
432        flock_release: bool,
433        lock_owner: Option<u64>,
434    ) -> Result<()> {
435        match self.get_real_rootfs(inode)? {
436            (Left(fs), idata) => fs.release(
437                ctx,
438                idata.ino(),
439                flags,
440                handle,
441                flush,
442                flock_release,
443                lock_owner,
444            ),
445            (Right(fs), idata) => fs.release(
446                ctx,
447                idata.ino(),
448                flags,
449                handle,
450                flush,
451                flock_release,
452                lock_owner,
453            ),
454        }
455    }
456
457    fn statfs(&self, ctx: &Context, inode: VfsInode) -> Result<statvfs64> {
458        match self.get_real_rootfs(inode)? {
459            (Left(fs), idata) => fs.statfs(ctx, idata.ino()),
460            (Right(fs), idata) => fs.statfs(ctx, idata.ino()),
461        }
462    }
463
464    fn setxattr(
465        &self,
466        ctx: &Context,
467        inode: VfsInode,
468        name: &CStr,
469        value: &[u8],
470        flags: u32,
471    ) -> Result<()> {
472        validate_path_component(name)?;
473
474        match self.get_real_rootfs(inode)? {
475            (Left(fs), idata) => fs.setxattr(ctx, idata.ino(), name, value, flags),
476            (Right(fs), idata) => fs.setxattr(ctx, idata.ino(), name, value, flags),
477        }
478    }
479
480    fn getxattr(
481        &self,
482        ctx: &Context,
483        inode: VfsInode,
484        name: &CStr,
485        size: u32,
486    ) -> Result<GetxattrReply> {
487        validate_path_component(name)?;
488
489        match self.get_real_rootfs(inode)? {
490            (Left(fs), idata) => fs.getxattr(ctx, idata.ino(), name, size),
491            (Right(fs), idata) => fs.getxattr(ctx, idata.ino(), name, size),
492        }
493    }
494
495    fn listxattr(&self, ctx: &Context, inode: VfsInode, size: u32) -> Result<ListxattrReply> {
496        match self.get_real_rootfs(inode)? {
497            (Left(fs), idata) => fs.listxattr(ctx, idata.ino(), size),
498            (Right(fs), idata) => fs.listxattr(ctx, idata.ino(), size),
499        }
500    }
501
502    fn removexattr(&self, ctx: &Context, inode: VfsInode, name: &CStr) -> Result<()> {
503        validate_path_component(name)?;
504
505        match self.get_real_rootfs(inode)? {
506            (Left(fs), idata) => fs.removexattr(ctx, idata.ino(), name),
507            (Right(fs), idata) => fs.removexattr(ctx, idata.ino(), name),
508        }
509    }
510
511    fn opendir(
512        &self,
513        ctx: &Context,
514        inode: VfsInode,
515        flags: u32,
516    ) -> Result<(Option<VfsHandle>, OpenOptions)> {
517        #[cfg(target_os = "linux")]
518        if self.opts.load().no_opendir {
519            return Err(Error::from_raw_os_error(libc::ENOSYS));
520        }
521        match self.get_real_rootfs(inode)? {
522            (Left(fs), idata) => fs.opendir(ctx, idata.ino(), flags),
523            (Right(fs), idata) => fs.opendir(ctx, idata.ino(), flags),
524        }
525    }
526
527    fn readdir(
528        &self,
529        ctx: &Context,
530        inode: VfsInode,
531        handle: u64,
532        size: u32,
533        offset: u64,
534        add_entry: &mut dyn FnMut(DirEntry) -> Result<usize>,
535    ) -> Result<()> {
536        match self.get_real_rootfs(inode)? {
537            (Left(fs), idata) => {
538                fs.readdir(
539                    ctx,
540                    idata.ino(),
541                    handle,
542                    size,
543                    offset,
544                    &mut |mut dir_entry| {
545                        match self.mountpoints.load().get(&dir_entry.ino) {
546                            // cross mountpoint, return mount root entry
547                            Some(mnt) => {
548                                dir_entry.ino = self.convert_inode(mnt.fs_idx, mnt.ino)?;
549                            }
550                            None => {
551                                dir_entry.ino =
552                                    self.convert_inode(idata.fs_idx(), dir_entry.ino)?;
553                            }
554                        }
555                        add_entry(dir_entry)
556                    },
557                )
558            }
559
560            (Right(fs), idata) => fs.readdir(
561                ctx,
562                idata.ino(),
563                handle,
564                size,
565                offset,
566                &mut |mut dir_entry| {
567                    let new_ino = self.convert_inode(idata.fs_idx(), dir_entry.ino)?;
568                    dir_entry.ino = new_ino;
569                    add_entry(dir_entry)
570                },
571            ),
572        }
573    }
574
575    fn readdirplus(
576        &self,
577        ctx: &Context,
578        inode: VfsInode,
579        handle: u64,
580        size: u32,
581        offset: u64,
582        add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result<usize>,
583    ) -> Result<()> {
584        match self.get_real_rootfs(inode)? {
585            (Left(fs), idata) => fs.readdirplus(
586                ctx,
587                idata.ino(),
588                handle,
589                size,
590                offset,
591                &mut |mut dir_entry, mut entry| {
592                    match self.mountpoints.load().get(&dir_entry.ino) {
593                        Some(mnt) => {
594                            // cross mountpoint, return mount root entry
595                            dir_entry.ino = self.convert_inode(mnt.fs_idx, mnt.ino)?;
596                            entry = mnt.root_entry;
597                        }
598                        None => {
599                            dir_entry.ino = self.convert_inode(idata.fs_idx(), dir_entry.ino)?;
600                            entry.inode = dir_entry.ino;
601                        }
602                    }
603                    entry.attr.st_ino = entry.inode;
604                    add_entry(dir_entry, entry)
605                },
606            ),
607
608            (Right(fs), idata) => fs.readdirplus(
609                ctx,
610                idata.ino(),
611                handle,
612                size,
613                offset,
614                &mut |mut dir_entry, mut entry| {
615                    dir_entry.ino = self.convert_inode(idata.fs_idx(), entry.inode)?;
616                    entry.inode = dir_entry.ino;
617                    entry.attr.st_ino = entry.inode;
618                    self.remap_attr_id(true, &mut entry.attr);
619                    add_entry(dir_entry, entry)
620                },
621            ),
622        }
623    }
624
625    fn fsyncdir(&self, ctx: &Context, inode: VfsInode, datasync: bool, handle: u64) -> Result<()> {
626        match self.get_real_rootfs(inode)? {
627            (Left(fs), idata) => fs.fsyncdir(ctx, idata.ino(), datasync, handle),
628            (Right(fs), idata) => fs.fsyncdir(ctx, idata.ino(), datasync, handle),
629        }
630    }
631
632    fn releasedir(&self, ctx: &Context, inode: VfsInode, flags: u32, handle: u64) -> Result<()> {
633        match self.get_real_rootfs(inode)? {
634            (Left(fs), idata) => fs.releasedir(ctx, idata.ino(), flags, handle),
635            (Right(fs), idata) => fs.releasedir(ctx, idata.ino(), flags, handle),
636        }
637    }
638
639    fn access(&self, ctx: &Context, inode: VfsInode, mask: u32) -> Result<()> {
640        match self.get_real_rootfs(inode)? {
641            (Left(fs), idata) => fs.access(ctx, idata.ino(), mask),
642            (Right(fs), idata) => fs.access(ctx, idata.ino(), mask),
643        }
644    }
645
646    #[inline]
647    fn id_remap(&self, ctx: &mut Context) -> Result<()> {
648        // If id_mapping is enabled, map the external ID to the internal ID.
649        if let Some((internal_id, external_id, range)) = self.id_mapping {
650            if ctx.uid >= external_id && ctx.uid < external_id + range {
651                ctx.uid += internal_id - external_id;
652            }
653            if ctx.gid >= external_id && ctx.gid < external_id + range {
654                ctx.gid += internal_id - external_id;
655            }
656        }
657
658        Ok(())
659    }
660
661    #[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
662    fn setupmapping(
663        &self,
664        ctx: &Context,
665        inode: VfsInode,
666        handle: u64,
667        foffset: u64,
668        len: u64,
669        flags: u64,
670        moffset: u64,
671        req: &mut dyn FsCacheReqHandler,
672    ) -> Result<()> {
673        match self.get_real_rootfs(inode)? {
674            (Left(fs), idata) => {
675                fs.setupmapping(ctx, idata.ino(), handle, foffset, len, flags, moffset, req)
676            }
677            (Right(fs), idata) => {
678                fs.setupmapping(ctx, idata.ino(), handle, foffset, len, flags, moffset, req)
679            }
680        }
681    }
682
683    #[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
684    fn removemapping(
685        &self,
686        ctx: &Context,
687        inode: VfsInode,
688        requests: Vec<virtio_fs::RemovemappingOne>,
689        req: &mut dyn FsCacheReqHandler,
690    ) -> Result<()> {
691        match self.get_real_rootfs(inode)? {
692            (Left(fs), idata) => fs.removemapping(ctx, idata.ino(), requests, req),
693            (Right(fs), idata) => fs.removemapping(ctx, idata.ino(), requests, req),
694        }
695    }
696}