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
310                .open(ctx, idata.ino(), flags, fuse_flags)
311                .map(|(h, opt, passthrough)| (h.map(Into::into), opt, passthrough)),
312        }
313    }
314
315    fn create(
316        &self,
317        ctx: &Context,
318        parent: VfsInode,
319        name: &CStr,
320        args: CreateIn,
321    ) -> Result<(Entry, Option<u64>, OpenOptions, Option<u32>)> {
322        validate_path_component(name)?;
323
324        match self.get_real_rootfs(parent)? {
325            (Left(fs), idata) => fs.create(ctx, idata.ino(), name, args),
326            (Right(fs), idata) => {
327                fs.create(ctx, idata.ino(), name, args)
328                    .map(|(mut a, b, c, d)| {
329                        self.convert_entry(idata.fs_idx(), a.inode, &mut a)?;
330                        Ok((a, b, c, d))
331                    })?
332            }
333        }
334    }
335
336    fn read(
337        &self,
338        ctx: &Context,
339        inode: VfsInode,
340        handle: u64,
341        w: &mut dyn ZeroCopyWriter,
342        size: u32,
343        offset: u64,
344        lock_owner: Option<u64>,
345        flags: u32,
346    ) -> Result<usize> {
347        match self.get_real_rootfs(inode)? {
348            (Left(fs), idata) => {
349                fs.read(ctx, idata.ino(), handle, w, size, offset, lock_owner, flags)
350            }
351            (Right(fs), idata) => {
352                fs.read(ctx, idata.ino(), handle, w, size, offset, lock_owner, flags)
353            }
354        }
355    }
356
357    fn write(
358        &self,
359        ctx: &Context,
360        inode: VfsInode,
361        handle: u64,
362        r: &mut dyn ZeroCopyReader,
363        size: u32,
364        offset: u64,
365        lock_owner: Option<u64>,
366        delayed_write: bool,
367        flags: u32,
368        fuse_flags: u32,
369    ) -> Result<usize> {
370        match self.get_real_rootfs(inode)? {
371            (Left(fs), idata) => fs.write(
372                ctx,
373                idata.ino(),
374                handle,
375                r,
376                size,
377                offset,
378                lock_owner,
379                delayed_write,
380                flags,
381                fuse_flags,
382            ),
383            (Right(fs), idata) => fs.write(
384                ctx,
385                idata.ino(),
386                handle,
387                r,
388                size,
389                offset,
390                lock_owner,
391                delayed_write,
392                flags,
393                fuse_flags,
394            ),
395        }
396    }
397
398    fn flush(&self, ctx: &Context, inode: VfsInode, handle: u64, lock_owner: u64) -> Result<()> {
399        match self.get_real_rootfs(inode)? {
400            (Left(fs), idata) => fs.flush(ctx, idata.ino(), handle, lock_owner),
401            (Right(fs), idata) => fs.flush(ctx, idata.ino(), handle, lock_owner),
402        }
403    }
404
405    fn fsync(&self, ctx: &Context, inode: VfsInode, datasync: bool, handle: u64) -> Result<()> {
406        match self.get_real_rootfs(inode)? {
407            (Left(fs), idata) => fs.fsync(ctx, idata.ino(), datasync, handle),
408            (Right(fs), idata) => fs.fsync(ctx, idata.ino(), datasync, handle),
409        }
410    }
411
412    fn fallocate(
413        &self,
414        ctx: &Context,
415        inode: VfsInode,
416        handle: u64,
417        mode: u32,
418        offset: u64,
419        length: u64,
420    ) -> Result<()> {
421        match self.get_real_rootfs(inode)? {
422            (Left(fs), idata) => fs.fallocate(ctx, idata.ino(), handle, mode, offset, length),
423            (Right(fs), idata) => fs.fallocate(ctx, idata.ino(), handle, mode, offset, length),
424        }
425    }
426
427    fn release(
428        &self,
429        ctx: &Context,
430        inode: VfsInode,
431        flags: u32,
432        handle: u64,
433        flush: bool,
434        flock_release: bool,
435        lock_owner: Option<u64>,
436    ) -> Result<()> {
437        match self.get_real_rootfs(inode)? {
438            (Left(fs), idata) => fs.release(
439                ctx,
440                idata.ino(),
441                flags,
442                handle,
443                flush,
444                flock_release,
445                lock_owner,
446            ),
447            (Right(fs), idata) => fs.release(
448                ctx,
449                idata.ino(),
450                flags,
451                handle,
452                flush,
453                flock_release,
454                lock_owner,
455            ),
456        }
457    }
458
459    fn statfs(&self, ctx: &Context, inode: VfsInode) -> Result<statvfs64> {
460        match self.get_real_rootfs(inode)? {
461            (Left(fs), idata) => fs.statfs(ctx, idata.ino()),
462            (Right(fs), idata) => fs.statfs(ctx, idata.ino()),
463        }
464    }
465
466    fn setxattr(
467        &self,
468        ctx: &Context,
469        inode: VfsInode,
470        name: &CStr,
471        value: &[u8],
472        flags: u32,
473    ) -> Result<()> {
474        validate_path_component(name)?;
475
476        match self.get_real_rootfs(inode)? {
477            (Left(fs), idata) => fs.setxattr(ctx, idata.ino(), name, value, flags),
478            (Right(fs), idata) => fs.setxattr(ctx, idata.ino(), name, value, flags),
479        }
480    }
481
482    fn getxattr(
483        &self,
484        ctx: &Context,
485        inode: VfsInode,
486        name: &CStr,
487        size: u32,
488    ) -> Result<GetxattrReply> {
489        validate_path_component(name)?;
490
491        match self.get_real_rootfs(inode)? {
492            (Left(fs), idata) => fs.getxattr(ctx, idata.ino(), name, size),
493            (Right(fs), idata) => fs.getxattr(ctx, idata.ino(), name, size),
494        }
495    }
496
497    fn listxattr(&self, ctx: &Context, inode: VfsInode, size: u32) -> Result<ListxattrReply> {
498        match self.get_real_rootfs(inode)? {
499            (Left(fs), idata) => fs.listxattr(ctx, idata.ino(), size),
500            (Right(fs), idata) => fs.listxattr(ctx, idata.ino(), size),
501        }
502    }
503
504    fn removexattr(&self, ctx: &Context, inode: VfsInode, name: &CStr) -> Result<()> {
505        validate_path_component(name)?;
506
507        match self.get_real_rootfs(inode)? {
508            (Left(fs), idata) => fs.removexattr(ctx, idata.ino(), name),
509            (Right(fs), idata) => fs.removexattr(ctx, idata.ino(), name),
510        }
511    }
512
513    fn opendir(
514        &self,
515        ctx: &Context,
516        inode: VfsInode,
517        flags: u32,
518    ) -> Result<(Option<VfsHandle>, OpenOptions)> {
519        #[cfg(target_os = "linux")]
520        if self.opts.load().no_opendir {
521            return Err(Error::from_raw_os_error(libc::ENOSYS));
522        }
523        match self.get_real_rootfs(inode)? {
524            (Left(fs), idata) => fs.opendir(ctx, idata.ino(), flags),
525            (Right(fs), idata) => fs
526                .opendir(ctx, idata.ino(), flags)
527                .map(|(h, opt)| (h.map(Into::into), opt)),
528        }
529    }
530
531    fn readdir(
532        &self,
533        ctx: &Context,
534        inode: VfsInode,
535        handle: u64,
536        size: u32,
537        offset: u64,
538        add_entry: &mut dyn FnMut(DirEntry) -> Result<usize>,
539    ) -> Result<()> {
540        match self.get_real_rootfs(inode)? {
541            (Left(fs), idata) => {
542                fs.readdir(
543                    ctx,
544                    idata.ino(),
545                    handle,
546                    size,
547                    offset,
548                    &mut |mut dir_entry| {
549                        match self.mountpoints.load().get(&dir_entry.ino) {
550                            // cross mountpoint, return mount root entry
551                            Some(mnt) => {
552                                dir_entry.ino = self.convert_inode(mnt.fs_idx, mnt.ino)?;
553                            }
554                            None => {
555                                dir_entry.ino =
556                                    self.convert_inode(idata.fs_idx(), dir_entry.ino)?;
557                            }
558                        }
559                        add_entry(dir_entry)
560                    },
561                )
562            }
563
564            (Right(fs), idata) => fs.readdir(
565                ctx,
566                idata.ino(),
567                handle,
568                size,
569                offset,
570                &mut |mut dir_entry| {
571                    let new_ino = self.convert_inode(idata.fs_idx(), dir_entry.ino)?;
572                    dir_entry.ino = new_ino;
573                    add_entry(dir_entry)
574                },
575            ),
576        }
577    }
578
579    fn readdirplus(
580        &self,
581        ctx: &Context,
582        inode: VfsInode,
583        handle: u64,
584        size: u32,
585        offset: u64,
586        add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result<usize>,
587    ) -> Result<()> {
588        match self.get_real_rootfs(inode)? {
589            (Left(fs), idata) => fs.readdirplus(
590                ctx,
591                idata.ino(),
592                handle,
593                size,
594                offset,
595                &mut |mut dir_entry, mut entry| {
596                    match self.mountpoints.load().get(&dir_entry.ino) {
597                        Some(mnt) => {
598                            // cross mountpoint, return mount root entry
599                            dir_entry.ino = self.convert_inode(mnt.fs_idx, mnt.ino)?;
600                            entry = mnt.root_entry;
601                        }
602                        None => {
603                            dir_entry.ino = self.convert_inode(idata.fs_idx(), dir_entry.ino)?;
604                            entry.inode = dir_entry.ino;
605                        }
606                    }
607                    entry.attr.st_ino = entry.inode;
608                    add_entry(dir_entry, entry)
609                },
610            ),
611
612            (Right(fs), idata) => fs.readdirplus(
613                ctx,
614                idata.ino(),
615                handle,
616                size,
617                offset,
618                &mut |mut dir_entry, mut entry| {
619                    dir_entry.ino = self.convert_inode(idata.fs_idx(), entry.inode)?;
620                    entry.inode = dir_entry.ino;
621                    entry.attr.st_ino = entry.inode;
622                    self.remap_attr_id(true, &mut entry.attr);
623                    add_entry(dir_entry, entry)
624                },
625            ),
626        }
627    }
628
629    fn fsyncdir(&self, ctx: &Context, inode: VfsInode, datasync: bool, handle: u64) -> Result<()> {
630        match self.get_real_rootfs(inode)? {
631            (Left(fs), idata) => fs.fsyncdir(ctx, idata.ino(), datasync, handle),
632            (Right(fs), idata) => fs.fsyncdir(ctx, idata.ino(), datasync, handle),
633        }
634    }
635
636    fn releasedir(&self, ctx: &Context, inode: VfsInode, flags: u32, handle: u64) -> Result<()> {
637        match self.get_real_rootfs(inode)? {
638            (Left(fs), idata) => fs.releasedir(ctx, idata.ino(), flags, handle),
639            (Right(fs), idata) => fs.releasedir(ctx, idata.ino(), flags, handle),
640        }
641    }
642
643    fn access(&self, ctx: &Context, inode: VfsInode, mask: u32) -> Result<()> {
644        match self.get_real_rootfs(inode)? {
645            (Left(fs), idata) => fs.access(ctx, idata.ino(), mask),
646            (Right(fs), idata) => fs.access(ctx, idata.ino(), mask),
647        }
648    }
649
650    #[inline]
651    fn id_remap(&self, ctx: &mut Context) -> Result<()> {
652        // If id_mapping is enabled, map the external ID to the internal ID.
653        if let Some((internal_id, external_id, range)) = self.id_mapping {
654            if ctx.uid >= external_id && ctx.uid < external_id + range {
655                ctx.uid += internal_id - external_id;
656            }
657            if ctx.gid >= external_id && ctx.gid < external_id + range {
658                ctx.gid += internal_id - external_id;
659            }
660        }
661
662        Ok(())
663    }
664
665    #[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
666    fn setupmapping(
667        &self,
668        ctx: &Context,
669        inode: VfsInode,
670        handle: u64,
671        foffset: u64,
672        len: u64,
673        flags: u64,
674        moffset: u64,
675        req: &mut dyn FsCacheReqHandler,
676    ) -> Result<()> {
677        match self.get_real_rootfs(inode)? {
678            (Left(fs), idata) => {
679                fs.setupmapping(ctx, idata.ino(), handle, foffset, len, flags, moffset, req)
680            }
681            (Right(fs), idata) => {
682                fs.setupmapping(ctx, idata.ino(), handle, foffset, len, flags, moffset, req)
683            }
684        }
685    }
686
687    #[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
688    fn removemapping(
689        &self,
690        ctx: &Context,
691        inode: VfsInode,
692        requests: Vec<virtio_fs::RemovemappingOne>,
693        req: &mut dyn FsCacheReqHandler,
694    ) -> Result<()> {
695        match self.get_real_rootfs(inode)? {
696            (Left(fs), idata) => fs.removemapping(ctx, idata.ino(), requests, req),
697            (Right(fs), idata) => fs.removemapping(ctx, idata.ino(), requests, req),
698        }
699    }
700}