fuse_backend_rs/passthrough/
sync_io.rs

1// Copyright (C) 2020 Alibaba Cloud. All rights reserved.
2// Copyright 2019 The Chromium OS Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE-BSD-3-Clause file.
5
6//! Fuse passthrough file system, mirroring an existing FS hierarchy.
7
8use std::ffi::{CStr, CString};
9use std::fs::File;
10use std::io;
11use std::mem::{self, size_of, ManuallyDrop, MaybeUninit};
12use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
13use std::sync::atomic::Ordering;
14use std::sync::Arc;
15use std::time::Duration;
16
17use super::os_compat::LinuxDirent64;
18use super::util::{stat_fd, sync_fd};
19use super::*;
20use crate::abi::fuse_abi::{CreateIn, Opcode, FOPEN_IN_KILL_SUIDGID, WRITE_KILL_PRIV};
21#[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
22use crate::abi::virtio_fs;
23use crate::api::filesystem::{
24    Context, DirEntry, Entry, FileSystem, FsOptions, GetxattrReply, ListxattrReply, OpenOptions,
25    SetattrValid, ZeroCopyReader, ZeroCopyWriter,
26};
27use crate::bytes_to_cstr;
28#[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
29use crate::transport::FsCacheReqHandler;
30
31impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
32    fn open_inode(&self, inode: Inode, flags: i32) -> io::Result<File> {
33        let data = self.inode_map.get(inode)?;
34        if !is_safe_inode(data.mode) {
35            Err(ebadf())
36        } else {
37            let mut new_flags = self.get_writeback_open_flags(flags);
38            if !self.cfg.allow_direct_io && flags & libc::O_DIRECT != 0 {
39                new_flags &= !libc::O_DIRECT;
40            }
41            data.open_file(new_flags | libc::O_CLOEXEC, &self.proc_self_fd)
42        }
43    }
44
45    /// Check the HandleData flags against the flags from the current request
46    /// if these do not match update the file descriptor flags and store the new
47    /// result in the HandleData entry
48    #[inline(always)]
49    fn check_fd_flags(&self, data: Arc<HandleData>, fd: RawFd, flags: u32) -> io::Result<()> {
50        let open_flags = data.get_flags();
51        if open_flags != flags {
52            let ret = unsafe { libc::fcntl(fd, libc::F_SETFL, flags) };
53            if ret != 0 {
54                return Err(io::Error::last_os_error());
55            }
56            data.set_flags(flags);
57        }
58        Ok(())
59    }
60
61    fn do_readdir(
62        &self,
63        inode: Inode,
64        handle: Handle,
65        size: u32,
66        offset: u64,
67        add_entry: &mut dyn FnMut(DirEntry, RawFd) -> io::Result<usize>,
68    ) -> io::Result<()> {
69        if size == 0 {
70            return Ok(());
71        }
72
73        let mut buf = Vec::<u8>::with_capacity(size as usize);
74        let data = self.get_dirdata(handle, inode, libc::O_RDONLY)?;
75
76        {
77            // Since we are going to work with the kernel offset, we have to acquire the file lock
78            // for both the `lseek64` and `getdents64` syscalls to ensure that no other thread
79            // changes the kernel offset while we are using it.
80            let (guard, dir) = data.get_file_mut();
81
82            // Safe because this doesn't modify any memory and we check the return value.
83            let res =
84                unsafe { libc::lseek64(dir.as_raw_fd(), offset as libc::off64_t, libc::SEEK_SET) };
85            if res < 0 {
86                return Err(io::Error::last_os_error());
87            }
88
89            // Safe because the kernel guarantees that it will only write to `buf` and we check the
90            // return value.
91            let res = unsafe {
92                libc::syscall(
93                    libc::SYS_getdents64,
94                    dir.as_raw_fd(),
95                    buf.as_mut_ptr() as *mut LinuxDirent64,
96                    size as libc::c_int,
97                )
98            };
99            if res < 0 {
100                return Err(io::Error::last_os_error());
101            }
102
103            // Safe because we trust the value returned by kernel.
104            unsafe { buf.set_len(res as usize) };
105
106            // Explicitly drop the lock so that it's not held while we fill in the fuse buffer.
107            mem::drop(guard);
108        }
109
110        let mut rem = &buf[..];
111        let orig_rem_len = rem.len();
112        while !rem.is_empty() {
113            // We only use debug asserts here because these values are coming from the kernel and we
114            // trust them implicitly.
115            debug_assert!(
116                rem.len() >= size_of::<LinuxDirent64>(),
117                "fuse: not enough space left in `rem`"
118            );
119
120            let (front, back) = rem.split_at(size_of::<LinuxDirent64>());
121
122            let dirent64 = LinuxDirent64::from_slice(front)
123                .expect("fuse: unable to get LinuxDirent64 from slice");
124
125            let namelen = dirent64.d_reclen as usize - size_of::<LinuxDirent64>();
126            debug_assert!(
127                namelen <= back.len(),
128                "fuse: back is smaller than `namelen`"
129            );
130
131            let name = &back[..namelen];
132            let res = if name.starts_with(CURRENT_DIR_CSTR) || name.starts_with(PARENT_DIR_CSTR) {
133                // We don't want to report the "." and ".." entries. However, returning `Ok(0)` will
134                // break the loop so return `Ok` with a non-zero value instead.
135                Ok(1)
136            } else {
137                // The Sys_getdents64 in kernel will pad the name with '\0'
138                // bytes up to 8-byte alignment, so @name may contain a few null
139                // terminators.  This causes an extra lookup from fuse when
140                // called by readdirplus, because kernel path walking only takes
141                // name without null terminators, the dentry with more than 1
142                // null terminators added by readdirplus doesn't satisfy the
143                // path walking.
144                let name = bytes_to_cstr(name)
145                    .map_err(|e| {
146                        error!("fuse: do_readdir: {:?}", e);
147                        einval()
148                    })?
149                    .to_bytes();
150
151                add_entry(
152                    DirEntry {
153                        ino: dirent64.d_ino,
154                        offset: dirent64.d_off as u64,
155                        type_: u32::from(dirent64.d_ty),
156                        name,
157                    },
158                    data.borrow_fd().as_raw_fd(),
159                )
160            };
161
162            debug_assert!(
163                rem.len() >= dirent64.d_reclen as usize,
164                "fuse: rem is smaller than `d_reclen`"
165            );
166
167            match res {
168                Ok(0) => break,
169                Ok(_) => rem = &rem[dirent64.d_reclen as usize..],
170                // If there's an error, we can only signal it if we haven't
171                // stored any entries yet - otherwise we'd end up with wrong
172                // lookup counts for the entries that are already in the
173                // buffer. So we return what we've collected until that point.
174                Err(e) if rem.len() == orig_rem_len => return Err(e),
175                Err(_) => return Ok(()),
176            }
177        }
178
179        Ok(())
180    }
181
182    fn do_open(
183        &self,
184        inode: Inode,
185        flags: u32,
186        fuse_flags: u32,
187    ) -> io::Result<(Option<Handle>, OpenOptions, Option<u32>)> {
188        let killpriv = if self.killpriv_v2.load(Ordering::Relaxed)
189            && (fuse_flags & FOPEN_IN_KILL_SUIDGID != 0)
190        {
191            self::drop_cap_fsetid()?
192        } else {
193            None
194        };
195        let file = self.open_inode(inode, flags as i32)?;
196        drop(killpriv);
197
198        let data = HandleData::new(inode, file, flags);
199        let handle = self.next_handle.fetch_add(1, Ordering::Relaxed);
200        self.handle_map.insert(handle, data);
201
202        let mut opts = OpenOptions::empty();
203        match self.cfg.cache_policy {
204            // We only set the direct I/O option on files.
205            CachePolicy::Never => opts.set(
206                OpenOptions::DIRECT_IO,
207                flags & (libc::O_DIRECTORY as u32) == 0,
208            ),
209            CachePolicy::Metadata => {
210                if flags & (libc::O_DIRECTORY as u32) == 0 {
211                    opts |= OpenOptions::DIRECT_IO;
212                } else {
213                    opts |= OpenOptions::CACHE_DIR | OpenOptions::KEEP_CACHE;
214                }
215            }
216            CachePolicy::Always => {
217                opts |= OpenOptions::KEEP_CACHE;
218                if flags & (libc::O_DIRECTORY as u32) != 0 {
219                    opts |= OpenOptions::CACHE_DIR;
220                }
221            }
222            _ => {}
223        };
224
225        Ok((Some(handle), opts, None))
226    }
227
228    fn do_getattr(
229        &self,
230        inode: Inode,
231        handle: Option<Handle>,
232    ) -> io::Result<(libc::stat64, Duration)> {
233        let data = self.inode_map.get(inode).map_err(|e| {
234            error!("fuse: do_getattr ino {} Not find err {:?}", inode, e);
235            e
236        })?;
237
238        // kernel sends 0 as handle in case of no_open, and it depends on fuse server to handle
239        // this case correctly.
240        let st = if !self.no_open.load(Ordering::Relaxed) && handle.is_some() {
241            // Safe as we just checked handle
242            let hd = self.handle_map.get(handle.unwrap(), inode)?;
243            stat_fd(hd.get_file(), None)
244        } else {
245            data.handle.stat()
246        };
247
248        let st = st.map_err(|e| {
249            error!("fuse: do_getattr stat failed ino {} err {:?}", inode, e);
250            e
251        })?;
252
253        Ok((st, self.cfg.attr_timeout))
254    }
255
256    fn do_unlink(&self, parent: Inode, name: &CStr, flags: libc::c_int) -> io::Result<()> {
257        let data = self.inode_map.get(parent)?;
258        let file = data.get_file()?;
259        // Safe because this doesn't modify any memory and we check the return value.
260        let res = unsafe { libc::unlinkat(file.as_raw_fd(), name.as_ptr(), flags) };
261        if res == 0 {
262            Ok(())
263        } else {
264            Err(io::Error::last_os_error())
265        }
266    }
267
268    fn get_dirdata(
269        &self,
270        handle: Handle,
271        inode: Inode,
272        flags: libc::c_int,
273    ) -> io::Result<Arc<HandleData>> {
274        let no_open = self.no_opendir.load(Ordering::Relaxed);
275        if !no_open {
276            self.handle_map.get(handle, inode)
277        } else {
278            let file = self.open_inode(inode, flags | libc::O_DIRECTORY)?;
279            Ok(Arc::new(HandleData::new(inode, file, flags as u32)))
280        }
281    }
282
283    fn get_data(
284        &self,
285        handle: Handle,
286        inode: Inode,
287        flags: libc::c_int,
288    ) -> io::Result<Arc<HandleData>> {
289        let no_open = self.no_open.load(Ordering::Relaxed);
290        if !no_open {
291            self.handle_map.get(handle, inode)
292        } else {
293            let file = self.open_inode(inode, flags)?;
294            Ok(Arc::new(HandleData::new(inode, file, flags as u32)))
295        }
296    }
297}
298
299impl<S: BitmapSlice + Send + Sync> FileSystem for PassthroughFs<S> {
300    type Inode = Inode;
301    type Handle = Handle;
302
303    fn init(&self, capable: FsOptions) -> io::Result<FsOptions> {
304        if self.cfg.do_import {
305            self.import()?;
306        }
307
308        let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO;
309        // !cfg.do_import means we are under vfs, in which case capable is already
310        // negotiated and must be honored.
311        if (!self.cfg.do_import || self.cfg.writeback)
312            && capable.contains(FsOptions::WRITEBACK_CACHE)
313        {
314            opts |= FsOptions::WRITEBACK_CACHE;
315            self.writeback.store(true, Ordering::Relaxed);
316        }
317        if (!self.cfg.do_import || self.cfg.no_open)
318            && capable.contains(FsOptions::ZERO_MESSAGE_OPEN)
319        {
320            opts |= FsOptions::ZERO_MESSAGE_OPEN;
321            // We can't support FUSE_ATOMIC_O_TRUNC with no_open
322            opts.remove(FsOptions::ATOMIC_O_TRUNC);
323            self.no_open.store(true, Ordering::Relaxed);
324        }
325        if (!self.cfg.do_import || self.cfg.no_opendir)
326            && capable.contains(FsOptions::ZERO_MESSAGE_OPENDIR)
327        {
328            opts |= FsOptions::ZERO_MESSAGE_OPENDIR;
329            self.no_opendir.store(true, Ordering::Relaxed);
330        }
331        if (!self.cfg.do_import || self.cfg.killpriv_v2)
332            && capable.contains(FsOptions::HANDLE_KILLPRIV_V2)
333        {
334            opts |= FsOptions::HANDLE_KILLPRIV_V2;
335            self.killpriv_v2.store(true, Ordering::Relaxed);
336        }
337
338        if capable.contains(FsOptions::PERFILE_DAX) {
339            opts |= FsOptions::PERFILE_DAX;
340            self.perfile_dax.store(true, Ordering::Relaxed);
341        }
342
343        Ok(opts)
344    }
345
346    fn destroy(&self) {
347        self.handle_map.clear();
348        self.inode_map.clear();
349
350        if let Err(e) = self.import() {
351            error!("fuse: failed to destroy instance, {:?}", e);
352        };
353    }
354
355    fn statfs(&self, _ctx: &Context, inode: Inode) -> io::Result<libc::statvfs64> {
356        let mut out = MaybeUninit::<libc::statvfs64>::zeroed();
357        let data = self.inode_map.get(inode)?;
358        let file = data.get_file()?;
359
360        // Safe because this will only modify `out` and we check the return value.
361        match unsafe { libc::fstatvfs64(file.as_raw_fd(), out.as_mut_ptr()) } {
362            // Safe because the kernel guarantees that `out` has been initialized.
363            0 => Ok(unsafe { out.assume_init() }),
364            _ => Err(io::Error::last_os_error()),
365        }
366    }
367
368    fn lookup(&self, _ctx: &Context, parent: Inode, name: &CStr) -> io::Result<Entry> {
369        // Don't use is_safe_path_component(), allow "." and ".." for NFS export support
370        if name.to_bytes_with_nul().contains(&SLASH_ASCII) {
371            return Err(einval());
372        }
373        self.do_lookup(parent, name)
374    }
375
376    fn forget(&self, _ctx: &Context, inode: Inode, count: u64) {
377        let mut inodes = self.inode_map.get_map_mut();
378
379        self.forget_one(&mut inodes, inode, count)
380    }
381
382    fn batch_forget(&self, _ctx: &Context, requests: Vec<(Inode, u64)>) {
383        let mut inodes = self.inode_map.get_map_mut();
384
385        for (inode, count) in requests {
386            self.forget_one(&mut inodes, inode, count)
387        }
388    }
389
390    fn opendir(
391        &self,
392        _ctx: &Context,
393        inode: Inode,
394        flags: u32,
395    ) -> io::Result<(Option<Handle>, OpenOptions)> {
396        if self.no_opendir.load(Ordering::Relaxed) {
397            info!("fuse: opendir is not supported.");
398            Err(enosys())
399        } else {
400            self.do_open(inode, flags | (libc::O_DIRECTORY as u32), 0)
401                .map(|(a, b, _)| (a, b))
402        }
403    }
404
405    fn releasedir(
406        &self,
407        _ctx: &Context,
408        inode: Inode,
409        _flags: u32,
410        handle: Handle,
411    ) -> io::Result<()> {
412        if self.no_opendir.load(Ordering::Relaxed) {
413            info!("fuse: releasedir is not supported.");
414            Err(io::Error::from_raw_os_error(libc::ENOSYS))
415        } else {
416            self.do_release(inode, handle)
417        }
418    }
419
420    fn mkdir(
421        &self,
422        ctx: &Context,
423        parent: Inode,
424        name: &CStr,
425        mode: u32,
426        umask: u32,
427    ) -> io::Result<Entry> {
428        self.validate_path_component(name)?;
429
430        let data = self.inode_map.get(parent)?;
431
432        let res = {
433            let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?;
434
435            let file = data.get_file()?;
436            // Safe because this doesn't modify any memory and we check the return value.
437            unsafe { libc::mkdirat(file.as_raw_fd(), name.as_ptr(), mode & !umask) }
438        };
439        if res < 0 {
440            return Err(io::Error::last_os_error());
441        }
442
443        self.do_lookup(parent, name)
444    }
445
446    fn rmdir(&self, _ctx: &Context, parent: Inode, name: &CStr) -> io::Result<()> {
447        self.validate_path_component(name)?;
448        self.do_unlink(parent, name, libc::AT_REMOVEDIR)
449    }
450
451    fn readdir(
452        &self,
453        _ctx: &Context,
454        inode: Inode,
455        handle: Handle,
456        size: u32,
457        offset: u64,
458        add_entry: &mut dyn FnMut(DirEntry) -> io::Result<usize>,
459    ) -> io::Result<()> {
460        if self.no_readdir.load(Ordering::Relaxed) {
461            return Ok(());
462        }
463        self.do_readdir(inode, handle, size, offset, &mut |mut dir_entry, _dir| {
464            dir_entry.ino = {
465                // Safe because do_readdir() has ensured dir_entry.name is a
466                // valid [u8] generated by CStr::to_bytes().
467                let name = unsafe {
468                    CStr::from_bytes_with_nul_unchecked(std::slice::from_raw_parts(
469                        &dir_entry.name[0],
470                        dir_entry.name.len() + 1,
471                    ))
472                };
473
474                let entry = self.do_lookup(inode, name)?;
475                let mut inodes = self.inode_map.get_map_mut();
476                self.forget_one(&mut inodes, entry.inode, 1);
477                entry.inode
478            };
479
480            add_entry(dir_entry)
481        })
482    }
483
484    fn readdirplus(
485        &self,
486        _ctx: &Context,
487        inode: Inode,
488        handle: Handle,
489        size: u32,
490        offset: u64,
491        add_entry: &mut dyn FnMut(DirEntry, Entry) -> io::Result<usize>,
492    ) -> io::Result<()> {
493        if self.no_readdir.load(Ordering::Relaxed) {
494            return Ok(());
495        }
496        self.do_readdir(inode, handle, size, offset, &mut |mut dir_entry, _dir| {
497            // Safe because do_readdir() has ensured dir_entry.name is a
498            // valid [u8] generated by CStr::to_bytes().
499            let name = unsafe {
500                CStr::from_bytes_with_nul_unchecked(std::slice::from_raw_parts(
501                    &dir_entry.name[0],
502                    dir_entry.name.len() + 1,
503                ))
504            };
505            let entry = self.do_lookup(inode, name)?;
506            let ino = entry.inode;
507            dir_entry.ino = entry.attr.st_ino;
508
509            add_entry(dir_entry, entry).inspect(|&r| {
510                // true when size is not large enough to hold entry.
511                if r == 0 {
512                    // Release the refcount acquired by self.do_lookup().
513                    let mut inodes = self.inode_map.get_map_mut();
514                    self.forget_one(&mut inodes, ino, 1);
515                }
516            })
517        })
518    }
519
520    fn open(
521        &self,
522        _ctx: &Context,
523        inode: Inode,
524        flags: u32,
525        fuse_flags: u32,
526    ) -> io::Result<(Option<Handle>, OpenOptions, Option<u32>)> {
527        if self.no_open.load(Ordering::Relaxed) {
528            info!("fuse: open is not supported.");
529            Err(enosys())
530        } else {
531            self.do_open(inode, flags, fuse_flags)
532        }
533    }
534
535    fn release(
536        &self,
537        _ctx: &Context,
538        inode: Inode,
539        _flags: u32,
540        handle: Handle,
541        _flush: bool,
542        _flock_release: bool,
543        _lock_owner: Option<u64>,
544    ) -> io::Result<()> {
545        if self.no_open.load(Ordering::Relaxed) {
546            Err(enosys())
547        } else {
548            self.do_release(inode, handle)
549        }
550    }
551
552    fn create(
553        &self,
554        ctx: &Context,
555        parent: Inode,
556        name: &CStr,
557        args: CreateIn,
558    ) -> io::Result<(Entry, Option<Handle>, OpenOptions, Option<u32>)> {
559        self.validate_path_component(name)?;
560
561        let dir = self.inode_map.get(parent)?;
562        let dir_file = dir.get_file()?;
563
564        let new_file = {
565            let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?;
566
567            let flags = self.get_writeback_open_flags(args.flags as i32);
568            Self::create_file_excl(&dir_file, name, flags, args.mode & !(args.umask & 0o777))?
569        };
570
571        let entry = self.do_lookup(parent, name)?;
572        let file = match new_file {
573            // File didn't exist, now created by create_file_excl()
574            Some(f) => f,
575            // File exists, and args.flags doesn't contain O_EXCL. Now let's open it with
576            // open_inode().
577            None => {
578                // Cap restored when _killpriv is dropped
579                let _killpriv = if self.killpriv_v2.load(Ordering::Relaxed)
580                    && (args.fuse_flags & FOPEN_IN_KILL_SUIDGID != 0)
581                {
582                    self::drop_cap_fsetid()?
583                } else {
584                    None
585                };
586
587                let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?;
588                self.open_inode(entry.inode, args.flags as i32)?
589            }
590        };
591
592        let ret_handle = if !self.no_open.load(Ordering::Relaxed) {
593            let handle = self.next_handle.fetch_add(1, Ordering::Relaxed);
594            let data = HandleData::new(entry.inode, file, args.flags);
595
596            self.handle_map.insert(handle, data);
597            Some(handle)
598        } else {
599            None
600        };
601
602        let mut opts = OpenOptions::empty();
603        match self.cfg.cache_policy {
604            CachePolicy::Never => opts |= OpenOptions::DIRECT_IO,
605            CachePolicy::Metadata => opts |= OpenOptions::DIRECT_IO,
606            CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE,
607            _ => {}
608        };
609
610        Ok((entry, ret_handle, opts, None))
611    }
612
613    fn unlink(&self, _ctx: &Context, parent: Inode, name: &CStr) -> io::Result<()> {
614        self.validate_path_component(name)?;
615        self.do_unlink(parent, name, 0)
616    }
617
618    #[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
619    fn setupmapping(
620        &self,
621        _ctx: &Context,
622        inode: Inode,
623        _handle: Handle,
624        foffset: u64,
625        len: u64,
626        flags: u64,
627        moffset: u64,
628        vu_req: &mut dyn FsCacheReqHandler,
629    ) -> io::Result<()> {
630        debug!(
631            "fuse: setupmapping ino {:?} foffset 0x{:x} len 0x{:x} flags 0x{:x} moffset 0x{:x}",
632            inode, foffset, len, flags, moffset
633        );
634
635        let open_flags = if (flags & virtio_fs::SetupmappingFlags::WRITE.bits()) != 0 {
636            libc::O_RDWR
637        } else {
638            libc::O_RDONLY
639        };
640
641        let file = self.open_inode(inode, open_flags)?;
642        (*vu_req).map(foffset, moffset, len, flags, file.as_raw_fd())
643    }
644
645    #[cfg(any(feature = "vhost-user-fs", feature = "virtiofs"))]
646    fn removemapping(
647        &self,
648        _ctx: &Context,
649        _inode: Inode,
650        requests: Vec<virtio_fs::RemovemappingOne>,
651        vu_req: &mut dyn FsCacheReqHandler,
652    ) -> io::Result<()> {
653        (*vu_req).unmap(requests)
654    }
655
656    fn read(
657        &self,
658        _ctx: &Context,
659        inode: Inode,
660        handle: Handle,
661        w: &mut dyn ZeroCopyWriter,
662        size: u32,
663        offset: u64,
664        _lock_owner: Option<u64>,
665        flags: u32,
666    ) -> io::Result<usize> {
667        let data = self.get_data(handle, inode, libc::O_RDONLY)?;
668
669        // Manually implement File::try_clone() by borrowing fd of data.file instead of dup().
670        // It's safe because the `data` variable's lifetime spans the whole function,
671        // so data.file won't be closed.
672        let f = unsafe { File::from_raw_fd(data.borrow_fd().as_raw_fd()) };
673
674        self.check_fd_flags(data.clone(), f.as_raw_fd(), flags)?;
675
676        let mut f = ManuallyDrop::new(f);
677
678        w.write_from(&mut *f, size as usize, offset)
679    }
680
681    fn write(
682        &self,
683        _ctx: &Context,
684        inode: Inode,
685        handle: Handle,
686        r: &mut dyn ZeroCopyReader,
687        size: u32,
688        offset: u64,
689        _lock_owner: Option<u64>,
690        _delayed_write: bool,
691        flags: u32,
692        fuse_flags: u32,
693    ) -> io::Result<usize> {
694        let data = self.get_data(handle, inode, libc::O_RDWR)?;
695
696        // Manually implement File::try_clone() by borrowing fd of data.file instead of dup().
697        // It's safe because the `data` variable's lifetime spans the whole function,
698        // so data.file won't be closed.
699        let f = unsafe { File::from_raw_fd(data.borrow_fd().as_raw_fd()) };
700
701        self.check_fd_flags(data.clone(), f.as_raw_fd(), flags)?;
702
703        if self.seal_size.load(Ordering::Relaxed) {
704            let st = stat_fd(&f, None)?;
705            self.seal_size_check(Opcode::Write, st.st_size as u64, offset, size as u64, 0)?;
706        }
707
708        let mut f = ManuallyDrop::new(f);
709
710        // Cap restored when _killpriv is dropped
711        let _killpriv =
712            if self.killpriv_v2.load(Ordering::Relaxed) && (fuse_flags & WRITE_KILL_PRIV != 0) {
713                self::drop_cap_fsetid()?
714            } else {
715                None
716            };
717
718        r.read_to(&mut *f, size as usize, offset)
719    }
720
721    fn getattr(
722        &self,
723        _ctx: &Context,
724        inode: Inode,
725        handle: Option<Handle>,
726    ) -> io::Result<(libc::stat64, Duration)> {
727        self.do_getattr(inode, handle)
728    }
729
730    fn setattr(
731        &self,
732        _ctx: &Context,
733        inode: Inode,
734        attr: libc::stat64,
735        handle: Option<Handle>,
736        valid: SetattrValid,
737    ) -> io::Result<(libc::stat64, Duration)> {
738        let inode_data = self.inode_map.get(inode)?;
739
740        enum Data {
741            Handle(Arc<HandleData>),
742            ProcPath(CString),
743        }
744
745        let file = inode_data.get_file()?;
746        let data = if self.no_open.load(Ordering::Relaxed) {
747            let pathname = CString::new(format!("{}", file.as_raw_fd()))
748                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
749            Data::ProcPath(pathname)
750        } else {
751            // If we have a handle then use it otherwise get a new fd from the inode.
752            if let Some(handle) = handle {
753                let hd = self.handle_map.get(handle, inode)?;
754                Data::Handle(hd)
755            } else {
756                let pathname = CString::new(format!("{}", file.as_raw_fd()))
757                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
758                Data::ProcPath(pathname)
759            }
760        };
761
762        if valid.contains(SetattrValid::SIZE) && self.seal_size.load(Ordering::Relaxed) {
763            return Err(io::Error::from_raw_os_error(libc::EPERM));
764        }
765
766        if valid.contains(SetattrValid::MODE) {
767            // Safe because this doesn't modify any memory and we check the return value.
768            let res = unsafe {
769                match data {
770                    Data::Handle(ref h) => libc::fchmod(h.borrow_fd().as_raw_fd(), attr.st_mode),
771                    Data::ProcPath(ref p) => {
772                        libc::fchmodat(self.proc_self_fd.as_raw_fd(), p.as_ptr(), attr.st_mode, 0)
773                    }
774                }
775            };
776            if res < 0 {
777                return Err(io::Error::last_os_error());
778            }
779        }
780
781        if valid.intersects(SetattrValid::UID | SetattrValid::GID) {
782            let uid = if valid.contains(SetattrValid::UID) {
783                attr.st_uid
784            } else {
785                // Cannot use -1 here because these are unsigned values.
786                u32::MAX
787            };
788            let gid = if valid.contains(SetattrValid::GID) {
789                attr.st_gid
790            } else {
791                // Cannot use -1 here because these are unsigned values.
792                u32::MAX
793            };
794
795            // Safe because this is a constant value and a valid C string.
796            let empty = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) };
797
798            // Safe because this doesn't modify any memory and we check the return value.
799            let res = unsafe {
800                libc::fchownat(
801                    file.as_raw_fd(),
802                    empty.as_ptr(),
803                    uid,
804                    gid,
805                    libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
806                )
807            };
808            if res < 0 {
809                return Err(io::Error::last_os_error());
810            }
811        }
812
813        if valid.contains(SetattrValid::SIZE) {
814            // Cap restored when _killpriv is dropped
815            let _killpriv = if self.killpriv_v2.load(Ordering::Relaxed)
816                && valid.contains(SetattrValid::KILL_SUIDGID)
817            {
818                self::drop_cap_fsetid()?
819            } else {
820                None
821            };
822
823            // Safe because this doesn't modify any memory and we check the return value.
824            let res = match data {
825                Data::Handle(ref h) => unsafe {
826                    libc::ftruncate(h.borrow_fd().as_raw_fd(), attr.st_size)
827                },
828                _ => {
829                    // There is no `ftruncateat` so we need to get a new fd and truncate it.
830                    let f = self.open_inode(inode, libc::O_NONBLOCK | libc::O_RDWR)?;
831                    unsafe { libc::ftruncate(f.as_raw_fd(), attr.st_size) }
832                }
833            };
834            if res < 0 {
835                return Err(io::Error::last_os_error());
836            }
837        }
838
839        if valid.intersects(SetattrValid::ATIME | SetattrValid::MTIME) {
840            let mut tvs = [
841                libc::timespec {
842                    tv_sec: 0,
843                    tv_nsec: libc::UTIME_OMIT,
844                },
845                libc::timespec {
846                    tv_sec: 0,
847                    tv_nsec: libc::UTIME_OMIT,
848                },
849            ];
850
851            if valid.contains(SetattrValid::ATIME_NOW) {
852                tvs[0].tv_nsec = libc::UTIME_NOW;
853            } else if valid.contains(SetattrValid::ATIME) {
854                tvs[0].tv_sec = attr.st_atime;
855                tvs[0].tv_nsec = attr.st_atime_nsec;
856            }
857
858            if valid.contains(SetattrValid::MTIME_NOW) {
859                tvs[1].tv_nsec = libc::UTIME_NOW;
860            } else if valid.contains(SetattrValid::MTIME) {
861                tvs[1].tv_sec = attr.st_mtime;
862                tvs[1].tv_nsec = attr.st_mtime_nsec;
863            }
864
865            // Safe because this doesn't modify any memory and we check the return value.
866            let res = match data {
867                Data::Handle(ref h) => unsafe {
868                    libc::futimens(h.borrow_fd().as_raw_fd(), tvs.as_ptr())
869                },
870                Data::ProcPath(ref p) => unsafe {
871                    libc::utimensat(self.proc_self_fd.as_raw_fd(), p.as_ptr(), tvs.as_ptr(), 0)
872                },
873            };
874            if res < 0 {
875                return Err(io::Error::last_os_error());
876            }
877        }
878
879        self.do_getattr(inode, handle)
880    }
881
882    fn rename(
883        &self,
884        _ctx: &Context,
885        olddir: Inode,
886        oldname: &CStr,
887        newdir: Inode,
888        newname: &CStr,
889        flags: u32,
890    ) -> io::Result<()> {
891        self.validate_path_component(oldname)?;
892        self.validate_path_component(newname)?;
893
894        let old_inode = self.inode_map.get(olddir)?;
895        let new_inode = self.inode_map.get(newdir)?;
896        let old_file = old_inode.get_file()?;
897        let new_file = new_inode.get_file()?;
898
899        // Safe because this doesn't modify any memory and we check the return value.
900        // TODO: Switch to libc::renameat2 once https://github.com/rust-lang/libc/pull/1508 lands
901        // and we have glibc 2.28.
902        let res = unsafe {
903            libc::syscall(
904                libc::SYS_renameat2,
905                old_file.as_raw_fd(),
906                oldname.as_ptr(),
907                new_file.as_raw_fd(),
908                newname.as_ptr(),
909                flags,
910            )
911        };
912        if res == 0 {
913            Ok(())
914        } else {
915            Err(io::Error::last_os_error())
916        }
917    }
918
919    fn mknod(
920        &self,
921        ctx: &Context,
922        parent: Inode,
923        name: &CStr,
924        mode: u32,
925        rdev: u32,
926        umask: u32,
927    ) -> io::Result<Entry> {
928        self.validate_path_component(name)?;
929
930        let data = self.inode_map.get(parent)?;
931        let file = data.get_file()?;
932
933        let res = {
934            let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?;
935
936            // Safe because this doesn't modify any memory and we check the return value.
937            unsafe {
938                libc::mknodat(
939                    file.as_raw_fd(),
940                    name.as_ptr(),
941                    (mode & !umask) as libc::mode_t,
942                    u64::from(rdev),
943                )
944            }
945        };
946        if res < 0 {
947            Err(io::Error::last_os_error())
948        } else {
949            self.do_lookup(parent, name)
950        }
951    }
952
953    fn link(
954        &self,
955        _ctx: &Context,
956        inode: Inode,
957        newparent: Inode,
958        newname: &CStr,
959    ) -> io::Result<Entry> {
960        self.validate_path_component(newname)?;
961
962        let data = self.inode_map.get(inode)?;
963        let new_inode = self.inode_map.get(newparent)?;
964        let file = data.get_file()?;
965        let new_file = new_inode.get_file()?;
966
967        // Safe because this is a constant value and a valid C string.
968        let empty = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) };
969
970        // Safe because this doesn't modify any memory and we check the return value.
971        let res = unsafe {
972            libc::linkat(
973                file.as_raw_fd(),
974                empty.as_ptr(),
975                new_file.as_raw_fd(),
976                newname.as_ptr(),
977                libc::AT_EMPTY_PATH,
978            )
979        };
980        if res == 0 {
981            self.do_lookup(newparent, newname)
982        } else {
983            Err(io::Error::last_os_error())
984        }
985    }
986
987    fn symlink(
988        &self,
989        ctx: &Context,
990        linkname: &CStr,
991        parent: Inode,
992        name: &CStr,
993    ) -> io::Result<Entry> {
994        self.validate_path_component(name)?;
995
996        let data = self.inode_map.get(parent)?;
997
998        let res = {
999            let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?;
1000
1001            let file = data.get_file()?;
1002            // Safe because this doesn't modify any memory and we check the return value.
1003            unsafe { libc::symlinkat(linkname.as_ptr(), file.as_raw_fd(), name.as_ptr()) }
1004        };
1005        if res == 0 {
1006            self.do_lookup(parent, name)
1007        } else {
1008            Err(io::Error::last_os_error())
1009        }
1010    }
1011
1012    fn readlink(&self, _ctx: &Context, inode: Inode) -> io::Result<Vec<u8>> {
1013        // Safe because this is a constant value and a valid C string.
1014        let empty = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) };
1015        let mut buf = Vec::<u8>::with_capacity(libc::PATH_MAX as usize);
1016        let data = self.inode_map.get(inode)?;
1017        let file = data.get_file()?;
1018
1019        // Safe because this will only modify the contents of `buf` and we check the return value.
1020        let res = unsafe {
1021            libc::readlinkat(
1022                file.as_raw_fd(),
1023                empty.as_ptr(),
1024                buf.as_mut_ptr() as *mut libc::c_char,
1025                libc::PATH_MAX as usize,
1026            )
1027        };
1028        if res < 0 {
1029            return Err(io::Error::last_os_error());
1030        }
1031
1032        // Safe because we trust the value returned by kernel.
1033        unsafe { buf.set_len(res as usize) };
1034
1035        Ok(buf)
1036    }
1037
1038    fn flush(
1039        &self,
1040        _ctx: &Context,
1041        inode: Inode,
1042        handle: Handle,
1043        _lock_owner: u64,
1044    ) -> io::Result<()> {
1045        if self.no_open.load(Ordering::Relaxed) {
1046            return Err(enosys());
1047        }
1048
1049        let data = self.handle_map.get(handle, inode)?;
1050
1051        // Since this method is called whenever an fd is closed in the client, we can emulate that
1052        // behavior by doing the same thing (dup-ing the fd and then immediately closing it). Safe
1053        // because this doesn't modify any memory and we check the return values.
1054        unsafe {
1055            let newfd = libc::dup(data.borrow_fd().as_raw_fd());
1056            if newfd < 0 {
1057                return Err(io::Error::last_os_error());
1058            }
1059
1060            if libc::close(newfd) < 0 {
1061                Err(io::Error::last_os_error())
1062            } else {
1063                Ok(())
1064            }
1065        }
1066    }
1067
1068    fn fsync(
1069        &self,
1070        _ctx: &Context,
1071        inode: Inode,
1072        datasync: bool,
1073        handle: Handle,
1074    ) -> io::Result<()> {
1075        let data = self.get_data(handle, inode, libc::O_RDONLY)?;
1076        let fd = data.borrow_fd();
1077        sync_fd(&fd, datasync)
1078    }
1079
1080    fn fsyncdir(
1081        &self,
1082        _ctx: &Context,
1083        inode: Inode,
1084        datasync: bool,
1085        handle: Handle,
1086    ) -> io::Result<()> {
1087        let data = self.get_dirdata(handle, inode, libc::O_RDONLY)?;
1088        let fd = data.borrow_fd();
1089        sync_fd(&fd, datasync)
1090    }
1091
1092    fn access(&self, ctx: &Context, inode: Inode, mask: u32) -> io::Result<()> {
1093        let data = self.inode_map.get(inode)?;
1094        let st = stat_fd(&data.get_file()?, None)?;
1095        let mode = mask as i32 & (libc::R_OK | libc::W_OK | libc::X_OK);
1096
1097        if mode == libc::F_OK {
1098            // The file exists since we were able to call `stat(2)` on it.
1099            return Ok(());
1100        }
1101
1102        if (mode & libc::R_OK) != 0
1103            && ctx.uid != 0
1104            && (st.st_uid != ctx.uid || st.st_mode & 0o400 == 0)
1105            && (st.st_gid != ctx.gid || st.st_mode & 0o040 == 0)
1106            && st.st_mode & 0o004 == 0
1107        {
1108            return Err(io::Error::from_raw_os_error(libc::EACCES));
1109        }
1110
1111        if (mode & libc::W_OK) != 0
1112            && ctx.uid != 0
1113            && (st.st_uid != ctx.uid || st.st_mode & 0o200 == 0)
1114            && (st.st_gid != ctx.gid || st.st_mode & 0o020 == 0)
1115            && st.st_mode & 0o002 == 0
1116        {
1117            return Err(io::Error::from_raw_os_error(libc::EACCES));
1118        }
1119
1120        // root can only execute something if it is executable by one of the owner, the group, or
1121        // everyone.
1122        if (mode & libc::X_OK) != 0
1123            && (ctx.uid != 0 || st.st_mode & 0o111 == 0)
1124            && (st.st_uid != ctx.uid || st.st_mode & 0o100 == 0)
1125            && (st.st_gid != ctx.gid || st.st_mode & 0o010 == 0)
1126            && st.st_mode & 0o001 == 0
1127        {
1128            return Err(io::Error::from_raw_os_error(libc::EACCES));
1129        }
1130
1131        Ok(())
1132    }
1133
1134    fn setxattr(
1135        &self,
1136        _ctx: &Context,
1137        inode: Inode,
1138        name: &CStr,
1139        value: &[u8],
1140        flags: u32,
1141    ) -> io::Result<()> {
1142        if !self.cfg.xattr {
1143            return Err(enosys());
1144        }
1145
1146        let data = self.inode_map.get(inode)?;
1147        let file = data.get_file()?;
1148        let pathname = CString::new(format!("/proc/self/fd/{}", file.as_raw_fd()))
1149            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1150
1151        // The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
1152        // need to use the {set,get,remove,list}xattr variants.
1153        // Safe because this doesn't modify any memory and we check the return value.
1154        let res = unsafe {
1155            libc::setxattr(
1156                pathname.as_ptr(),
1157                name.as_ptr(),
1158                value.as_ptr() as *const libc::c_void,
1159                value.len(),
1160                flags as libc::c_int,
1161            )
1162        };
1163        if res == 0 {
1164            Ok(())
1165        } else {
1166            Err(io::Error::last_os_error())
1167        }
1168    }
1169
1170    fn getxattr(
1171        &self,
1172        _ctx: &Context,
1173        inode: Inode,
1174        name: &CStr,
1175        size: u32,
1176    ) -> io::Result<GetxattrReply> {
1177        if !self.cfg.xattr {
1178            return Err(enosys());
1179        }
1180
1181        let data = self.inode_map.get(inode)?;
1182        let file = data.get_file()?;
1183        let mut buf = Vec::<u8>::with_capacity(size as usize);
1184        let pathname = CString::new(format!("/proc/self/fd/{}", file.as_raw_fd(),))
1185            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1186
1187        // The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
1188        // need to use the {set,get,remove,list}xattr variants.
1189        // Safe because this will only modify the contents of `buf`.
1190        let res = unsafe {
1191            libc::getxattr(
1192                pathname.as_ptr(),
1193                name.as_ptr(),
1194                buf.as_mut_ptr() as *mut libc::c_void,
1195                size as libc::size_t,
1196            )
1197        };
1198        if res < 0 {
1199            return Err(io::Error::last_os_error());
1200        }
1201
1202        if size == 0 {
1203            Ok(GetxattrReply::Count(res as u32))
1204        } else {
1205            // Safe because we trust the value returned by kernel.
1206            unsafe { buf.set_len(res as usize) };
1207            Ok(GetxattrReply::Value(buf))
1208        }
1209    }
1210
1211    fn listxattr(&self, _ctx: &Context, inode: Inode, size: u32) -> io::Result<ListxattrReply> {
1212        if !self.cfg.xattr {
1213            return Err(enosys());
1214        }
1215
1216        let data = self.inode_map.get(inode)?;
1217        let file = data.get_file()?;
1218        let mut buf = Vec::<u8>::with_capacity(size as usize);
1219        let pathname = CString::new(format!("/proc/self/fd/{}", file.as_raw_fd()))
1220            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1221
1222        // The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
1223        // need to use the {set,get,remove,list}xattr variants.
1224        // Safe because this will only modify the contents of `buf`.
1225        let res = unsafe {
1226            libc::listxattr(
1227                pathname.as_ptr(),
1228                buf.as_mut_ptr() as *mut libc::c_char,
1229                size as libc::size_t,
1230            )
1231        };
1232        if res < 0 {
1233            return Err(io::Error::last_os_error());
1234        }
1235
1236        if size == 0 {
1237            Ok(ListxattrReply::Count(res as u32))
1238        } else {
1239            // Safe because we trust the value returned by kernel.
1240            unsafe { buf.set_len(res as usize) };
1241            Ok(ListxattrReply::Names(buf))
1242        }
1243    }
1244
1245    fn removexattr(&self, _ctx: &Context, inode: Inode, name: &CStr) -> io::Result<()> {
1246        if !self.cfg.xattr {
1247            return Err(enosys());
1248        }
1249
1250        let data = self.inode_map.get(inode)?;
1251        let file = data.get_file()?;
1252        let pathname = CString::new(format!("/proc/self/fd/{}", file.as_raw_fd()))
1253            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1254
1255        // The f{set,get,remove,list}xattr functions don't work on an fd opened with `O_PATH` so we
1256        // need to use the {set,get,remove,list}xattr variants.
1257        // Safe because this doesn't modify any memory and we check the return value.
1258        let res = unsafe { libc::removexattr(pathname.as_ptr(), name.as_ptr()) };
1259        if res == 0 {
1260            Ok(())
1261        } else {
1262            Err(io::Error::last_os_error())
1263        }
1264    }
1265
1266    fn fallocate(
1267        &self,
1268        _ctx: &Context,
1269        inode: Inode,
1270        handle: Handle,
1271        mode: u32,
1272        offset: u64,
1273        length: u64,
1274    ) -> io::Result<()> {
1275        // Let the Arc<HandleData> in scope, otherwise fd may get invalid.
1276        let data = self.get_data(handle, inode, libc::O_RDWR)?;
1277        let fd = data.borrow_fd();
1278
1279        if self.seal_size.load(Ordering::Relaxed) {
1280            let st = stat_fd(&fd, None)?;
1281            self.seal_size_check(
1282                Opcode::Fallocate,
1283                st.st_size as u64,
1284                offset,
1285                length,
1286                mode as i32,
1287            )?;
1288        }
1289
1290        // Safe because this doesn't modify any memory and we check the return value.
1291        let res = unsafe {
1292            libc::fallocate64(
1293                fd.as_raw_fd(),
1294                mode as libc::c_int,
1295                offset as libc::off64_t,
1296                length as libc::off64_t,
1297            )
1298        };
1299        if res == 0 {
1300            Ok(())
1301        } else {
1302            Err(io::Error::last_os_error())
1303        }
1304    }
1305
1306    fn lseek(
1307        &self,
1308        _ctx: &Context,
1309        inode: Inode,
1310        handle: Handle,
1311        offset: u64,
1312        whence: u32,
1313    ) -> io::Result<u64> {
1314        // Let the Arc<HandleData> in scope, otherwise fd may get invalid.
1315        let data = self.handle_map.get(handle, inode)?;
1316
1317        // Acquire the lock to get exclusive access, otherwise it may break do_readdir().
1318        let (_guard, file) = data.get_file_mut();
1319
1320        // Safe because this doesn't modify any memory and we check the return value.
1321        let res = unsafe {
1322            libc::lseek(
1323                file.as_raw_fd(),
1324                offset as libc::off64_t,
1325                whence as libc::c_int,
1326            )
1327        };
1328        if res < 0 {
1329            Err(io::Error::last_os_error())
1330        } else {
1331            Ok(res as u64)
1332        }
1333    }
1334}
1335
1336#[cfg(test)]
1337mod tests {
1338    use std::convert::TryInto;
1339
1340    use super::*;
1341    use crate::abi::fuse_abi::ROOT_ID;
1342    use std::path::Path;
1343    use vmm_sys_util::{tempdir::TempDir, tempfile::TempFile};
1344
1345    fn prepare_fs_tmpdir() -> (PassthroughFs, TempDir) {
1346        let source = TempDir::new().expect("Cannot create temporary directory.");
1347        let fs_cfg = Config {
1348            writeback: true,
1349            do_import: true,
1350            no_open: false,
1351            no_readdir: false,
1352            inode_file_handles: true,
1353            xattr: true,
1354            killpriv_v2: true, //enable killpriv_v2
1355            root_dir: source
1356                .as_path()
1357                .to_str()
1358                .expect("source path to string")
1359                .to_string(),
1360            ..Default::default()
1361        };
1362        let fs = PassthroughFs::<()>::new(fs_cfg).unwrap();
1363        fs.import().unwrap();
1364
1365        // enable all fuse options
1366        let opt = FsOptions::all();
1367        fs.init(opt).unwrap();
1368
1369        (fs, source)
1370    }
1371
1372    fn prepare_context() -> Context {
1373        Context {
1374            uid: unsafe { libc::getuid() },
1375            gid: unsafe { libc::getgid() },
1376            pid: unsafe { libc::getpid() },
1377            ..Default::default()
1378        }
1379    }
1380
1381    fn create_file_with_sugid(ctx: &Context, fs: &PassthroughFs<()>) -> (Entry, Handle) {
1382        let fname = CString::new("testfile").unwrap();
1383        let args = CreateIn {
1384            flags: libc::O_WRONLY as u32,
1385            mode: 0o6777,
1386            umask: 0,
1387            fuse_flags: 0,
1388        };
1389        let (test_entry, handle, _, _) = fs.create(&ctx, ROOT_ID, &fname, args).unwrap();
1390
1391        (test_entry, handle.unwrap())
1392    }
1393
1394    #[test]
1395    fn test_dir_operations() {
1396        let (fs, _source) = prepare_fs_tmpdir();
1397        let ctx = prepare_context();
1398
1399        let dir = CString::new("testdir").unwrap();
1400        fs.mkdir(&ctx, ROOT_ID, &dir, 0o755, 0).unwrap();
1401
1402        let (handle, _) = fs.opendir(&ctx, ROOT_ID, libc::O_RDONLY as u32).unwrap();
1403
1404        assert!(fs
1405            .readdir(&ctx, ROOT_ID, handle.unwrap(), 10, 0, &mut |_| Ok(1))
1406            .is_err());
1407
1408        assert!(fs
1409            .readdirplus(&ctx, ROOT_ID, handle.unwrap(), 10, 0, &mut |_, _| Ok(1))
1410            .is_err());
1411
1412        assert!(fs.fsyncdir(&ctx, ROOT_ID, true, handle.unwrap()).is_ok());
1413
1414        assert!(fs.releasedir(&ctx, ROOT_ID, 0, handle.unwrap()).is_ok());
1415        assert!(fs.rmdir(&ctx, ROOT_ID, &dir).is_ok());
1416    }
1417
1418    #[test]
1419    fn test_link_rename() {
1420        let (fs, _source) = prepare_fs_tmpdir();
1421        let ctx = prepare_context();
1422
1423        let fname = CString::new("testfile").unwrap();
1424        let args = CreateIn::default();
1425        let (test_entry, _, _, _) = fs.create(&ctx, ROOT_ID, &fname, args).unwrap();
1426
1427        let link_name = CString::new("testlink").unwrap();
1428        fs.link(&ctx, test_entry.inode, ROOT_ID, &link_name)
1429            .unwrap();
1430
1431        let new_name = CString::new("newlink").unwrap();
1432        fs.rename(&ctx, ROOT_ID, &link_name, ROOT_ID, &new_name, 0)
1433            .unwrap();
1434
1435        let link_entry = fs.lookup(&ctx, ROOT_ID, &new_name).unwrap();
1436
1437        assert_eq!(link_entry.inode, test_entry.inode);
1438    }
1439
1440    #[test]
1441    fn test_unlink_delete_file() {
1442        let (fs, source) = prepare_fs_tmpdir();
1443        let child_path = TempFile::new_in(source.as_path()).expect("Cannot create temporary file.");
1444
1445        let ctx = prepare_context();
1446
1447        let child_str = child_path
1448            .as_path()
1449            .file_name()
1450            .unwrap()
1451            .to_str()
1452            .expect("path to string");
1453        let child = CString::new(child_str).unwrap();
1454
1455        fs.unlink(&ctx, ROOT_ID, &child).unwrap();
1456
1457        assert!(!Path::new(child_str).exists())
1458    }
1459
1460    #[test]
1461    // test virtiofs CVE-2020-35517, should not open device file
1462    fn test_mknod_and_open_device() {
1463        let (fs, _source) = prepare_fs_tmpdir();
1464
1465        let ctx = prepare_context();
1466
1467        let device_name = CString::new("test_device").unwrap();
1468        let mode = libc::S_IFBLK;
1469        let mask = 0o777;
1470        let device_no = libc::makedev(0, 103) as u32;
1471
1472        let device_entry = fs
1473            .mknod(&ctx, ROOT_ID, &device_name, mode, device_no, mask)
1474            .unwrap();
1475        let (d_st, _) = fs.getattr(&ctx, device_entry.inode, None).unwrap();
1476
1477        assert_eq!(d_st.st_mode & libc::S_IFMT, libc::S_IFBLK);
1478        assert_eq!(d_st.st_rdev as u32, device_no);
1479
1480        // open device should fail because of is_safe_inode check
1481        let err = fs
1482            .open(&ctx, device_entry.inode, libc::O_RDWR as u32, 0)
1483            .is_err();
1484        assert_eq!(err, true);
1485    }
1486
1487    #[test]
1488    fn test_create_access() {
1489        let (fs, _source) = prepare_fs_tmpdir();
1490        let ctx = prepare_context();
1491
1492        let fname = CString::new("testfile").unwrap();
1493        let args = CreateIn {
1494            flags: libc::O_WRONLY as u32,
1495            mode: 0644,
1496            umask: 0,
1497            fuse_flags: 0,
1498        };
1499        let (test_entry, _, _, _) = fs.create(&ctx, ROOT_ID, &fname, args).unwrap();
1500
1501        let mask = (libc::R_OK | libc::W_OK) as u32;
1502        assert_eq!(fs.access(&ctx, test_entry.inode, mask).is_ok(), true);
1503        let mask = (libc::R_OK | libc::W_OK | libc::X_OK) as u32;
1504        assert_eq!(fs.access(&ctx, test_entry.inode, mask).is_ok(), false);
1505        assert!(fs
1506            .release(&ctx, test_entry.inode, 0, 0, false, false, Some(0))
1507            .is_err());
1508    }
1509
1510    #[test]
1511    fn test_symlink_escape_root() {
1512        let (fs, _source) = prepare_fs_tmpdir();
1513        let child_path =
1514            TempFile::new_in(_source.as_path()).expect("Cannot create temporary file.");
1515        let ctx = prepare_context();
1516
1517        let eval_sym_dest = CString::new("/root").unwrap();
1518        let eval_sym_name = CString::new("eval_sym").unwrap();
1519        let normal_sym_dest = CString::new(child_path.as_path().to_str().unwrap()).unwrap();
1520        let normal_sym_name = CString::new("normal_sym").unwrap();
1521
1522        let normal_sym_entry = fs
1523            .symlink(&ctx, &normal_sym_dest, ROOT_ID, &normal_sym_name)
1524            .unwrap();
1525
1526        let eval_sym_entry = fs
1527            .symlink(&ctx, &eval_sym_dest, ROOT_ID, &eval_sym_name)
1528            .unwrap();
1529
1530        let normal_buf = fs.readlink(&ctx, normal_sym_entry.inode).unwrap();
1531        let eval_buf = fs.readlink(&ctx, eval_sym_entry.inode).unwrap();
1532        let normal_dest_name = CString::new(String::from_utf8(normal_buf).unwrap()).unwrap();
1533        let eval_dest_name = CString::new(String::from_utf8(eval_buf).unwrap()).unwrap();
1534
1535        assert_eq!(normal_dest_name, normal_sym_dest);
1536        assert_eq!(eval_dest_name, eval_sym_dest);
1537    }
1538
1539    #[test]
1540    fn test_setattr_and_drop_priv() {
1541        let (fs, _source) = prepare_fs_tmpdir();
1542        let ctx = prepare_context();
1543
1544        let (test_entry, _) = create_file_with_sugid(&ctx, &fs);
1545
1546        let (mut old_att, _) = fs.getattr(&ctx, test_entry.inode, None).unwrap();
1547
1548        old_att.st_size = 4096;
1549        let mut valid = SetattrValid::SIZE | SetattrValid::KILL_SUIDGID;
1550        let (attr_not_drop, _) = fs
1551            .setattr(&ctx, test_entry.inode, old_att, None, valid)
1552            .unwrap();
1553        // during file size change,
1554        // suid/sgid should be dropped because of killpriv_v2
1555        assert_eq!(attr_not_drop.st_mode, 0o100777);
1556
1557        old_att.st_size = 0;
1558        old_att.st_uid = 1;
1559        old_att.st_gid = 1;
1560        old_att.st_atime = 0;
1561        old_att.st_mtime = 0;
1562        valid = SetattrValid::SIZE
1563            | SetattrValid::ATIME
1564            | SetattrValid::MTIME
1565            | SetattrValid::UID
1566            | SetattrValid::GID;
1567
1568        let (attr, _) = fs
1569            .setattr(&ctx, test_entry.inode, old_att, None, valid)
1570            .unwrap();
1571        // suid/sgid is dropped because chmod is called
1572        assert_eq!(attr.st_mode, 0o100777);
1573        assert_eq!(attr.st_size, 0);
1574    }
1575
1576    #[test]
1577    // fallocate missing killpriv logic, should be fixed
1578    fn test_fallocate_drop_priv() {
1579        let (fs, _source) = prepare_fs_tmpdir();
1580        let ctx = prepare_context();
1581
1582        let (test_entry, handle) = create_file_with_sugid(&ctx, &fs);
1583
1584        let offset = fs
1585            .lseek(
1586                &ctx,
1587                test_entry.inode,
1588                handle,
1589                4096,
1590                libc::SEEK_SET.try_into().unwrap(),
1591            )
1592            .unwrap();
1593        fs.fallocate(&ctx, test_entry.inode, handle, 0, offset, 4096)
1594            .unwrap();
1595
1596        let (att, _) = fs.getattr(&ctx, test_entry.inode, None).unwrap();
1597
1598        assert_eq!(att.st_size, 8192);
1599        // suid/sgid not dropped
1600        assert_eq!(att.st_mode, 0o106777);
1601    }
1602
1603    #[test]
1604    fn test_fsync_flush() {
1605        let (fs, _source) = prepare_fs_tmpdir();
1606        let ctx = prepare_context();
1607
1608        let (test_entry, handle) = create_file_with_sugid(&ctx, &fs);
1609
1610        assert!(fs.fsync(&ctx, test_entry.inode, false, handle).is_ok());
1611        assert!(fs.flush(&ctx, test_entry.inode, handle, 0).is_ok());
1612    }
1613
1614    #[test]
1615    fn test_statfs() {
1616        let (fs, _source) = prepare_fs_tmpdir();
1617        let ctx = prepare_context();
1618
1619        let statfs = fs.statfs(&ctx, ROOT_ID).unwrap();
1620        assert_eq!(statfs.f_namemax, 255);
1621    }
1622
1623    #[test]
1624    fn test_fsync_dir() {
1625        let (fs, _source) = prepare_fs_tmpdir();
1626        let ctx = prepare_context();
1627        fs.no_opendir.store(true, Ordering::Relaxed);
1628
1629        assert!(fs.fsyncdir(&ctx, ROOT_ID, false, 0).is_ok());
1630    }
1631}