1use crate::fs::fuchsia::RemoteUnixDomainSocket;
6use crate::fs::fuchsia::remote_volume::RemoteVolume;
7use crate::fs::fuchsia::sync_file::{SyncFence, SyncFile, SyncPoint, Timeline};
8use crate::mm::memory::MemoryObject;
9use crate::mm::{ProtectionFlags, VMEX_RESOURCE};
10use crate::security;
11use crate::task::{CurrentTask, FullCredentials, Kernel};
12use crate::vfs::buffers::{InputBuffer, OutputBuffer, with_iovec_segments};
13use crate::vfs::fsverity::FsVerityState;
14use crate::vfs::socket::{Socket, SocketFile, ZxioBackedSocket};
15use crate::vfs::{
16 Anon, AppendLockGuard, CacheConfig, CacheMode, DEFAULT_BYTES_PER_BLOCK, DirectoryEntryType,
17 DirentSink, FallocMode, FileHandle, FileObject, FileOps, FileSystem, FileSystemHandle,
18 FileSystemOps, FileSystemOptions, FsNode, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString,
19 SeekTarget, SymlinkTarget, XattrOp, XattrStorage, default_ioctl, default_seek,
20 fileops_impl_directory, fileops_impl_nonseekable, fileops_impl_noop_sync,
21 fileops_impl_seekable, fs_node_impl_not_dir, fs_node_impl_symlink, fs_node_impl_xattr_delegate,
22};
23use bstr::ByteSlice;
24use fidl::AsHandleRef;
25use fidl::endpoints::DiscoverableProtocolMarker as _;
26use fuchsia_runtime::UtcInstant;
27use linux_uapi::SYNC_IOC_MAGIC;
28use once_cell::sync::OnceCell;
29use starnix_crypt::EncryptionKeyId;
30use starnix_logging::{CATEGORY_STARNIX_MM, impossible_error, log_warn, trace_duration};
31use starnix_sync::{
32 FileOpsCore, LockEqualOrBefore, Locked, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
33 Unlocked,
34};
35use starnix_syscalls::{SyscallArg, SyscallResult};
36use starnix_types::vfs::default_statfs;
37use starnix_uapi::auth::FsCred;
38use starnix_uapi::device_type::DeviceType;
39use starnix_uapi::errors::Errno;
40use starnix_uapi::file_mode::FileMode;
41use starnix_uapi::mount_flags::MountFlags;
42use starnix_uapi::open_flags::OpenFlags;
43use starnix_uapi::{
44 __kernel_fsid_t, errno, error, from_status_like_fdio, fsverity_descriptor, ino_t, off_t, statfs,
45};
46use std::mem::MaybeUninit;
47use std::sync::Arc;
48use syncio::zxio::{
49 ZXIO_NODE_PROTOCOL_DIRECTORY, ZXIO_NODE_PROTOCOL_FILE, ZXIO_NODE_PROTOCOL_SYMLINK,
50 ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET, ZXIO_OBJECT_TYPE_DIR, ZXIO_OBJECT_TYPE_FILE,
51 ZXIO_OBJECT_TYPE_NONE, ZXIO_OBJECT_TYPE_PACKET_SOCKET, ZXIO_OBJECT_TYPE_RAW_SOCKET,
52 ZXIO_OBJECT_TYPE_STREAM_SOCKET, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET, zxio_node_attr,
53};
54use syncio::{
55 AllocateMode, DirentIterator, SelinuxContextAttr, XattrSetMode, ZXIO_ROOT_HASH_LENGTH, Zxio,
56 ZxioDirent, ZxioOpenOptions, zxio_fsverity_descriptor_t, zxio_node_attr_has_t,
57 zxio_node_attributes_t,
58};
59use zx::{Counter, HandleBased};
60use {
61 fidl_fuchsia_io as fio, fidl_fuchsia_starnix_binder as fbinder,
62 fidl_fuchsia_unknown as funknown,
63};
64
65pub fn new_remote_fs(
66 locked: &mut Locked<Unlocked>,
67 current_task: &CurrentTask,
68 options: FileSystemOptions,
69) -> Result<FileSystemHandle, Errno> {
70 let kernel = current_task.kernel();
71 let requested_path = std::str::from_utf8(&options.source)
74 .map_err(|_| errno!(EINVAL, "source path is not utf8"))?;
75 let mut create_flags =
76 fio::PERM_READABLE | fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY;
77 if !options.flags.contains(MountFlags::RDONLY) {
78 create_flags |= fio::PERM_WRITABLE;
79 }
80 let (root_proxy, subdir) = kernel.open_ns_dir(requested_path, create_flags)?;
81
82 let subdir = if subdir.is_empty() { ".".to_string() } else { subdir };
83 let mut open_rights = fio::PERM_READABLE;
84 if !options.flags.contains(MountFlags::RDONLY) {
85 open_rights |= fio::PERM_WRITABLE;
86 }
87 let mut subdir_options = options;
88 subdir_options.source = subdir.into();
89 create_remotefs_filesystem(locked, kernel, &root_proxy, subdir_options, open_rights)
90}
91
92pub fn create_remotefs_filesystem<L>(
95 locked: &mut Locked<L>,
96 kernel: &Kernel,
97 root: &fio::DirectorySynchronousProxy,
98 options: FileSystemOptions,
99 rights: fio::Flags,
100) -> Result<FileSystemHandle, Errno>
101where
102 L: LockEqualOrBefore<FileOpsCore>,
103{
104 let root = syncio::directory_open_directory_async(
105 root,
106 std::str::from_utf8(&options.source)
107 .map_err(|_| errno!(EINVAL, "source path is not utf8"))?,
108 rights,
109 )
110 .map_err(|e| errno!(EIO, format!("Failed to open root: {e}")))?;
111 RemoteFs::new_fs(locked, kernel, root.into_channel(), options, rights)
112}
113
114pub struct RemoteFs {
115 use_remote_ids: bool,
121
122 root_proxy: fio::DirectorySynchronousProxy,
123}
124
125impl RemoteFs {
126 fn from_fs(fs: &FileSystem) -> &RemoteFs {
133 if let Some(remote_vol) = fs.downcast_ops::<RemoteVolume>() {
134 remote_vol.remotefs()
135 } else {
136 fs.downcast_ops::<RemoteFs>().unwrap()
137 }
138 }
139}
140
141const REMOTE_FS_MAGIC: u32 = u32::from_be_bytes(*b"f.io");
142const SYNC_IOC_FILE_INFO: u8 = 4;
143const SYNC_IOC_MERGE: u8 = 3;
144
145impl FileSystemOps for RemoteFs {
146 fn statfs(
147 &self,
148 _locked: &mut Locked<FileOpsCore>,
149 _fs: &FileSystem,
150 _current_task: &CurrentTask,
151 ) -> Result<statfs, Errno> {
152 let (status, info) = self
153 .root_proxy
154 .query_filesystem(zx::MonotonicInstant::INFINITE)
155 .map_err(|_| errno!(EIO))?;
156 if status == 0 {
158 if let Some(info) = info {
159 let (total_blocks, free_blocks) = if info.block_size > 0 {
160 (
161 (info.total_bytes / u64::from(info.block_size))
162 .try_into()
163 .unwrap_or(i64::MAX),
164 ((info.total_bytes.saturating_sub(info.used_bytes))
165 / u64::from(info.block_size))
166 .try_into()
167 .unwrap_or(i64::MAX),
168 )
169 } else {
170 (0, 0)
171 };
172
173 let fsid = __kernel_fsid_t {
174 val: [
175 (info.fs_id & 0xffffffff) as i32,
176 ((info.fs_id >> 32) & 0xffffffff) as i32,
177 ],
178 };
179
180 return Ok(statfs {
181 f_type: info.fs_type as i64,
182 f_bsize: info.block_size.into(),
183 f_blocks: total_blocks,
184 f_bfree: free_blocks,
185 f_bavail: free_blocks,
186 f_files: info.total_nodes.try_into().unwrap_or(i64::MAX),
187 f_ffree: (info.total_nodes.saturating_sub(info.used_nodes))
188 .try_into()
189 .unwrap_or(i64::MAX),
190 f_fsid: fsid,
191 f_namelen: info.max_filename_size.try_into().unwrap_or(0),
192 f_frsize: info.block_size.into(),
193 ..statfs::default()
194 });
195 }
196 }
197 Ok(default_statfs(REMOTE_FS_MAGIC))
198 }
199
200 fn name(&self) -> &'static FsStr {
201 "remotefs".into()
202 }
203
204 fn uses_external_node_ids(&self) -> bool {
205 self.use_remote_ids
206 }
207
208 fn rename(
209 &self,
210 _locked: &mut Locked<FileOpsCore>,
211 _fs: &FileSystem,
212 current_task: &CurrentTask,
213 old_parent: &FsNodeHandle,
214 old_name: &FsStr,
215 new_parent: &FsNodeHandle,
216 new_name: &FsStr,
217 _renamed: &FsNodeHandle,
218 _replaced: Option<&FsNodeHandle>,
219 ) -> Result<(), Errno> {
220 old_parent.fail_if_locked(current_task)?;
222 new_parent.fail_if_locked(current_task)?;
223
224 let Some(old_parent) = old_parent.downcast_ops::<RemoteNode>() else {
225 return error!(EXDEV);
226 };
227 let Some(new_parent) = new_parent.downcast_ops::<RemoteNode>() else {
228 return error!(EXDEV);
229 };
230 old_parent
231 .zxio
232 .rename(get_name_str(old_name)?, &new_parent.zxio, get_name_str(new_name)?)
233 .map_err(|status| from_status_like_fdio!(status))
234 }
235
236 fn manages_timestamps(&self) -> bool {
237 true
238 }
239}
240
241impl RemoteFs {
242 pub fn new(root: zx::Channel, server_end: zx::Channel) -> Result<RemoteFs, Errno> {
243 let root_proxy = fio::DirectorySynchronousProxy::new(root);
246 root_proxy
247 .open(
248 ".",
249 fio::Flags::PROTOCOL_DIRECTORY
250 | fio::PERM_READABLE
251 | fio::Flags::PERM_INHERIT_WRITE
252 | fio::Flags::PERM_INHERIT_EXECUTE
253 | fio::Flags::FLAG_SEND_REPRESENTATION,
254 &fio::Options {
255 attributes: Some(fio::NodeAttributesQuery::ID),
256 ..Default::default()
257 },
258 server_end,
259 )
260 .map_err(|_| errno!(EIO))?;
261 let (status, info) =
267 root_proxy.query_filesystem(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
268 let use_remote_ids = status == 0
270 && info
271 .map(|i| i.fs_type == fidl_fuchsia_fs::VfsType::Fxfs.into_primitive())
272 .unwrap_or(false);
273 Ok(RemoteFs { use_remote_ids, root_proxy })
274 }
275
276 pub fn new_fs<L>(
277 locked: &mut Locked<L>,
278 kernel: &Kernel,
279 root: zx::Channel,
280 mut options: FileSystemOptions,
281 rights: fio::Flags,
282 ) -> Result<FileSystemHandle, Errno>
283 where
284 L: LockEqualOrBefore<FileOpsCore>,
285 {
286 let (client_end, server_end) = zx::Channel::create();
287 let remotefs = RemoteFs::new(root, server_end)?;
288 let mut attrs = zxio_node_attributes_t {
289 has: zxio_node_attr_has_t { id: true, ..Default::default() },
290 ..Default::default()
291 };
292 let (remote_node, node_id) =
293 match Zxio::create_with_on_representation(client_end.into(), Some(&mut attrs)) {
294 Err(status) => return Err(from_status_like_fdio!(status)),
295 Ok(zxio) => (RemoteNode { zxio, rights }, attrs.id),
296 };
297
298 if !rights.contains(fio::PERM_WRITABLE) {
299 options.flags |= MountFlags::RDONLY;
300 }
301 let use_remote_ids = remotefs.use_remote_ids;
302 let fs = FileSystem::new(
303 locked,
304 kernel,
305 CacheMode::Cached(CacheConfig::default()),
306 remotefs,
307 options,
308 )?;
309 if use_remote_ids {
310 fs.create_root(node_id, remote_node);
311 } else {
312 let root_ino = fs.allocate_ino();
313 fs.create_root(root_ino, remote_node);
314 }
315 Ok(fs)
316 }
317
318 pub fn use_remote_ids(&self) -> bool {
319 self.use_remote_ids
320 }
321}
322
323pub struct RemoteNode {
324 zxio: syncio::Zxio,
330
331 rights: fio::Flags,
334}
335
336impl RemoteNode {
337 pub fn new(zxio: syncio::Zxio, rights: fio::Flags) -> Self {
338 Self { zxio, rights }
339 }
340}
341
342pub fn new_remote_file<L>(
352 locked: &mut Locked<L>,
353 current_task: &CurrentTask,
354 handle: zx::NullableHandle,
355 flags: OpenFlags,
356) -> Result<FileHandle, Errno>
357where
358 L: LockEqualOrBefore<FileOpsCore>,
359{
360 let remote_creds = current_task.full_current_creds();
361 let (attrs, ops) = remote_file_attrs_and_ops(current_task, handle.into(), remote_creds)?;
362 let mut rights = fio::Flags::empty();
363 if flags.can_read() {
364 rights |= fio::PERM_READABLE;
365 }
366 if flags.can_write() {
367 rights |= fio::PERM_WRITABLE;
368 }
369 let mode = get_mode(&attrs, rights);
370 let mut info = FsNodeInfo::new(mode, FsCred::root());
372 update_info_from_attrs(&mut info, &attrs);
373 Ok(Anon::new_private_file_extended(locked, current_task, ops, flags, "[fuchsia:remote]", info))
374}
375
376pub fn new_remote_file_ops(
380 current_task: &CurrentTask,
381 handle: zx::NullableHandle,
382 creds: FullCredentials,
383) -> Result<Box<dyn FileOps>, Errno> {
384 let (_, ops) = remote_file_attrs_and_ops(current_task, handle, creds)?;
385 Ok(ops)
386}
387
388fn remote_file_attrs_and_ops(
389 current_task: &CurrentTask,
390 mut handle: zx::NullableHandle,
391 remote_creds: FullCredentials,
392) -> Result<(zxio_node_attr, Box<dyn FileOps>), Errno> {
393 let handle_type =
394 handle.basic_info().map_err(|status| from_status_like_fdio!(status))?.object_type;
395
396 if handle_type == zx::ObjectType::CHANNEL {
398 let channel = zx::Channel::from(handle);
399 let queryable = funknown::QueryableSynchronousProxy::new(channel);
400 if let Ok(name) = queryable.query(zx::MonotonicInstant::INFINITE) {
401 if name == fbinder::UnixDomainSocketMarker::PROTOCOL_NAME.as_bytes() {
402 let socket_ops =
403 RemoteUnixDomainSocket::new(queryable.into_channel(), remote_creds)?;
404 let socket = Socket::new_with_ops(Box::new(socket_ops))?;
405 let file_ops = SocketFile::new(socket);
406 let attr = zxio_node_attr {
407 has: zxio_node_attr_has_t { mode: true, ..zxio_node_attr_has_t::default() },
408 mode: 0o777 | FileMode::IFSOCK.bits(),
409 ..zxio_node_attr::default()
410 };
411 return Ok((attr, file_ops));
412 }
413 };
414 handle = queryable.into_channel().into_handle();
415 } else if handle_type == zx::ObjectType::COUNTER {
416 let attr = zxio_node_attr::default();
417 let file_ops = Box::new(RemoteCounter::new(handle.into()));
418 return Ok((attr, file_ops));
419 }
420
421 let zxio = Zxio::create(handle).map_err(|status| from_status_like_fdio!(status))?;
423 let mut attrs = zxio
424 .attr_get(zxio_node_attr_has_t {
425 protocols: true,
426 abilities: true,
427 content_size: true,
428 storage_size: true,
429 link_count: true,
430 object_type: true,
431 ..Default::default()
432 })
433 .map_err(|status| from_status_like_fdio!(status))?;
434 let ops: Box<dyn FileOps> = match (handle_type, attrs.object_type) {
435 (_, ZXIO_OBJECT_TYPE_DIR) => Box::new(RemoteDirectoryObject::new(zxio)),
436 (zx::ObjectType::VMO, _)
437 | (zx::ObjectType::DEBUGLOG, _)
438 | (_, ZXIO_OBJECT_TYPE_FILE)
439 | (_, ZXIO_OBJECT_TYPE_NONE) => Box::new(RemoteFileObject::new(zxio)),
440 (zx::ObjectType::SOCKET, _)
441 | (_, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET)
442 | (_, ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET)
443 | (_, ZXIO_OBJECT_TYPE_STREAM_SOCKET)
444 | (_, ZXIO_OBJECT_TYPE_RAW_SOCKET)
445 | (_, ZXIO_OBJECT_TYPE_PACKET_SOCKET) => {
446 let socket_ops = ZxioBackedSocket::new_with_zxio(current_task, zxio);
447 let socket = Socket::new_with_ops(Box::new(socket_ops))?;
448 attrs.has.mode = true;
449 attrs.mode = FileMode::IFSOCK.bits();
450 SocketFile::new(socket)
451 }
452 _ => return error!(ENOTSUP),
453 };
454 Ok((attrs, ops))
455}
456
457pub fn create_fuchsia_pipe<L>(
458 locked: &mut Locked<L>,
459 current_task: &CurrentTask,
460 socket: zx::Socket,
461 flags: OpenFlags,
462) -> Result<FileHandle, Errno>
463where
464 L: LockEqualOrBefore<FileOpsCore>,
465{
466 new_remote_file(locked, current_task, socket.into(), flags)
467}
468
469fn fetch_and_refresh_info_impl<'a>(
470 zxio: &syncio::Zxio,
471 info: &'a RwLock<FsNodeInfo>,
472) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
473 let attrs = zxio
474 .attr_get(zxio_node_attr_has_t {
475 content_size: true,
476 storage_size: true,
477 link_count: true,
478 modification_time: true,
479 change_time: true,
480 access_time: true,
481 casefold: true,
482 wrapping_key_id: true,
483 pending_access_time_update: info.read().pending_time_access_update,
484 ..Default::default()
485 })
486 .map_err(|status| from_status_like_fdio!(status))?;
487 let mut info = info.write();
488 update_info_from_attrs(&mut info, &attrs);
489 info.pending_time_access_update = false;
490 Ok(RwLockWriteGuard::downgrade(info))
491}
492
493pub fn update_info_from_attrs(info: &mut FsNodeInfo, attrs: &zxio_node_attributes_t) {
495 if attrs.has.content_size {
497 info.size = attrs.content_size.try_into().unwrap_or(std::usize::MAX);
498 }
499 if attrs.has.storage_size {
500 info.blocks = usize::try_from(attrs.storage_size)
501 .unwrap_or(std::usize::MAX)
502 .div_ceil(DEFAULT_BYTES_PER_BLOCK)
503 }
504 info.blksize = DEFAULT_BYTES_PER_BLOCK;
505 if attrs.has.link_count {
506 info.link_count = attrs.link_count.try_into().unwrap_or(std::usize::MAX);
507 }
508 if attrs.has.modification_time {
509 info.time_modify =
510 UtcInstant::from_nanos(attrs.modification_time.try_into().unwrap_or(i64::MAX));
511 }
512 if attrs.has.change_time {
513 info.time_status_change =
514 UtcInstant::from_nanos(attrs.change_time.try_into().unwrap_or(i64::MAX));
515 }
516 if attrs.has.access_time {
517 info.time_access = UtcInstant::from_nanos(attrs.access_time.try_into().unwrap_or(i64::MAX));
518 }
519 if attrs.has.wrapping_key_id {
520 info.wrapping_key_id = Some(attrs.wrapping_key_id);
521 }
522}
523
524fn get_mode(attrs: &zxio_node_attributes_t, rights: fio::Flags) -> FileMode {
525 if attrs.protocols & ZXIO_NODE_PROTOCOL_SYMLINK != 0 {
526 FileMode::IFLNK | FileMode::ALLOW_ALL
528 } else if attrs.has.mode {
529 FileMode::from_bits(attrs.mode)
531 } else {
532 let is_directory =
535 attrs.protocols & ZXIO_NODE_PROTOCOL_DIRECTORY == ZXIO_NODE_PROTOCOL_DIRECTORY;
536 let mode = if is_directory { FileMode::IFDIR } else { FileMode::IFREG };
537 let mut permissions = FileMode::EMPTY;
538 if rights.contains(fio::PERM_READABLE) {
539 permissions |= FileMode::IRUSR;
540 }
541 if rights.contains(fio::PERM_WRITABLE) {
542 permissions |= FileMode::IWUSR;
543 }
544 if rights.contains(fio::PERM_EXECUTABLE) {
545 permissions |= FileMode::IXUSR;
546 }
547 permissions |= FileMode::from_bits((permissions.bits() >> 3) | (permissions.bits() >> 6));
549 mode | permissions
550 }
551}
552
553fn get_name_str<'a>(name_bytes: &'a FsStr) -> Result<&'a str, Errno> {
554 std::str::from_utf8(name_bytes.as_ref()).map_err(|_| {
555 log_warn!("bad utf8 in pathname! remote filesystems can't handle this");
556 errno!(EINVAL)
557 })
558}
559
560impl XattrStorage for syncio::Zxio {
561 fn get_xattr(
562 &self,
563 _locked: &mut Locked<FileOpsCore>,
564 name: &FsStr,
565 ) -> Result<FsString, Errno> {
566 Ok(self
567 .xattr_get(name)
568 .map_err(|status| match status {
569 zx::Status::NOT_FOUND => errno!(ENODATA),
570 status => from_status_like_fdio!(status),
571 })?
572 .into())
573 }
574
575 fn set_xattr(
576 &self,
577 _locked: &mut Locked<FileOpsCore>,
578 name: &FsStr,
579 value: &FsStr,
580 op: XattrOp,
581 ) -> Result<(), Errno> {
582 let mode = match op {
583 XattrOp::Set => XattrSetMode::Set,
584 XattrOp::Create => XattrSetMode::Create,
585 XattrOp::Replace => XattrSetMode::Replace,
586 };
587
588 self.xattr_set(name, value, mode).map_err(|status| match status {
589 zx::Status::NOT_FOUND => errno!(ENODATA),
590 status => from_status_like_fdio!(status),
591 })
592 }
593
594 fn remove_xattr(&self, _locked: &mut Locked<FileOpsCore>, name: &FsStr) -> Result<(), Errno> {
595 self.xattr_remove(name).map_err(|status| match status {
596 zx::Status::NOT_FOUND => errno!(ENODATA),
597 _ => from_status_like_fdio!(status),
598 })
599 }
600
601 fn list_xattrs(&self, _locked: &mut Locked<FileOpsCore>) -> Result<Vec<FsString>, Errno> {
602 self.xattr_list()
603 .map(|attrs| attrs.into_iter().map(FsString::new).collect::<Vec<_>>())
604 .map_err(|status| from_status_like_fdio!(status))
605 }
606}
607
608impl FsNodeOps for RemoteNode {
609 fs_node_impl_xattr_delegate!(self, self.zxio);
610
611 fn create_file_ops(
612 &self,
613 locked: &mut Locked<FileOpsCore>,
614 node: &FsNode,
615 current_task: &CurrentTask,
616 flags: OpenFlags,
617 ) -> Result<Box<dyn FileOps>, Errno> {
618 {
619 let node_info = node.fetch_and_refresh_info(locked, current_task)?;
620 if node_info.mode.is_dir() {
621 if let Some(wrapping_key_id) = node_info.wrapping_key_id {
622 if flags.can_write() {
623 let crypt_service =
625 node.fs().crypt_service().ok_or_else(|| errno!(ENOKEY))?;
626 if !crypt_service.contains_key(EncryptionKeyId::from(wrapping_key_id)) {
627 return error!(ENOKEY);
628 }
629 }
630 }
631 return Ok(Box::new(RemoteDirectoryObject::new(
634 self.zxio.deep_clone().map_err(|status| from_status_like_fdio!(status))?,
635 )));
636 }
637 }
638
639 node.fail_if_locked(current_task)?;
641
642 if flags.can_write() {
644 node.fsverity.lock().check_writable()?;
645 }
646
647 Ok(Box::new(RemoteFileObject::new(self.zxio.clone())))
650 }
651
652 fn mknod(
653 &self,
654 _locked: &mut Locked<FileOpsCore>,
655 node: &FsNode,
656 current_task: &CurrentTask,
657 name: &FsStr,
658 mode: FileMode,
659 dev: DeviceType,
660 owner: FsCred,
661 ) -> Result<FsNodeHandle, Errno> {
662 node.fail_if_locked(current_task)?;
663 let name = get_name_str(name)?;
664
665 let fs = node.fs();
666 let fs_ops = RemoteFs::from_fs(&fs);
667
668 let zxio;
669 let mut node_id;
670 if !(mode.is_reg() || mode.is_chr() || mode.is_blk() || mode.is_fifo() || mode.is_sock()) {
671 return error!(EINVAL, name);
672 }
673 let mut attrs = zxio_node_attributes_t {
674 has: zxio_node_attr_has_t { id: true, ..Default::default() },
675 ..Default::default()
676 };
677 zxio = self
678 .zxio
679 .open(
680 name,
681 fio::Flags::FLAG_MUST_CREATE
682 | fio::Flags::PROTOCOL_FILE
683 | fio::PERM_READABLE
684 | fio::PERM_WRITABLE,
685 ZxioOpenOptions::new(
686 Some(&mut attrs),
687 Some(zxio_node_attributes_t {
688 mode: mode.bits(),
689 uid: owner.uid,
690 gid: owner.gid,
691 rdev: dev.bits(),
692 has: zxio_node_attr_has_t {
693 mode: true,
694 uid: true,
695 gid: true,
696 rdev: true,
697 ..Default::default()
698 },
699 ..Default::default()
700 }),
701 ),
702 )
703 .map_err(|status| from_status_like_fdio!(status, name))?;
704 node_id = attrs.id;
705
706 let ops = if mode.is_reg() {
707 Box::new(RemoteNode { zxio, rights: self.rights }) as Box<dyn FsNodeOps>
708 } else {
709 Box::new(RemoteSpecialNode { zxio }) as Box<dyn FsNodeOps>
710 };
711
712 if !fs_ops.use_remote_ids {
713 node_id = fs.allocate_ino();
714 }
715 let child =
716 fs.create_node(node_id, ops, FsNodeInfo { rdev: dev, ..FsNodeInfo::new(mode, owner) });
717 Ok(child)
718 }
719
720 fn mkdir(
721 &self,
722 _locked: &mut Locked<FileOpsCore>,
723 node: &FsNode,
724 current_task: &CurrentTask,
725 name: &FsStr,
726 mode: FileMode,
727 owner: FsCred,
728 ) -> Result<FsNodeHandle, Errno> {
729 node.fail_if_locked(current_task)?;
730 let name = get_name_str(name)?;
731
732 let fs = node.fs();
733 let fs_ops = RemoteFs::from_fs(&fs);
734
735 let zxio;
736 let mut node_id;
737 let mut attrs = zxio_node_attributes_t {
738 has: zxio_node_attr_has_t { id: true, ..Default::default() },
739 ..Default::default()
740 };
741 zxio = self
742 .zxio
743 .open(
744 name,
745 fio::Flags::FLAG_MUST_CREATE
746 | fio::Flags::PROTOCOL_DIRECTORY
747 | fio::PERM_READABLE
748 | fio::PERM_WRITABLE,
749 ZxioOpenOptions::new(
750 Some(&mut attrs),
751 Some(zxio_node_attributes_t {
752 mode: mode.bits(),
753 uid: owner.uid,
754 gid: owner.gid,
755 has: zxio_node_attr_has_t {
756 mode: true,
757 uid: true,
758 gid: true,
759 ..Default::default()
760 },
761 ..Default::default()
762 }),
763 ),
764 )
765 .map_err(|status| from_status_like_fdio!(status, name))?;
766 node_id = attrs.id;
767
768 let ops = RemoteNode { zxio, rights: self.rights };
769 if !fs_ops.use_remote_ids {
770 node_id = fs.allocate_ino();
771 }
772 let child = fs.create_node(node_id, ops, FsNodeInfo::new(mode, owner));
773 Ok(child)
774 }
775
776 fn lookup(
777 &self,
778 _locked: &mut Locked<FileOpsCore>,
779 node: &FsNode,
780 current_task: &CurrentTask,
781 name: &FsStr,
782 ) -> Result<FsNodeHandle, Errno> {
783 let name = get_name_str(name)?;
784
785 let fs = node.fs();
786 let fs_ops = RemoteFs::from_fs(&fs);
787
788 let mut attrs = zxio_node_attributes_t {
789 has: zxio_node_attr_has_t {
790 protocols: true,
791 abilities: true,
792 mode: true,
793 uid: true,
794 gid: true,
795 rdev: true,
796 id: true,
797 fsverity_enabled: true,
798 casefold: true,
799 modification_time: true,
800 change_time: true,
801 access_time: true,
802 ..Default::default()
803 },
804 ..Default::default()
805 };
806 let mut options = ZxioOpenOptions::new(Some(&mut attrs), None);
807 let mut selinux_context_buffer =
808 MaybeUninit::<[u8; fio::MAX_SELINUX_CONTEXT_ATTRIBUTE_LEN as usize]>::uninit();
809 let mut cached_context = security::fs_is_xattr_labeled(node.fs())
810 .then(|| SelinuxContextAttr::new(&mut selinux_context_buffer));
811 if let Some(buffer) = &mut cached_context {
812 options = options.with_selinux_context_read(buffer).unwrap();
813 }
814 let zxio = self
815 .zxio
816 .open(name, self.rights, options)
817 .map_err(|status| from_status_like_fdio!(status, name))?;
818 let symlink_zxio = zxio.clone();
819 let mode = get_mode(&attrs, self.rights);
820 let node_id = if fs_ops.use_remote_ids {
821 if attrs.id == fio::INO_UNKNOWN {
822 return error!(ENOTSUP);
823 }
824 attrs.id
825 } else {
826 fs.allocate_ino()
827 };
828 let owner = FsCred { uid: attrs.uid, gid: attrs.gid };
829 let rdev = DeviceType::from_bits(attrs.rdev);
830 let fsverity_enabled = attrs.fsverity_enabled;
831 if fsverity_enabled && (attrs.protocols & ZXIO_NODE_PROTOCOL_FILE == 0) {
833 return error!(EINVAL);
834 }
835 let casefold = attrs.casefold;
836 let time_modify =
837 UtcInstant::from_nanos(attrs.modification_time.try_into().unwrap_or(i64::MAX));
838 let time_status_change =
839 UtcInstant::from_nanos(attrs.change_time.try_into().unwrap_or(i64::MAX));
840 let time_access = UtcInstant::from_nanos(attrs.access_time.try_into().unwrap_or(i64::MAX));
841
842 let node = fs.get_or_create_node(node_id, || {
843 let ops = if mode.is_lnk() {
844 Box::new(RemoteSymlink { zxio: Mutex::new(zxio) }) as Box<dyn FsNodeOps>
845 } else if mode.is_reg() || mode.is_dir() {
846 Box::new(RemoteNode { zxio, rights: self.rights }) as Box<dyn FsNodeOps>
847 } else {
848 Box::new(RemoteSpecialNode { zxio }) as Box<dyn FsNodeOps>
849 };
850 let child = FsNode::new_uncached(
851 node_id,
852 ops,
853 &fs,
854 FsNodeInfo {
855 rdev,
856 casefold,
857 time_status_change,
858 time_modify,
859 time_access,
860 ..FsNodeInfo::new(mode, owner)
861 },
862 );
863 if fsverity_enabled {
864 *child.fsverity.lock() = FsVerityState::FsVerity;
865 }
866 if let Some(buffer) = cached_context.as_ref().and_then(|buffer| buffer.get()) {
867 let _ = security::fs_node_notify_security_context(
870 current_task,
871 &child,
872 FsStr::new(buffer),
873 );
874 }
875 Ok(child)
876 })?;
877 if let Some(symlink) = node.downcast_ops::<RemoteSymlink>() {
878 let mut zxio_guard = symlink.zxio.lock();
879 *zxio_guard = symlink_zxio;
880 }
881 Ok(node)
882 }
883
884 fn truncate(
885 &self,
886 _locked: &mut Locked<FileOpsCore>,
887 _guard: &AppendLockGuard<'_>,
888 node: &FsNode,
889 current_task: &CurrentTask,
890 length: u64,
891 ) -> Result<(), Errno> {
892 node.fail_if_locked(current_task)?;
893 self.zxio.truncate(length).map_err(|status| from_status_like_fdio!(status))
894 }
895
896 fn allocate(
897 &self,
898 _locked: &mut Locked<FileOpsCore>,
899 _guard: &AppendLockGuard<'_>,
900 node: &FsNode,
901 current_task: &CurrentTask,
902 mode: FallocMode,
903 offset: u64,
904 length: u64,
905 ) -> Result<(), Errno> {
906 match mode {
907 FallocMode::Allocate { keep_size: false } => {
908 node.fail_if_locked(current_task)?;
909 self.zxio
910 .allocate(offset, length, AllocateMode::empty())
911 .map_err(|status| from_status_like_fdio!(status))?;
912 Ok(())
913 }
914 _ => error!(EINVAL),
915 }
916 }
917
918 fn fetch_and_refresh_info<'a>(
919 &self,
920 _locked: &mut Locked<FileOpsCore>,
921 _node: &FsNode,
922 _current_task: &CurrentTask,
923 info: &'a RwLock<FsNodeInfo>,
924 ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
925 fetch_and_refresh_info_impl(&self.zxio, info)
926 }
927
928 fn update_attributes(
929 &self,
930 _locked: &mut Locked<FileOpsCore>,
931 _current_task: &CurrentTask,
932 info: &FsNodeInfo,
933 has: zxio_node_attr_has_t,
934 ) -> Result<(), Errno> {
935 let mut mutable_node_attributes = zxio_node_attributes_t {
937 modification_time: info.time_modify.into_nanos() as u64,
938 access_time: info.time_access.into_nanos() as u64,
939 mode: info.mode.bits(),
940 uid: info.uid,
941 gid: info.gid,
942 rdev: info.rdev.bits(),
943 casefold: info.casefold,
944 has,
945 ..Default::default()
946 };
947 if let Some(id) = info.wrapping_key_id {
948 mutable_node_attributes.wrapping_key_id = id;
949 }
950 self.zxio
951 .attr_set(&mutable_node_attributes)
952 .map_err(|status| from_status_like_fdio!(status))
953 }
954
955 fn unlink(
956 &self,
957 _locked: &mut Locked<FileOpsCore>,
958 _node: &FsNode,
959 _current_task: &CurrentTask,
960 name: &FsStr,
961 _child: &FsNodeHandle,
962 ) -> Result<(), Errno> {
963 let name = get_name_str(name)?;
967 self.zxio
968 .unlink(name, fio::UnlinkFlags::empty())
969 .map_err(|status| from_status_like_fdio!(status))
970 }
971
972 fn create_symlink(
973 &self,
974 _locked: &mut Locked<FileOpsCore>,
975 node: &FsNode,
976 current_task: &CurrentTask,
977 name: &FsStr,
978 target: &FsStr,
979 owner: FsCred,
980 ) -> Result<FsNodeHandle, Errno> {
981 node.fail_if_locked(current_task)?;
982
983 let name = get_name_str(name)?;
984 let zxio = self
985 .zxio
986 .create_symlink(name, target)
987 .map_err(|status| from_status_like_fdio!(status))?;
988
989 let fs = node.fs();
990 let fs_ops = RemoteFs::from_fs(&fs);
991
992 let node_id = if fs_ops.use_remote_ids {
993 let attrs = zxio
994 .attr_get(zxio_node_attr_has_t { id: true, ..Default::default() })
995 .map_err(|status| from_status_like_fdio!(status))?;
996 attrs.id
997 } else {
998 fs.allocate_ino()
999 };
1000 let symlink = fs.create_node(
1001 node_id,
1002 RemoteSymlink { zxio: Mutex::new(zxio) },
1003 FsNodeInfo {
1004 size: target.len(),
1005 ..FsNodeInfo::new(FileMode::IFLNK | FileMode::ALLOW_ALL, owner)
1006 },
1007 );
1008 Ok(symlink)
1009 }
1010
1011 fn create_tmpfile(
1012 &self,
1013 node: &FsNode,
1014 _current_task: &CurrentTask,
1015 mode: FileMode,
1016 owner: FsCred,
1017 ) -> Result<FsNodeHandle, Errno> {
1018 let fs = node.fs();
1019 let fs_ops = RemoteFs::from_fs(&fs);
1020
1021 let zxio;
1022 let mut node_id;
1023 if !mode.is_reg() {
1024 return error!(EINVAL);
1025 }
1026 let mut attrs = zxio_node_attributes_t {
1027 has: zxio_node_attr_has_t { id: true, ..Default::default() },
1028 ..Default::default()
1029 };
1030 zxio = self
1037 .zxio
1038 .open(
1039 ".",
1040 fio::Flags::PROTOCOL_FILE
1041 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
1042 | self.rights,
1043 ZxioOpenOptions::new(
1044 Some(&mut attrs),
1045 Some(zxio_node_attributes_t {
1046 mode: mode.bits(),
1047 uid: owner.uid,
1048 gid: owner.gid,
1049 has: zxio_node_attr_has_t {
1050 mode: true,
1051 uid: true,
1052 gid: true,
1053 ..Default::default()
1054 },
1055 ..Default::default()
1056 }),
1057 ),
1058 )
1059 .map_err(|status| from_status_like_fdio!(status))?;
1060 node_id = attrs.id;
1061
1062 let ops = Box::new(RemoteNode { zxio, rights: self.rights }) as Box<dyn FsNodeOps>;
1063
1064 if !fs_ops.use_remote_ids {
1065 node_id = fs.allocate_ino();
1066 }
1067 let child = fs.create_node(node_id, ops, FsNodeInfo::new(mode, owner));
1068
1069 Ok(child)
1070 }
1071
1072 fn link(
1073 &self,
1074 _locked: &mut Locked<FileOpsCore>,
1075 node: &FsNode,
1076 _current_task: &CurrentTask,
1077 name: &FsStr,
1078 child: &FsNodeHandle,
1079 ) -> Result<(), Errno> {
1080 if !RemoteFs::from_fs(&node.fs()).use_remote_ids {
1081 return error!(EPERM);
1082 }
1083 let name = get_name_str(name)?;
1084 let link_into = |zxio: &syncio::Zxio| {
1085 zxio.link_into(&self.zxio, name).map_err(|status| match status {
1086 zx::Status::BAD_STATE => errno!(EXDEV),
1087 zx::Status::ACCESS_DENIED => errno!(ENOKEY),
1088 s => from_status_like_fdio!(s),
1089 })
1090 };
1091 if let Some(child) = child.downcast_ops::<RemoteNode>() {
1092 link_into(&child.zxio)
1093 } else if let Some(child) = child.downcast_ops::<RemoteSymlink>() {
1094 link_into(&child.zxio())
1095 } else {
1096 error!(EXDEV)
1097 }
1098 }
1099
1100 fn forget(
1101 self: Box<Self>,
1102 _locked: &mut Locked<FileOpsCore>,
1103 _current_task: &CurrentTask,
1104 info: FsNodeInfo,
1105 ) -> Result<(), Errno> {
1106 if info.pending_time_access_update {
1108 self.zxio
1109 .close_and_update_access_time()
1110 .map_err(|status| from_status_like_fdio!(status))?;
1111 }
1112 Ok(())
1113 }
1114
1115 fn enable_fsverity(&self, descriptor: &fsverity_descriptor) -> Result<(), Errno> {
1116 let descr = zxio_fsverity_descriptor_t {
1117 hash_algorithm: descriptor.hash_algorithm,
1118 salt_size: descriptor.salt_size,
1119 salt: descriptor.salt,
1120 };
1121 self.zxio.enable_verity(&descr).map_err(|status| from_status_like_fdio!(status))
1122 }
1123
1124 fn get_fsverity_descriptor(&self, log_blocksize: u8) -> Result<fsverity_descriptor, Errno> {
1125 let mut root_hash = [0; ZXIO_ROOT_HASH_LENGTH];
1126 let attrs = self
1127 .zxio
1128 .attr_get_with_root_hash(
1129 zxio_node_attr_has_t {
1130 content_size: true,
1131 fsverity_options: true,
1132 fsverity_root_hash: true,
1133 ..Default::default()
1134 },
1135 &mut root_hash,
1136 )
1137 .map_err(|status| match status {
1138 zx::Status::INVALID_ARGS => errno!(ENODATA),
1139 _ => from_status_like_fdio!(status),
1140 })?;
1141 return Ok(fsverity_descriptor {
1142 version: 1,
1143 hash_algorithm: attrs.fsverity_options.hash_alg,
1144 log_blocksize,
1145 salt_size: attrs.fsverity_options.salt_size as u8,
1146 __reserved_0x04: 0u32,
1147 data_size: attrs.content_size,
1148 root_hash,
1149 salt: attrs.fsverity_options.salt,
1150 __reserved: [0u8; 144],
1151 });
1152 }
1153}
1154
1155struct RemoteSpecialNode {
1156 zxio: syncio::Zxio,
1157}
1158
1159impl FsNodeOps for RemoteSpecialNode {
1160 fs_node_impl_not_dir!();
1161 fs_node_impl_xattr_delegate!(self, self.zxio);
1162
1163 fn create_file_ops(
1164 &self,
1165 _locked: &mut Locked<FileOpsCore>,
1166 _node: &FsNode,
1167 _current_task: &CurrentTask,
1168 _flags: OpenFlags,
1169 ) -> Result<Box<dyn FileOps>, Errno> {
1170 unreachable!("Special nodes cannot be opened.");
1171 }
1172}
1173
1174fn zxio_read_write_inner_map_error(status: zx::Status) -> Errno {
1175 match status {
1176 zx::Status::INVALID_ARGS | zx::Status::NOT_FOUND => errno!(EFAULT, ""),
1179 status => from_status_like_fdio!(status),
1180 }
1181}
1182
1183fn zxio_read_inner(
1184 data: &mut dyn OutputBuffer,
1185 unified_read_fn: impl FnOnce(&[syncio::zxio::zx_iovec]) -> Result<usize, zx::Status>,
1186 vmo_read_fn: impl FnOnce(&mut [u8]) -> Result<usize, zx::Status>,
1187) -> Result<usize, Errno> {
1188 let read_bytes = with_iovec_segments(data, |iovecs| {
1189 unified_read_fn(&iovecs).map_err(zxio_read_write_inner_map_error)
1190 });
1191
1192 match read_bytes {
1193 Some(actual) => {
1194 let actual = actual?;
1195 unsafe { data.advance(actual) }?;
1198 Ok(actual)
1199 }
1200 None => {
1201 let total = data.available();
1203 let mut bytes = vec![0u8; total];
1204 let actual =
1205 vmo_read_fn(&mut bytes).map_err(|status| from_status_like_fdio!(status))?;
1206 data.write_all(&bytes[0..actual])
1207 }
1208 }
1209}
1210
1211fn zxio_read_at(zxio: &Zxio, offset: usize, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
1212 let offset = offset as u64;
1213 zxio_read_inner(
1214 data,
1215 |iovecs| {
1216 unsafe { zxio.readv_at(offset, iovecs) }
1221 },
1222 |bytes| zxio.read_at(offset, bytes),
1223 )
1224}
1225
1226fn zxio_write_inner(
1227 data: &mut dyn InputBuffer,
1228 unified_write_fn: impl FnOnce(&[syncio::zxio::zx_iovec]) -> Result<usize, zx::Status>,
1229 vmo_write_fn: impl FnOnce(&[u8]) -> Result<usize, zx::Status>,
1230) -> Result<usize, Errno> {
1231 let write_bytes = with_iovec_segments(data, |iovecs| {
1232 unified_write_fn(&iovecs).map_err(zxio_read_write_inner_map_error)
1233 });
1234
1235 match write_bytes {
1236 Some(actual) => {
1237 let actual = actual?;
1238 data.advance(actual)?;
1239 Ok(actual)
1240 }
1241 None => {
1242 let bytes = data.peek_all()?;
1244 let actual = vmo_write_fn(&bytes).map_err(|status| from_status_like_fdio!(status))?;
1245 data.advance(actual)?;
1246 Ok(actual)
1247 }
1248 }
1249}
1250
1251fn zxio_write_at(
1252 zxio: &Zxio,
1253 _current_task: &CurrentTask,
1254 offset: usize,
1255 data: &mut dyn InputBuffer,
1256) -> Result<usize, Errno> {
1257 let offset = offset as u64;
1258 zxio_write_inner(
1259 data,
1260 |iovecs| {
1261 unsafe { zxio.writev_at(offset, iovecs) }
1264 },
1265 |bytes| zxio.write_at(offset, bytes),
1266 )
1267}
1268
1269#[derive(Default)]
1271struct RemoteDirectoryIterator<'a> {
1272 iterator: Option<DirentIterator<'a>>,
1273
1274 pending_entry: Entry,
1277}
1278
1279#[derive(Default)]
1280enum Entry {
1281 #[default]
1283 None,
1284
1285 Some(ZxioDirent),
1286
1287 DotDot,
1289}
1290
1291impl Entry {
1292 fn take(&mut self) -> Entry {
1293 std::mem::replace(self, Entry::None)
1294 }
1295}
1296
1297impl From<Option<ZxioDirent>> for Entry {
1298 fn from(value: Option<ZxioDirent>) -> Self {
1299 match value {
1300 None => Entry::None,
1301 Some(x) => Entry::Some(x),
1302 }
1303 }
1304}
1305
1306impl<'a> RemoteDirectoryIterator<'a> {
1307 fn get_or_init_iterator(&mut self, zxio: &'a Zxio) -> Result<&mut DirentIterator<'a>, Errno> {
1308 if self.iterator.is_none() {
1309 let iterator =
1310 zxio.create_dirent_iterator().map_err(|status| from_status_like_fdio!(status))?;
1311 self.iterator = Some(iterator);
1312 }
1313 if let Some(iterator) = &mut self.iterator {
1314 return Ok(iterator);
1315 }
1316
1317 error!(EIO)
1319 }
1320
1321 pub fn next(&mut self, zxio: &'a Zxio) -> Result<Entry, Errno> {
1324 let mut next = self.pending_entry.take();
1325 if let Entry::None = next {
1326 next = self
1327 .get_or_init_iterator(zxio)?
1328 .next()
1329 .transpose()
1330 .map_err(|status| from_status_like_fdio!(status))?
1331 .into();
1332 }
1333 match &next {
1337 Entry::Some(ZxioDirent { name, .. }) if name == "." => {
1338 self.pending_entry = Entry::DotDot;
1339 }
1340 _ => {}
1341 }
1342 Ok(next)
1343 }
1344}
1345
1346struct RemoteDirectoryObject {
1347 iterator: Mutex<RemoteDirectoryIterator<'static>>,
1348
1349 zxio: Zxio,
1353}
1354
1355impl RemoteDirectoryObject {
1356 pub fn new(zxio: Zxio) -> RemoteDirectoryObject {
1357 RemoteDirectoryObject { zxio, iterator: Mutex::new(RemoteDirectoryIterator::default()) }
1358 }
1359
1360 unsafe fn zxio(&self) -> &'static Zxio {
1367 #[allow(clippy::undocumented_unsafe_blocks, reason = "2024 edition migration")]
1368 unsafe {
1369 &*(&self.zxio as *const Zxio)
1370 }
1371 }
1372}
1373
1374impl FileOps for RemoteDirectoryObject {
1375 fileops_impl_directory!();
1376
1377 fn seek(
1378 &self,
1379 _locked: &mut Locked<FileOpsCore>,
1380 _file: &FileObject,
1381 _current_task: &CurrentTask,
1382 current_offset: off_t,
1383 target: SeekTarget,
1384 ) -> Result<off_t, Errno> {
1385 let mut iterator = self.iterator.lock();
1386 let new_offset = default_seek(current_offset, target, || error!(EINVAL))?;
1387 let mut iterator_position = current_offset;
1388
1389 if new_offset < iterator_position {
1390 if let Some(iterator) = &mut iterator.iterator {
1393 iterator.rewind().map_err(|status| from_status_like_fdio!(status))?;
1394 }
1395 iterator.pending_entry = Entry::None;
1396 iterator_position = 0;
1397 }
1398
1399 for i in iterator_position..new_offset {
1401 match iterator.next(unsafe { self.zxio() }) {
1404 Ok(Entry::Some(_) | Entry::DotDot) => {}
1405 Ok(Entry::None) => break, Err(_) => {
1407 return Ok(i);
1413 }
1414 }
1415 }
1416
1417 Ok(new_offset)
1418 }
1419
1420 fn readdir(
1421 &self,
1422 _locked: &mut Locked<FileOpsCore>,
1423 file: &FileObject,
1424 _current_task: &CurrentTask,
1425 sink: &mut dyn DirentSink,
1426 ) -> Result<(), Errno> {
1427 let mut iterator = self.iterator.lock();
1430
1431 loop {
1432 let entry = iterator.next(unsafe { self.zxio() })?;
1435 if let Err(e) = match &entry {
1436 Entry::Some(entry) => {
1437 let inode_num: ino_t = entry.id.ok_or_else(|| errno!(EIO))?;
1438 let entry_type = if entry.is_dir() {
1439 DirectoryEntryType::DIR
1440 } else if entry.is_file() {
1441 DirectoryEntryType::REG
1442 } else {
1443 DirectoryEntryType::UNKNOWN
1444 };
1445 sink.add(inode_num, sink.offset() + 1, entry_type, entry.name.as_bstr())
1446 }
1447 Entry::DotDot => {
1448 let inode_num = if let Some(parent) = file.name.parent_within_mount() {
1449 parent.node.ino
1450 } else {
1451 file.name.entry.node.ino
1453 };
1454 sink.add(inode_num, sink.offset() + 1, DirectoryEntryType::DIR, "..".into())
1455 }
1456 Entry::None => break,
1457 } {
1458 iterator.pending_entry = entry;
1459 return Err(e);
1460 }
1461 }
1462 Ok(())
1463 }
1464
1465 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1466 self.zxio.sync().map_err(|status| match status {
1467 zx::Status::NO_RESOURCES | zx::Status::NO_MEMORY | zx::Status::NO_SPACE => {
1468 errno!(ENOSPC)
1469 }
1470 zx::Status::INVALID_ARGS | zx::Status::NOT_FILE => errno!(EINVAL),
1471 zx::Status::BAD_HANDLE => errno!(EBADFD),
1472 zx::Status::NOT_SUPPORTED => errno!(ENOTSUP),
1473 zx::Status::INTERRUPTED_RETRY => errno!(EINTR),
1474 _ => errno!(EIO),
1475 })
1476 }
1477
1478 fn to_handle(
1479 &self,
1480 _file: &FileObject,
1481 _current_task: &CurrentTask,
1482 ) -> Result<Option<zx::NullableHandle>, Errno> {
1483 self.zxio
1484 .deep_clone()
1485 .and_then(Zxio::release)
1486 .map(Some)
1487 .map_err(|status| from_status_like_fdio!(status))
1488 }
1489}
1490
1491pub struct RemoteFileObject {
1492 zxio: Zxio,
1495
1496 read_only_memory: OnceCell<Arc<MemoryObject>>,
1498
1499 read_exec_memory: OnceCell<Arc<MemoryObject>>,
1501}
1502
1503impl RemoteFileObject {
1504 fn new(zxio: Zxio) -> RemoteFileObject {
1505 RemoteFileObject {
1506 zxio,
1507 read_only_memory: Default::default(),
1508 read_exec_memory: Default::default(),
1509 }
1510 }
1511
1512 fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno> {
1513 let without_exec = self
1514 .zxio
1515 .vmo_get(prot.to_vmar_flags() - zx::VmarFlags::PERM_EXECUTE)
1516 .map_err(|status| from_status_like_fdio!(status))?;
1517 let all_flags = if prot.contains(ProtectionFlags::EXEC) {
1518 without_exec.replace_as_executable(&VMEX_RESOURCE).map_err(impossible_error)?
1519 } else {
1520 without_exec
1521 };
1522 Ok(Arc::new(MemoryObject::from(all_flags)))
1523 }
1524}
1525
1526impl FileOps for RemoteFileObject {
1527 fileops_impl_seekable!();
1528
1529 fn read(
1530 &self,
1531 _locked: &mut Locked<FileOpsCore>,
1532 _file: &FileObject,
1533 _current_task: &CurrentTask,
1534 offset: usize,
1535 data: &mut dyn OutputBuffer,
1536 ) -> Result<usize, Errno> {
1537 zxio_read_at(&self.zxio, offset, data)
1538 }
1539
1540 fn write(
1541 &self,
1542 _locked: &mut Locked<FileOpsCore>,
1543 _file: &FileObject,
1544 current_task: &CurrentTask,
1545 offset: usize,
1546 data: &mut dyn InputBuffer,
1547 ) -> Result<usize, Errno> {
1548 zxio_write_at(&self.zxio, current_task, offset, data)
1549 }
1550
1551 fn get_memory(
1552 &self,
1553 _locked: &mut Locked<FileOpsCore>,
1554 _file: &FileObject,
1555 _current_task: &CurrentTask,
1556 _length: Option<usize>,
1557 prot: ProtectionFlags,
1558 ) -> Result<Arc<MemoryObject>, Errno> {
1559 trace_duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
1560 let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
1561 Some(&self.read_exec_memory)
1562 } else if prot == ProtectionFlags::READ {
1563 Some(&self.read_only_memory)
1564 } else {
1565 None
1566 };
1567
1568 memory_cache
1569 .map(|c| c.get_or_try_init(|| self.fetch_remote_memory(prot)).cloned())
1570 .unwrap_or_else(|| self.fetch_remote_memory(prot))
1571 }
1572
1573 fn to_handle(
1574 &self,
1575 _file: &FileObject,
1576 _current_task: &CurrentTask,
1577 ) -> Result<Option<zx::NullableHandle>, Errno> {
1578 self.zxio
1579 .deep_clone()
1580 .and_then(Zxio::release)
1581 .map(Some)
1582 .map_err(|status| from_status_like_fdio!(status))
1583 }
1584
1585 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1586 self.zxio.sync().map_err(|status| match status {
1587 zx::Status::NO_RESOURCES | zx::Status::NO_MEMORY | zx::Status::NO_SPACE => {
1588 errno!(ENOSPC)
1589 }
1590 zx::Status::INVALID_ARGS | zx::Status::NOT_FILE => errno!(EINVAL),
1591 zx::Status::BAD_HANDLE => errno!(EBADFD),
1592 zx::Status::NOT_SUPPORTED => errno!(ENOTSUP),
1593 zx::Status::INTERRUPTED_RETRY => errno!(EINTR),
1594 _ => errno!(EIO),
1595 })
1596 }
1597
1598 fn ioctl(
1599 &self,
1600 locked: &mut Locked<Unlocked>,
1601 file: &FileObject,
1602 current_task: &CurrentTask,
1603 request: u32,
1604 arg: SyscallArg,
1605 ) -> Result<SyscallResult, Errno> {
1606 default_ioctl(file, locked, current_task, request, arg)
1607 }
1608}
1609
1610struct RemoteSymlink {
1611 zxio: Mutex<syncio::Zxio>,
1612}
1613
1614impl RemoteSymlink {
1615 fn zxio(&self) -> syncio::Zxio {
1616 self.zxio.lock().clone()
1617 }
1618}
1619
1620impl FsNodeOps for RemoteSymlink {
1621 fs_node_impl_symlink!();
1622 fs_node_impl_xattr_delegate!(self, self.zxio());
1623
1624 fn readlink(
1625 &self,
1626 _locked: &mut Locked<FileOpsCore>,
1627 _node: &FsNode,
1628 _current_task: &CurrentTask,
1629 ) -> Result<SymlinkTarget, Errno> {
1630 Ok(SymlinkTarget::Path(
1631 self.zxio().read_link().map_err(|status| from_status_like_fdio!(status))?.into(),
1632 ))
1633 }
1634
1635 fn fetch_and_refresh_info<'a>(
1636 &self,
1637 _locked: &mut Locked<FileOpsCore>,
1638 _node: &FsNode,
1639 _current_task: &CurrentTask,
1640 info: &'a RwLock<FsNodeInfo>,
1641 ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
1642 fetch_and_refresh_info_impl(&self.zxio(), info)
1643 }
1644
1645 fn forget(
1646 self: Box<Self>,
1647 _locked: &mut Locked<FileOpsCore>,
1648 _current_task: &CurrentTask,
1649 info: FsNodeInfo,
1650 ) -> Result<(), Errno> {
1651 if info.pending_time_access_update {
1653 self.zxio()
1654 .close_and_update_access_time()
1655 .map_err(|status| from_status_like_fdio!(status))?;
1656 }
1657 Ok(())
1658 }
1659}
1660
1661pub struct RemoteCounter {
1662 counter: Counter,
1663}
1664
1665impl RemoteCounter {
1666 fn new(counter: Counter) -> Self {
1667 Self { counter }
1668 }
1669
1670 pub fn duplicate_handle(&self) -> Result<Counter, Errno> {
1671 self.counter.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)
1672 }
1673}
1674
1675impl FileOps for RemoteCounter {
1676 fileops_impl_nonseekable!();
1677 fileops_impl_noop_sync!();
1678
1679 fn read(
1680 &self,
1681 _locked: &mut Locked<FileOpsCore>,
1682 _file: &FileObject,
1683 _current_task: &CurrentTask,
1684 _offset: usize,
1685 _data: &mut dyn OutputBuffer,
1686 ) -> Result<usize, Errno> {
1687 error!(ENOTSUP)
1688 }
1689
1690 fn write(
1691 &self,
1692 _locked: &mut Locked<FileOpsCore>,
1693 _file: &FileObject,
1694 _current_task: &CurrentTask,
1695 _offset: usize,
1696 _data: &mut dyn InputBuffer,
1697 ) -> Result<usize, Errno> {
1698 error!(ENOTSUP)
1699 }
1700
1701 fn ioctl(
1702 &self,
1703 locked: &mut Locked<Unlocked>,
1704 file: &FileObject,
1705 current_task: &CurrentTask,
1706 request: u32,
1707 arg: SyscallArg,
1708 ) -> Result<SyscallResult, Errno> {
1709 let ioctl_type = (request >> 8) as u8;
1710 let ioctl_number = request as u8;
1711 if ioctl_type == SYNC_IOC_MAGIC
1712 && (ioctl_number == SYNC_IOC_FILE_INFO || ioctl_number == SYNC_IOC_MERGE)
1713 {
1714 let mut sync_points: Vec<SyncPoint> = vec![];
1715 let counter = self.duplicate_handle()?;
1716 sync_points.push(SyncPoint::new(Timeline::Hwc, counter.into()));
1717 let sync_file_name: &[u8; 32] = b"remote counter\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
1718 let sync_file = SyncFile::new(*sync_file_name, SyncFence { sync_points });
1719 return sync_file.ioctl(locked, file, current_task, request, arg);
1720 }
1721
1722 error!(EINVAL)
1723 }
1724}
1725
1726#[cfg(test)]
1727mod test {
1728 use super::*;
1729 use crate::mm::PAGE_SIZE;
1730 use crate::testing::*;
1731 use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
1732 use crate::vfs::socket::{SocketFile, SocketMessageFlags};
1733 use crate::vfs::{EpollFileObject, LookupContext, Namespace, SymlinkMode, TimeUpdateType};
1734 use assert_matches::assert_matches;
1735 use fidl_fuchsia_io as fio;
1736 use flyweights::FlyByteStr;
1737 use fxfs_testing::{TestFixture, TestFixtureOptions};
1738 use starnix_uapi::auth::Credentials;
1739 use starnix_uapi::errors::EINVAL;
1740 use starnix_uapi::file_mode::{AccessCheck, mode};
1741 use starnix_uapi::open_flags::OpenFlags;
1742 use starnix_uapi::vfs::{EpollEvent, FdEvents};
1743 use zx::HandleBased;
1744
1745 #[::fuchsia::test]
1746 async fn test_remote_uds() {
1747 spawn_kernel_and_run(async |locked, current_task| {
1748 let (s1, s2) = zx::Socket::create_datagram();
1749 s2.write(&vec![0]).expect("write");
1750 let file = new_remote_file(locked, ¤t_task, s1.into(), OpenFlags::RDWR)
1751 .expect("new_remote_file");
1752 assert!(file.node().is_sock());
1753 let socket_ops = file.downcast_file::<SocketFile>().unwrap();
1754 let flags = SocketMessageFlags::CTRUNC
1755 | SocketMessageFlags::TRUNC
1756 | SocketMessageFlags::NOSIGNAL
1757 | SocketMessageFlags::CMSG_CLOEXEC;
1758 let mut buffer = VecOutputBuffer::new(1024);
1759 let info = socket_ops
1760 .recvmsg(locked, ¤t_task, &file, &mut buffer, flags, None)
1761 .expect("recvmsg");
1762 assert!(info.ancillary_data.is_empty());
1763 assert_eq!(info.message_length, 1);
1764 })
1765 .await;
1766 }
1767
1768 #[::fuchsia::test]
1769 async fn test_tree() {
1770 spawn_kernel_and_run(async |locked, current_task| {
1771 let kernel = current_task.kernel();
1772 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
1773 let (server, client) = zx::Channel::create();
1774 fdio::open("/pkg", rights, server).expect("failed to open /pkg");
1775 let fs = RemoteFs::new_fs(
1776 locked,
1777 &kernel,
1778 client,
1779 FileSystemOptions { source: FlyByteStr::new(b"/pkg"), ..Default::default() },
1780 rights,
1781 )
1782 .unwrap();
1783 let ns = Namespace::new(fs);
1784 let root = ns.root();
1785 let mut context = LookupContext::default();
1786 assert_eq!(
1787 root.lookup_child(locked, ¤t_task, &mut context, "nib".into()).err(),
1788 Some(errno!(ENOENT))
1789 );
1790 let mut context = LookupContext::default();
1791 root.lookup_child(locked, ¤t_task, &mut context, "lib".into()).unwrap();
1792
1793 let mut context = LookupContext::default();
1794 let _test_file = root
1795 .lookup_child(
1796 locked,
1797 ¤t_task,
1798 &mut context,
1799 "data/tests/hello_starnix".into(),
1800 )
1801 .unwrap()
1802 .open(locked, ¤t_task, OpenFlags::RDONLY, AccessCheck::default())
1803 .unwrap();
1804 })
1805 .await;
1806 }
1807
1808 #[::fuchsia::test]
1809 async fn test_blocking_io() {
1810 spawn_kernel_and_run(async |locked, current_task| {
1811 let (client, server) = zx::Socket::create_stream();
1812 let pipe = create_fuchsia_pipe(locked, ¤t_task, client, OpenFlags::RDWR).unwrap();
1813
1814 let bytes = [0u8; 64];
1815 assert_eq!(bytes.len(), server.write(&bytes).unwrap());
1816
1817 let bytes_read =
1819 pipe.read(locked, ¤t_task, &mut VecOutputBuffer::new(64)).unwrap();
1820
1821 assert_eq!(bytes_read, bytes.len());
1822 })
1823 .await;
1824 }
1825
1826 #[::fuchsia::test]
1827 async fn test_poll() {
1828 spawn_kernel_and_run(async |locked, current_task| {
1829 let (client, server) = zx::Socket::create_stream();
1830 let pipe = create_fuchsia_pipe(locked, ¤t_task, client, OpenFlags::RDWR)
1831 .expect("create_fuchsia_pipe");
1832 let server_zxio = Zxio::create(server.into_handle()).expect("Zxio::create");
1833
1834 assert_eq!(
1835 pipe.query_events(locked, ¤t_task),
1836 Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
1837 );
1838
1839 let epoll_object = EpollFileObject::new_file(locked, ¤t_task);
1840 let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
1841 let event = EpollEvent::new(FdEvents::POLLIN, 0);
1842 epoll_file
1843 .add(locked, ¤t_task, &pipe, &epoll_object, event)
1844 .expect("poll_file.add");
1845
1846 let fds = epoll_file
1847 .wait(locked, ¤t_task, 1, zx::MonotonicInstant::ZERO)
1848 .expect("wait");
1849 assert!(fds.is_empty());
1850
1851 assert_eq!(server_zxio.write(&[0]).expect("write"), 1);
1852
1853 assert_eq!(
1854 pipe.query_events(locked, ¤t_task),
1855 Ok(FdEvents::POLLOUT
1856 | FdEvents::POLLWRNORM
1857 | FdEvents::POLLIN
1858 | FdEvents::POLLRDNORM)
1859 );
1860 let fds = epoll_file
1861 .wait(locked, ¤t_task, 1, zx::MonotonicInstant::ZERO)
1862 .expect("wait");
1863 assert_eq!(fds.len(), 1);
1864
1865 assert_eq!(
1866 pipe.read(locked, ¤t_task, &mut VecOutputBuffer::new(64)).expect("read"),
1867 1
1868 );
1869
1870 assert_eq!(
1871 pipe.query_events(locked, ¤t_task),
1872 Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
1873 );
1874 let fds = epoll_file
1875 .wait(locked, ¤t_task, 1, zx::MonotonicInstant::ZERO)
1876 .expect("wait");
1877 assert!(fds.is_empty());
1878 })
1879 .await;
1880 }
1881
1882 #[::fuchsia::test]
1883 async fn test_new_remote_directory() {
1884 spawn_kernel_and_run(async |locked, current_task| {
1885 let (server, client) = zx::Channel::create();
1886 fdio::open("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE, server)
1887 .expect("failed to open /pkg");
1888
1889 let fd = new_remote_file(locked, ¤t_task, client.into(), OpenFlags::RDWR)
1890 .expect("new_remote_file");
1891 assert!(fd.node().is_dir());
1892 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
1893 })
1894 .await;
1895 }
1896
1897 #[::fuchsia::test]
1898 async fn test_new_remote_file() {
1899 spawn_kernel_and_run(async |locked, current_task| {
1900 let (server, client) = zx::Channel::create();
1901 fdio::open("/pkg/meta/contents", fio::PERM_READABLE, server)
1902 .expect("failed to open /pkg/meta/contents");
1903
1904 let fd = new_remote_file(locked, ¤t_task, client.into(), OpenFlags::RDONLY)
1905 .expect("new_remote_file");
1906 assert!(!fd.node().is_dir());
1907 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
1908 })
1909 .await;
1910 }
1911
1912 #[::fuchsia::test]
1913 async fn test_new_remote_counter() {
1914 spawn_kernel_and_run(async |locked, current_task| {
1915 let counter = zx::Counter::create();
1916
1917 let fd = new_remote_file(locked, ¤t_task, counter.into(), OpenFlags::RDONLY)
1918 .expect("new_remote_file");
1919 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
1920 })
1921 .await;
1922 }
1923
1924 #[::fuchsia::test]
1925 async fn test_new_remote_vmo() {
1926 spawn_kernel_and_run(async |locked, current_task| {
1927 let vmo = zx::Vmo::create(*PAGE_SIZE).expect("Vmo::create");
1928 let fd = new_remote_file(locked, ¤t_task, vmo.into(), OpenFlags::RDWR)
1929 .expect("new_remote_file");
1930 assert!(!fd.node().is_dir());
1931 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
1932 })
1933 .await;
1934 }
1935
1936 #[::fuchsia::test(threads = 2)]
1937 async fn test_symlink() {
1938 let fixture = TestFixture::new().await;
1939 let (server, client) = zx::Channel::create();
1940 fixture.root().clone(server.into()).expect("clone failed");
1941
1942 const LINK_PATH: &'static str = "symlink";
1943 const LINK_TARGET: &'static str = "私は「UTF8」です";
1944 const LINK_SIZE: usize = 22;
1947 assert_eq!(LINK_SIZE, LINK_TARGET.len());
1948
1949 spawn_kernel_and_run(async move |locked, current_task| {
1950 let kernel = current_task.kernel();
1951 let fs = RemoteFs::new_fs(
1952 locked,
1953 &kernel,
1954 client,
1955 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
1956 fio::PERM_READABLE | fio::PERM_WRITABLE,
1957 )
1958 .expect("new_fs failed");
1959 let ns = Namespace::new(fs);
1960 let root = ns.root();
1961 let symlink_node = root
1962 .create_symlink(locked, ¤t_task, LINK_PATH.into(), LINK_TARGET.into())
1963 .expect("symlink failed");
1964 assert_matches!(&*symlink_node.entry.node.info(), FsNodeInfo { size: LINK_SIZE, .. });
1965
1966 let mut context = LookupContext::new(SymlinkMode::NoFollow);
1967 let child = root
1968 .lookup_child(locked, ¤t_task, &mut context, "symlink".into())
1969 .expect("lookup_child failed");
1970
1971 match child.readlink(locked, ¤t_task).expect("readlink failed") {
1972 SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
1973 SymlinkTarget::Node(_) => panic!("readlink returned SymlinkTarget::Node"),
1974 }
1975 let stat_result = child.entry.node.stat(locked, ¤t_task).expect("stat failed");
1977 assert_eq!(stat_result.st_size as usize, LINK_SIZE);
1978 })
1979 .await;
1980
1981 let fixture = TestFixture::open(
1983 fixture.close().await,
1984 TestFixtureOptions { format: false, ..Default::default() },
1985 )
1986 .await;
1987 let (server, client) = zx::Channel::create();
1988 fixture.root().clone(server.into()).expect("clone failed after remount");
1989
1990 spawn_kernel_and_run(async move |locked, current_task| {
1991 let kernel = current_task.kernel();
1992 let fs = RemoteFs::new_fs(
1993 locked,
1994 &kernel,
1995 client,
1996 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
1997 fio::PERM_READABLE | fio::PERM_WRITABLE,
1998 )
1999 .expect("new_fs failed after remount");
2000 let ns = Namespace::new(fs);
2001 let root = ns.root();
2002 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2003 let child = root
2004 .lookup_child(locked, ¤t_task, &mut context, "symlink".into())
2005 .expect("lookup_child failed after remount");
2006
2007 match child.readlink(locked, ¤t_task).expect("readlink failed after remount") {
2008 SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2009 SymlinkTarget::Node(_) => {
2010 panic!("readlink returned SymlinkTarget::Node after remount")
2011 }
2012 }
2013 let stat_result =
2015 child.entry.node.stat(locked, ¤t_task).expect("stat failed after remount");
2016 assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2017 })
2018 .await;
2019
2020 fixture.close().await;
2021 }
2022
2023 #[::fuchsia::test]
2024 async fn test_mode_uid_gid_and_dev_persists() {
2025 const FILE_MODE: FileMode = mode!(IFREG, 0o467);
2026 const DIR_MODE: FileMode = mode!(IFDIR, 0o647);
2027 const BLK_MODE: FileMode = mode!(IFBLK, 0o746);
2028
2029 let fixture = TestFixture::new().await;
2030 let (server, client) = zx::Channel::create();
2031 fixture.root().clone(server.into()).expect("clone failed");
2032
2033 spawn_kernel_and_run(async move |locked, current_task| {
2035 let kernel = current_task.kernel();
2036 current_task.set_creds(Credentials {
2037 euid: 1,
2038 fsuid: 1,
2039 egid: 2,
2040 fsgid: 2,
2041 ..current_task.current_creds()
2042 });
2043 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2044 let fs = RemoteFs::new_fs(
2045 locked,
2046 &kernel,
2047 client,
2048 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2049 rights,
2050 )
2051 .expect("new_fs failed");
2052 let ns = Namespace::new(fs);
2053 current_task.fs().set_umask(FileMode::from_bits(0));
2054 ns.root()
2055 .create_node(locked, ¤t_task, "file".into(), FILE_MODE, DeviceType::NONE)
2056 .expect("create_node failed");
2057 ns.root()
2058 .create_node(locked, ¤t_task, "dir".into(), DIR_MODE, DeviceType::NONE)
2059 .expect("create_node failed");
2060 ns.root()
2061 .create_node(locked, ¤t_task, "dev".into(), BLK_MODE, DeviceType::RANDOM)
2062 .expect("create_node failed");
2063 })
2064 .await;
2065
2066 let fixture = TestFixture::open(
2068 fixture.close().await,
2069 TestFixtureOptions { format: false, ..Default::default() },
2070 )
2071 .await;
2072
2073 let (server, client) = zx::Channel::create();
2074 fixture.root().clone(server.into()).expect("clone failed");
2075
2076 spawn_kernel_and_run(async move |locked, current_task| {
2077 let kernel = current_task.kernel();
2078 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2079 let fs = RemoteFs::new_fs(
2080 locked,
2081 &kernel,
2082 client,
2083 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2084 rights,
2085 )
2086 .expect("new_fs failed");
2087 let ns = Namespace::new(fs);
2088 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2089 let child = ns
2090 .root()
2091 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2092 .expect("lookup_child failed");
2093 assert_matches!(
2094 &*child.entry.node.info(),
2095 FsNodeInfo { mode: FILE_MODE, uid: 1, gid: 2, rdev: DeviceType::NONE, .. }
2096 );
2097 let child = ns
2098 .root()
2099 .lookup_child(locked, ¤t_task, &mut context, "dir".into())
2100 .expect("lookup_child failed");
2101 assert_matches!(
2102 &*child.entry.node.info(),
2103 FsNodeInfo { mode: DIR_MODE, uid: 1, gid: 2, rdev: DeviceType::NONE, .. }
2104 );
2105 let child = ns
2106 .root()
2107 .lookup_child(locked, ¤t_task, &mut context, "dev".into())
2108 .expect("lookup_child failed");
2109 assert_matches!(
2110 &*child.entry.node.info(),
2111 FsNodeInfo { mode: BLK_MODE, uid: 1, gid: 2, rdev: DeviceType::RANDOM, .. }
2112 );
2113 })
2114 .await;
2115 fixture.close().await;
2116 }
2117
2118 #[::fuchsia::test]
2119 async fn test_dot_dot_inode_numbers() {
2120 let fixture = TestFixture::new().await;
2121 let (server, client) = zx::Channel::create();
2122 fixture.root().clone(server.into()).expect("clone failed");
2123
2124 const MODE: FileMode = FileMode::from_bits(FileMode::IFDIR.bits() | 0o777);
2125
2126 spawn_kernel_and_run(async |locked, current_task| {
2127 let kernel = current_task.kernel();
2128 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2129 let fs = RemoteFs::new_fs(
2130 locked,
2131 &kernel,
2132 client,
2133 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2134 rights,
2135 )
2136 .expect("new_fs failed");
2137 let ns = Namespace::new(fs);
2138 current_task.fs().set_umask(FileMode::from_bits(0));
2139 let sub_dir1 = ns
2140 .root()
2141 .create_node(locked, ¤t_task, "dir".into(), MODE, DeviceType::NONE)
2142 .expect("create_node failed");
2143 let sub_dir2 = sub_dir1
2144 .create_node(locked, ¤t_task, "dir".into(), MODE, DeviceType::NONE)
2145 .expect("create_node failed");
2146
2147 let dir_handle = ns
2148 .root()
2149 .entry
2150 .open_anonymous(locked, ¤t_task, OpenFlags::RDONLY)
2151 .expect("open failed");
2152
2153 #[derive(Default)]
2154 struct Sink {
2155 offset: off_t,
2156 dot_dot_inode_num: u64,
2157 }
2158 impl DirentSink for Sink {
2159 fn add(
2160 &mut self,
2161 inode_num: ino_t,
2162 offset: off_t,
2163 entry_type: DirectoryEntryType,
2164 name: &FsStr,
2165 ) -> Result<(), Errno> {
2166 if name == ".." {
2167 self.dot_dot_inode_num = inode_num;
2168 assert_eq!(entry_type, DirectoryEntryType::DIR);
2169 }
2170 self.offset = offset;
2171 Ok(())
2172 }
2173 fn offset(&self) -> off_t {
2174 self.offset
2175 }
2176 }
2177 let mut sink = Sink::default();
2178 dir_handle.readdir(locked, ¤t_task, &mut sink).expect("readdir failed");
2179
2180 assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2182
2183 let dir_handle = sub_dir1
2184 .entry
2185 .open_anonymous(locked, ¤t_task, OpenFlags::RDONLY)
2186 .expect("open failed");
2187 let mut sink = Sink::default();
2188 dir_handle.readdir(locked, ¤t_task, &mut sink).expect("readdir failed");
2189
2190 assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2192
2193 let dir_handle = sub_dir2
2194 .entry
2195 .open_anonymous(locked, ¤t_task, OpenFlags::RDONLY)
2196 .expect("open failed");
2197 let mut sink = Sink::default();
2198 dir_handle.readdir(locked, ¤t_task, &mut sink).expect("readdir failed");
2199
2200 assert_eq!(sink.dot_dot_inode_num, sub_dir1.entry.node.ino);
2202 })
2203 .await;
2204 fixture.close().await;
2205 }
2206
2207 #[::fuchsia::test]
2208 async fn test_remote_special_node() {
2209 let fixture = TestFixture::new().await;
2210 let (server, client) = zx::Channel::create();
2211 fixture.root().clone(server.into()).expect("clone failed");
2212
2213 const FIFO_MODE: FileMode = FileMode::from_bits(FileMode::IFIFO.bits() | 0o777);
2214 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2215
2216 spawn_kernel_and_run(async |locked, current_task| {
2217 let kernel = current_task.kernel();
2218 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2219 let fs = RemoteFs::new_fs(
2220 locked,
2221 &kernel,
2222 client,
2223 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2224 rights,
2225 )
2226 .expect("new_fs failed");
2227 let ns = Namespace::new(fs);
2228 current_task.fs().set_umask(FileMode::from_bits(0));
2229 let root = ns.root();
2230
2231 root.create_node(locked, ¤t_task, "fifo".into(), FIFO_MODE, DeviceType::NONE)
2233 .expect("create_node failed");
2234 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2235 let fifo_node = root
2236 .lookup_child(locked, ¤t_task, &mut context, "fifo".into())
2237 .expect("lookup_child failed");
2238
2239 match fifo_node.truncate(locked, ¤t_task, 0) {
2242 Ok(_) => {
2243 panic!("truncate passed for special node")
2244 }
2245 Err(errno) if errno == EINVAL => {}
2246 Err(e) => {
2247 panic!("truncate failed with error {:?}", e)
2248 }
2249 };
2250
2251 root.create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceType::NONE)
2253 .expect("create_node failed");
2254 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2255 let reg_node = root
2256 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2257 .expect("lookup_child failed");
2258
2259 reg_node.truncate(locked, ¤t_task, 0).expect("truncate failed");
2261 })
2262 .await;
2263 fixture.close().await;
2264 }
2265
2266 #[::fuchsia::test]
2267 async fn test_hard_link() {
2268 let fixture = TestFixture::new().await;
2269 let (server, client) = zx::Channel::create();
2270 fixture.root().clone(server.into()).expect("clone failed");
2271
2272 spawn_kernel_and_run(async move |locked, current_task| {
2273 let kernel = current_task.kernel();
2274 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2275 let fs = RemoteFs::new_fs(
2276 locked,
2277 &kernel,
2278 client,
2279 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2280 rights,
2281 )
2282 .expect("new_fs failed");
2283 let ns = Namespace::new(fs);
2284 current_task.fs().set_umask(FileMode::from_bits(0));
2285 let node = ns
2286 .root()
2287 .create_node(
2288 locked,
2289 ¤t_task,
2290 "file1".into(),
2291 mode!(IFREG, 0o666),
2292 DeviceType::NONE,
2293 )
2294 .expect("create_node failed");
2295 ns.root()
2296 .entry
2297 .node
2298 .link(locked, ¤t_task, &ns.root().mount, "file2".into(), &node.entry.node)
2299 .expect("link failed");
2300 })
2301 .await;
2302
2303 let fixture = TestFixture::open(
2304 fixture.close().await,
2305 TestFixtureOptions { format: false, ..Default::default() },
2306 )
2307 .await;
2308
2309 let (server, client) = zx::Channel::create();
2310 fixture.root().clone(server.into()).expect("clone failed");
2311
2312 spawn_kernel_and_run(async move |locked, current_task| {
2313 let kernel = current_task.kernel();
2314 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2315 let fs = RemoteFs::new_fs(
2316 locked,
2317 &kernel,
2318 client,
2319 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2320 rights,
2321 )
2322 .expect("new_fs failed");
2323 let ns = Namespace::new(fs);
2324 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2325 let child1 = ns
2326 .root()
2327 .lookup_child(locked, ¤t_task, &mut context, "file1".into())
2328 .expect("lookup_child failed");
2329 let child2 = ns
2330 .root()
2331 .lookup_child(locked, ¤t_task, &mut context, "file2".into())
2332 .expect("lookup_child failed");
2333 assert!(Arc::ptr_eq(&child1.entry.node, &child2.entry.node));
2334 })
2335 .await;
2336 fixture.close().await;
2337 }
2338
2339 #[::fuchsia::test]
2340 async fn test_lookup_on_fsverity_enabled_file() {
2341 let fixture = TestFixture::new().await;
2342 let (server, client) = zx::Channel::create();
2343 fixture.root().clone(server.into()).expect("clone failed");
2344
2345 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2346
2347 spawn_kernel_and_run(async move |locked, current_task| {
2348 let kernel = current_task.kernel();
2349 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2350 let fs = RemoteFs::new_fs(
2351 locked,
2352 &kernel,
2353 client,
2354 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2355 rights,
2356 )
2357 .expect("new_fs failed");
2358 let ns = Namespace::new(fs);
2359 current_task.fs().set_umask(FileMode::from_bits(0));
2360 let file = ns
2361 .root()
2362 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceType::NONE)
2363 .expect("create_node failed");
2364 let desc = fsverity_descriptor {
2366 version: 1,
2367 hash_algorithm: 1,
2368 salt_size: 32,
2369 log_blocksize: 12,
2370 ..Default::default()
2371 };
2372 file.entry.node.ops().enable_fsverity(&desc).expect("enable fsverity failed");
2373 })
2374 .await;
2375
2376 let fixture = TestFixture::open(
2379 fixture.close().await,
2380 TestFixtureOptions { format: false, ..Default::default() },
2381 )
2382 .await;
2383 let (server, client) = zx::Channel::create();
2384 fixture.root().clone(server.into()).expect("clone failed");
2385
2386 spawn_kernel_and_run(async move |locked, current_task| {
2387 let kernel = current_task.kernel();
2388 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2389 let fs = RemoteFs::new_fs(
2390 locked,
2391 &kernel,
2392 client,
2393 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2394 rights,
2395 )
2396 .expect("new_fs failed");
2397 let ns = Namespace::new(fs);
2398 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2399 let _child = ns
2400 .root()
2401 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2402 .expect("lookup_child failed");
2403 })
2404 .await;
2405 fixture.close().await;
2406 }
2407
2408 #[::fuchsia::test]
2409 async fn test_update_attributes_persists() {
2410 let fixture = TestFixture::new().await;
2411 let (server, client) = zx::Channel::create();
2412 fixture.root().clone(server.into()).expect("clone failed");
2413
2414 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2415
2416 spawn_kernel_and_run(async move |locked, current_task| {
2417 let kernel = current_task.kernel();
2418 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2419 let fs = RemoteFs::new_fs(
2420 locked,
2421 &kernel,
2422 client,
2423 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2424 rights,
2425 )
2426 .expect("new_fs failed");
2427 let ns = Namespace::new(fs);
2428 current_task.fs().set_umask(FileMode::from_bits(0));
2429 let file = ns
2430 .root()
2431 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceType::NONE)
2432 .expect("create_node failed");
2433 file.entry
2435 .node
2436 .chmod(locked, ¤t_task, &file.mount, MODE | FileMode::ALLOW_ALL)
2437 .expect("chmod failed");
2438 })
2439 .await;
2440
2441 let fixture = TestFixture::open(
2443 fixture.close().await,
2444 TestFixtureOptions { format: false, ..Default::default() },
2445 )
2446 .await;
2447 let (server, client) = zx::Channel::create();
2448 fixture.root().clone(server.into()).expect("clone failed");
2449
2450 spawn_kernel_and_run(async move |locked, current_task| {
2451 let kernel = current_task.kernel();
2452 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2453 let fs = RemoteFs::new_fs(
2454 locked,
2455 &kernel,
2456 client,
2457 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2458 rights,
2459 )
2460 .expect("new_fs failed");
2461 let ns = Namespace::new(fs);
2462 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2463 let child = ns
2464 .root()
2465 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2466 .expect("lookup_child failed");
2467 assert_eq!(child.entry.node.info().mode, MODE | FileMode::ALLOW_ALL);
2468 })
2469 .await;
2470 fixture.close().await;
2471 }
2472
2473 #[::fuchsia::test]
2474 async fn test_statfs() {
2475 let fixture = TestFixture::new().await;
2476 let (server, client) = zx::Channel::create();
2477 fixture.root().clone(server.into()).expect("clone failed");
2478
2479 spawn_kernel_and_run(async move |locked, current_task| {
2480 let kernel = current_task.kernel();
2481 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2482 let fs = RemoteFs::new_fs(
2483 locked,
2484 &kernel,
2485 client,
2486 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2487 rights,
2488 )
2489 .expect("new_fs failed");
2490
2491 let statfs = fs.statfs(locked, ¤t_task).expect("statfs failed");
2492 assert!(statfs.f_type != 0);
2493 assert!(statfs.f_bsize > 0);
2494 assert!(statfs.f_blocks > 0);
2495 assert!(statfs.f_bfree > 0 && statfs.f_bfree <= statfs.f_blocks);
2496 assert!(statfs.f_files > 0);
2497 assert!(statfs.f_ffree > 0 && statfs.f_ffree <= statfs.f_files);
2498 assert!(statfs.f_fsid.val[0] != 0 || statfs.f_fsid.val[1] != 0);
2499 assert!(statfs.f_namelen > 0);
2500 assert!(statfs.f_frsize > 0);
2501 })
2502 .await;
2503
2504 fixture.close().await;
2505 }
2506
2507 #[::fuchsia::test]
2508 async fn test_allocate() {
2509 let fixture = TestFixture::new().await;
2510 let (server, client) = zx::Channel::create();
2511 fixture.root().clone(server.into()).expect("clone failed");
2512
2513 spawn_kernel_and_run(async move |locked, current_task| {
2514 let kernel = current_task.kernel();
2515 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2516 let fs = RemoteFs::new_fs(
2517 locked,
2518 &kernel,
2519 client,
2520 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2521 rights,
2522 )
2523 .expect("new_fs failed");
2524 let ns = Namespace::new(fs);
2525 current_task.fs().set_umask(FileMode::from_bits(0));
2526 let root = ns.root();
2527
2528 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2529 root.create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceType::NONE)
2530 .expect("create_node failed");
2531 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2532 let reg_node = root
2533 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2534 .expect("lookup_child failed");
2535
2536 reg_node
2537 .entry
2538 .node
2539 .fallocate(locked, ¤t_task, FallocMode::Allocate { keep_size: false }, 0, 20)
2540 .expect("truncate failed");
2541 })
2542 .await;
2543 fixture.close().await;
2544 }
2545
2546 #[::fuchsia::test]
2547 async fn test_allocate_overflow() {
2548 let fixture = TestFixture::new().await;
2549 let (server, client) = zx::Channel::create();
2550 fixture.root().clone(server.into()).expect("clone failed");
2551
2552 spawn_kernel_and_run(async move |locked, current_task| {
2553 let kernel = current_task.kernel();
2554 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2555 let fs = RemoteFs::new_fs(
2556 locked,
2557 &kernel,
2558 client,
2559 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2560 rights,
2561 )
2562 .expect("new_fs failed");
2563 let ns = Namespace::new(fs);
2564 current_task.fs().set_umask(FileMode::from_bits(0));
2565 let root = ns.root();
2566
2567 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2568 root.create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceType::NONE)
2569 .expect("create_node failed");
2570 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2571 let reg_node = root
2572 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2573 .expect("lookup_child failed");
2574
2575 reg_node
2576 .entry
2577 .node
2578 .fallocate(
2579 locked,
2580 ¤t_task,
2581 FallocMode::Allocate { keep_size: false },
2582 1,
2583 u64::MAX,
2584 )
2585 .expect_err("truncate unexpectedly passed");
2586 })
2587 .await;
2588 fixture.close().await;
2589 }
2590
2591 #[::fuchsia::test]
2592 async fn test_time_modify_persists() {
2593 let fixture = TestFixture::new().await;
2594 let (server, client) = zx::Channel::create();
2595 fixture.root().clone(server.into()).expect("clone failed");
2596
2597 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2598
2599 let last_modified = spawn_kernel_and_run(async move |locked, current_task| {
2600 let kernel = current_task.kernel();
2601 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2602 let fs = RemoteFs::new_fs(
2603 locked,
2604 &kernel,
2605 client,
2606 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2607 rights,
2608 )
2609 .expect("new_fs failed");
2610 let ns: Arc<Namespace> = Namespace::new(fs);
2611 current_task.fs().set_umask(FileMode::from_bits(0));
2612 let child = ns
2613 .root()
2614 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceType::NONE)
2615 .expect("create_node failed");
2616 let file = child
2618 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
2619 .expect("open failed");
2620 let time_before_write = child
2623 .entry
2624 .node
2625 .fetch_and_refresh_info(locked, ¤t_task)
2626 .expect("fetch_and_refresh_info failed")
2627 .time_modify;
2628 let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
2629 let written = file
2630 .write(locked, ¤t_task, &mut VecInputBuffer::new(&write_bytes))
2631 .expect("write failed");
2632 assert_eq!(written, write_bytes.len());
2633 let last_modified = child
2634 .entry
2635 .node
2636 .fetch_and_refresh_info(locked, ¤t_task)
2637 .expect("fetch_and_refresh_info failed")
2638 .time_modify;
2639 assert!(last_modified > time_before_write);
2640 last_modified
2641 })
2642 .await;
2643
2644 let fixture = TestFixture::open(
2647 fixture.close().await,
2648 TestFixtureOptions { format: false, ..Default::default() },
2649 )
2650 .await;
2651 let (server, client) = zx::Channel::create();
2652 fixture.root().clone(server.into()).expect("clone failed");
2653 let refreshed_modified_time = spawn_kernel_and_run(async move |locked, current_task| {
2654 let kernel = current_task.kernel();
2655 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2656 let fs = RemoteFs::new_fs(
2657 locked,
2658 &kernel,
2659 client,
2660 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2661 rights,
2662 )
2663 .expect("new_fs failed");
2664 let ns = Namespace::new(fs);
2665 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2666 let child = ns
2667 .root()
2668 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2669 .expect("lookup_child failed");
2670 let last_modified = child
2671 .entry
2672 .node
2673 .fetch_and_refresh_info(locked, ¤t_task)
2674 .expect("fetch_and_refresh_info failed")
2675 .time_modify;
2676 last_modified
2677 })
2678 .await;
2679 assert_eq!(last_modified, refreshed_modified_time);
2680
2681 fixture.close().await;
2682 }
2683
2684 #[::fuchsia::test]
2685 async fn test_update_atime_mtime() {
2686 let fixture = TestFixture::new().await;
2687 let (server, client) = zx::Channel::create();
2688 fixture.root().clone(server.into()).expect("clone failed");
2689
2690 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2691
2692 spawn_kernel_and_run(async move |locked, current_task| {
2693 let kernel = current_task.kernel();
2694 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2695 let fs = RemoteFs::new_fs(
2696 locked,
2697 &kernel,
2698 client,
2699 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2700 rights,
2701 )
2702 .expect("new_fs failed");
2703 let ns: Arc<Namespace> = Namespace::new(fs);
2704 current_task.fs().set_umask(FileMode::from_bits(0));
2705 let child = ns
2706 .root()
2707 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceType::NONE)
2708 .expect("create_node failed");
2709
2710 let info_original = child
2711 .entry
2712 .node
2713 .fetch_and_refresh_info(locked, ¤t_task)
2714 .expect("fetch_and_refresh_info failed")
2715 .clone();
2716
2717 child
2718 .entry
2719 .node
2720 .update_atime_mtime(
2721 locked,
2722 ¤t_task,
2723 &child.mount,
2724 TimeUpdateType::Time(UtcInstant::from_nanos(30)),
2725 TimeUpdateType::Omit,
2726 )
2727 .expect("update_atime_mtime failed");
2728 let info_after_update = child
2729 .entry
2730 .node
2731 .fetch_and_refresh_info(locked, ¤t_task)
2732 .expect("fetch_and_refresh_info failed")
2733 .clone();
2734 assert_eq!(info_after_update.time_modify, info_original.time_modify);
2735 assert_eq!(info_after_update.time_access, UtcInstant::from_nanos(30));
2736
2737 child
2738 .entry
2739 .node
2740 .update_atime_mtime(
2741 locked,
2742 ¤t_task,
2743 &child.mount,
2744 TimeUpdateType::Omit,
2745 TimeUpdateType::Time(UtcInstant::from_nanos(50)),
2746 )
2747 .expect("update_atime_mtime failed");
2748 let info_after_update2 = child
2749 .entry
2750 .node
2751 .fetch_and_refresh_info(locked, ¤t_task)
2752 .expect("fetch_and_refresh_info failed")
2753 .clone();
2754 assert_eq!(info_after_update2.time_modify, UtcInstant::from_nanos(50));
2755 assert_eq!(info_after_update2.time_access, UtcInstant::from_nanos(30));
2756 })
2757 .await;
2758 fixture.close().await;
2759 }
2760
2761 #[::fuchsia::test]
2762 async fn test_write_updates_mtime_ctime() {
2763 let fixture = TestFixture::new().await;
2764 let (server, client) = zx::Channel::create();
2765 fixture.root().clone(server.into()).expect("clone failed");
2766
2767 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2768
2769 spawn_kernel_and_run(async move |locked, current_task| {
2770 let kernel = current_task.kernel();
2771 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2772 let fs = RemoteFs::new_fs(
2773 locked,
2774 &kernel,
2775 client,
2776 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2777 rights,
2778 )
2779 .expect("new_fs failed");
2780 let ns: Arc<Namespace> = Namespace::new(fs);
2781 current_task.fs().set_umask(FileMode::from_bits(0));
2782 let child = ns
2783 .root()
2784 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceType::NONE)
2785 .expect("create_node failed");
2786 let file = child
2787 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
2788 .expect("open failed");
2789 let (ctime_before_write, mtime_before_write) = {
2792 let info = child
2793 .entry
2794 .node
2795 .fetch_and_refresh_info(locked, ¤t_task)
2796 .expect("fetch_and_refresh_info failed");
2797 (info.time_status_change, info.time_modify)
2798 };
2799
2800 let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
2802 let written = file
2803 .write(locked, ¤t_task, &mut VecInputBuffer::new(&write_bytes))
2804 .expect("write failed");
2805 assert_eq!(written, write_bytes.len());
2806
2807 let (ctime_after_write_no_refresh, mtime_after_write_no_refresh) = {
2811 let info = child.entry.node.info();
2812 (info.time_status_change, info.time_modify)
2813 };
2814 assert_eq!(ctime_after_write_no_refresh, ctime_before_write);
2815 assert_eq!(mtime_after_write_no_refresh, mtime_before_write);
2816
2817 let (ctime_after_write_refresh, mtime_after_write_refresh) = {
2821 let info = child
2822 .entry
2823 .node
2824 .fetch_and_refresh_info(locked, ¤t_task)
2825 .expect("fetch_and_refresh_info failed");
2826 (info.time_status_change, info.time_modify)
2827 };
2828 assert_eq!(ctime_after_write_refresh, mtime_after_write_refresh);
2829 assert!(ctime_after_write_refresh > ctime_after_write_no_refresh);
2830 })
2831 .await;
2832 fixture.close().await;
2833 }
2834
2835 #[::fuchsia::test]
2836 async fn test_casefold_persists() {
2837 let fixture = TestFixture::new().await;
2838 let (server, client) = zx::Channel::create();
2839 fixture.root().clone(server.into()).expect("clone failed");
2840
2841 spawn_kernel_and_run(async move |locked, current_task| {
2842 let kernel = current_task.kernel();
2843 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2844 let fs = RemoteFs::new_fs(
2845 locked,
2846 &kernel,
2847 client,
2848 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2849 rights,
2850 )
2851 .expect("new_fs failed");
2852 let ns: Arc<Namespace> = Namespace::new(fs);
2853 let child = ns
2854 .root()
2855 .create_node(
2856 locked,
2857 ¤t_task,
2858 "dir".into(),
2859 FileMode::ALLOW_ALL.with_type(FileMode::IFDIR),
2860 DeviceType::NONE,
2861 )
2862 .expect("create_node failed");
2863 child
2864 .entry
2865 .node
2866 .update_attributes(locked, ¤t_task, |info| {
2867 info.casefold = true;
2868 Ok(())
2869 })
2870 .expect("enable casefold")
2871 })
2872 .await;
2873
2874 let fixture = TestFixture::open(
2876 fixture.close().await,
2877 TestFixtureOptions { format: false, ..Default::default() },
2878 )
2879 .await;
2880 let (server, client) = zx::Channel::create();
2881 fixture.root().clone(server.into()).expect("clone failed");
2882 let casefold = spawn_kernel_and_run(async move |locked, current_task| {
2883 let kernel = current_task.kernel();
2884 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2885 let fs = RemoteFs::new_fs(
2886 locked,
2887 &kernel,
2888 client,
2889 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2890 rights,
2891 )
2892 .expect("new_fs failed");
2893 let ns = Namespace::new(fs);
2894 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2895 let child = ns
2896 .root()
2897 .lookup_child(locked, ¤t_task, &mut context, "dir".into())
2898 .expect("lookup_child failed");
2899 let casefold = child
2900 .entry
2901 .node
2902 .fetch_and_refresh_info(locked, ¤t_task)
2903 .expect("fetch_and_refresh_info failed")
2904 .casefold;
2905 casefold
2906 })
2907 .await;
2908 assert!(casefold);
2909
2910 fixture.close().await;
2911 }
2912
2913 #[::fuchsia::test]
2914 async fn test_update_time_access_persists() {
2915 const TEST_FILE: &str = "test_file";
2916
2917 let fixture = TestFixture::new().await;
2918 let (server, client) = zx::Channel::create();
2919 fixture.root().clone(server.into()).expect("clone failed");
2920 let info_after_read = spawn_kernel_and_run(async move |locked, current_task| {
2922 let kernel = current_task.kernel();
2923 let fs = RemoteFs::new_fs(
2924 locked,
2925 &kernel,
2926 client,
2927 FileSystemOptions {
2928 source: FlyByteStr::new(b"/"),
2929 flags: MountFlags::RELATIME,
2930 ..Default::default()
2931 },
2932 fio::PERM_READABLE | fio::PERM_WRITABLE,
2933 )
2934 .expect("new_fs failed");
2935 let ns = Namespace::new_with_flags(fs, MountFlags::RELATIME);
2936 let child = ns
2937 .root()
2938 .open_create_node(
2939 locked,
2940 ¤t_task,
2941 TEST_FILE.into(),
2942 FileMode::ALLOW_ALL.with_type(FileMode::IFREG),
2943 DeviceType::NONE,
2944 OpenFlags::empty(),
2945 )
2946 .expect("create_node failed");
2947
2948 let file_handle = child
2949 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
2950 .expect("open failed");
2951
2952 file_handle
2955 .read(locked, ¤t_task, &mut VecOutputBuffer::new(10))
2956 .expect("read failed");
2957
2958 let info_after_read = child
2960 .entry
2961 .node
2962 .fetch_and_refresh_info(locked, ¤t_task)
2963 .expect("fetch_and_refresh_info failed")
2964 .clone();
2965
2966 info_after_read
2967 })
2968 .await;
2969
2970 let fixture = TestFixture::open(
2972 fixture.close().await,
2973 TestFixtureOptions { format: false, ..Default::default() },
2974 )
2975 .await;
2976
2977 let (server, client) = zx::Channel::create();
2978 fixture.root().clone(server.into()).expect("clone failed");
2979
2980 spawn_kernel_and_run(async move |locked, current_task| {
2981 let kernel = current_task.kernel();
2982 let fs = RemoteFs::new_fs(
2983 locked,
2984 &kernel,
2985 client,
2986 FileSystemOptions {
2987 source: FlyByteStr::new(b"/"),
2988 flags: MountFlags::RELATIME,
2989 ..Default::default()
2990 },
2991 fio::PERM_READABLE | fio::PERM_WRITABLE,
2992 )
2993 .expect("new_fs failed");
2994 let ns = Namespace::new_with_flags(fs, MountFlags::RELATIME);
2995 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2996 let child = ns
2997 .root()
2998 .lookup_child(locked, ¤t_task, &mut context, TEST_FILE.into())
2999 .expect("lookup_child failed");
3000
3001 let persisted_info = child
3004 .entry
3005 .node
3006 .fetch_and_refresh_info(locked, ¤t_task)
3007 .expect("fetch_and_refresh_info failed")
3008 .clone();
3009 assert_eq!(info_after_read.time_access, persisted_info.time_access);
3010 })
3011 .await;
3012 fixture.close().await;
3013 }
3014
3015 #[::fuchsia::test]
3016 async fn test_pending_access_time_updates() {
3017 const TEST_FILE: &str = "test_file";
3018
3019 let fixture = TestFixture::new().await;
3020 let (server, client) = zx::Channel::create();
3021 fixture.root().clone(server.into()).expect("clone failed");
3022
3023 spawn_kernel_and_run(async move |locked, current_task| {
3024 let kernel = current_task.kernel.clone();
3025 let fs = RemoteFs::new_fs(
3026 locked,
3027 &kernel,
3028 client,
3029 FileSystemOptions {
3030 source: FlyByteStr::new(b"/"),
3031 flags: MountFlags::RELATIME,
3032 ..Default::default()
3033 },
3034 fio::PERM_READABLE | fio::PERM_WRITABLE,
3035 )
3036 .expect("new_fs failed");
3037
3038 let ns = Namespace::new_with_flags(fs, MountFlags::RELATIME);
3039 let child = ns
3040 .root()
3041 .open_create_node(
3042 locked,
3043 ¤t_task,
3044 TEST_FILE.into(),
3045 FileMode::ALLOW_ALL.with_type(FileMode::IFREG),
3046 DeviceType::NONE,
3047 OpenFlags::empty(),
3048 )
3049 .expect("create_node failed");
3050
3051 let file_handle = child
3052 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
3053 .expect("open failed");
3054
3055 file_handle
3058 .read(locked, ¤t_task, &mut VecOutputBuffer::new(10))
3059 .expect("read failed");
3060
3061 let atime_after_first_read = child
3062 .entry
3063 .node
3064 .fetch_and_refresh_info(locked, ¤t_task)
3065 .expect("fetch_and_refresh_info failed")
3066 .time_access;
3067
3068 file_handle
3071 .read(locked, ¤t_task, &mut VecOutputBuffer::new(10))
3072 .expect("read failed");
3073
3074 let atime_after_second_read = child
3075 .entry
3076 .node
3077 .fetch_and_refresh_info(locked, ¤t_task)
3078 .expect("fetch_and_refresh_info failed")
3079 .time_access;
3080 assert_eq!(atime_after_first_read, atime_after_second_read);
3081
3082 let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3084 let _written = file_handle
3085 .write(locked, ¤t_task, &mut VecInputBuffer::new(&write_bytes))
3086 .expect("write failed");
3087
3088 file_handle
3090 .read(locked, ¤t_task, &mut VecOutputBuffer::new(10))
3091 .expect("read failed");
3092
3093 assert!(
3094 atime_after_second_read
3095 < child
3096 .entry
3097 .node
3098 .fetch_and_refresh_info(locked, ¤t_task)
3099 .expect("fetch_and_refresh_info failed")
3100 .time_access
3101 );
3102 })
3103 .await;
3104 fixture.close().await;
3105 }
3106}