1use 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 #[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 let (guard, dir) = data.get_file_mut();
81
82 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 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 unsafe { buf.set_len(res as usize) };
105
106 mem::drop(guard);
108 }
109
110 let mut rem = &buf[..];
111 let orig_rem_len = rem.len();
112 while !rem.is_empty() {
113 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 Ok(1)
136 } else {
137 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 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 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 let st = if !self.no_open.load(Ordering::Relaxed) && handle.is_some() {
241 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 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 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 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 match unsafe { libc::fstatvfs64(file.as_raw_fd(), out.as_mut_ptr()) } {
362 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 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 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 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 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 if r == 0 {
512 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 Some(f) => f,
575 None => {
578 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 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 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 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 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 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 u32::MAX
787 };
788 let gid = if valid.contains(SetattrValid::GID) {
789 attr.st_gid
790 } else {
791 u32::MAX
793 };
794
795 let empty = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) };
797
798 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 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 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 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 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 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 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 let empty = unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) };
969
970 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 data = self.handle_map.get(handle, inode)?;
1316
1317 let (_guard, file) = data.get_file_mut();
1319
1320 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, 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 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 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 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 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 assert_eq!(attr.st_mode, 0o100777);
1573 assert_eq!(attr.st_size, 0);
1574 }
1575
1576 #[test]
1577 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 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}