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, Kernel};
12use crate::vfs::buffers::{InputBuffer, OutputBuffer, with_iovec_segments};
13use crate::vfs::file_server::serve_file_tagged;
14use crate::vfs::fs_args::MountParams;
15use crate::vfs::fsverity::FsVerityState;
16use crate::vfs::socket::{Socket, SocketFile, ZxioBackedSocket};
17use crate::vfs::{
18 Anon, AppendLockWriteGuard, CacheMode, DEFAULT_BYTES_PER_BLOCK, DirEntry, DirectoryEntryType,
19 DirentSink, FallocMode, FileHandle, FileObject, FileOps, FileSystem, FileSystemHandle,
20 FileSystemOps, FileSystemOptions, FsNode, FsNodeFlags, FsNodeHandle, FsNodeInfo, FsNodeOps,
21 FsStr, FsString, LookupVec, RenameContext, SeekTarget, StatxFlags, SymlinkTarget, XattrOp,
22 XattrStorage, default_seek, fileops_impl_directory, fileops_impl_nonseekable,
23 fileops_impl_noop_sync, fileops_impl_seekable, fs_node_impl_not_dir, fs_node_impl_symlink,
24 fs_node_impl_xattr_delegate,
25};
26use bstr::ByteSlice;
27use fidl::endpoints::DiscoverableProtocolMarker as _;
28use fidl_fuchsia_io as fio;
29use fidl_fuchsia_starnix_binder as fbinder;
30use fidl_fuchsia_unknown as funknown;
31use fuchsia_runtime::UtcInstant;
32use linux_uapi::SYNC_IOC_MAGIC;
33use once_cell::sync::OnceCell;
34use smallvec::{SmallVec, smallvec};
35use starnix_crypt::EncryptionKeyId;
36use starnix_logging::{CATEGORY_STARNIX_MM, impossible_error, log_warn, track_stub};
37use starnix_sync::{
38 DynamicLockDepRwLock, FuchsiaRemoteTargetLock, LockDepReadGuard, LockDepRwLock,
39 LockDepWriteGuard,
40};
41use starnix_syscalls::{SyscallArg, SyscallResult};
42use starnix_types::vfs::default_statfs;
43use starnix_uapi::auth::{Credentials, FsCred};
44use starnix_uapi::device_id::DeviceId;
45use starnix_uapi::errors::Errno;
46use starnix_uapi::file_mode::FileMode;
47use starnix_uapi::mount_flags::FileSystemFlags;
48use starnix_uapi::open_flags::OpenFlags;
49use starnix_uapi::{
50 __kernel_fsid_t, errno, error, from_status_like_fdio, fsverity_descriptor, mode, off_t, statfs,
51};
52use std::ops::ControlFlow;
53use std::sync::atomic::{AtomicU32, Ordering};
54use std::sync::{Arc, LazyLock};
55use sync_io_client::{RemoteIo, create_with_on_representation};
56use syncio::zxio::{
57 ZXIO_NODE_PROTOCOL_DIRECTORY, ZXIO_NODE_PROTOCOL_SYMLINK, ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET,
58 ZXIO_OBJECT_TYPE_NONE, ZXIO_OBJECT_TYPE_PACKET_SOCKET, ZXIO_OBJECT_TYPE_RAW_SOCKET,
59 ZXIO_OBJECT_TYPE_STREAM_SOCKET, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET, zxio_node_attr,
60};
61use syncio::{
62 AllocateMode, XattrSetMode, Zxio, zxio_fsverity_descriptor_t, zxio_node_attr_has_t,
63 zxio_node_attributes_t,
64};
65use zx::Counter;
66
67fn is_special(file_info: &fio::FileInfo) -> bool {
68 matches!(
69 file_info,
70 fio::FileInfo {
71 attributes:
72 Some(fio::NodeAttributes2 {
73 mutable_attributes: fio::MutableNodeAttributes { mode: Some(mode), .. },
74 ..
75 }),
76 ..
77 } if {
78 let mode = FileMode::from_bits(*mode);
79 mode.is_chr() || mode.is_blk() || mode.is_fifo() || mode.is_sock()
80 }
81 )
82}
83
84pub fn new_remote_fs(
85 current_task: &CurrentTask,
86 options: FileSystemOptions,
87) -> Result<FileSystemHandle, Errno> {
88 let kernel = current_task.kernel();
89 let requested_path = std::str::from_utf8(&options.source)
90 .map_err(|_| errno!(EINVAL, "source path is not utf8"))?;
91 let mut create_flags =
92 fio::PERM_READABLE | fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY;
93 if !options.flags.load(Ordering::Relaxed).contains(FileSystemFlags::RDONLY) {
94 create_flags |= fio::PERM_WRITABLE;
95 }
96 let (root_proxy, subdir) = kernel.open_ns_dir(requested_path, create_flags)?;
97
98 let subdir = if subdir.is_empty() { ".".to_string() } else { subdir };
99 let mut open_rights = fio::PERM_READABLE;
100 if !options.flags.load(Ordering::Relaxed).contains(FileSystemFlags::RDONLY) {
101 open_rights |= fio::PERM_WRITABLE;
102 }
103 let mut subdir_options = options;
104 subdir_options.source = subdir.into();
105 new_remotefs_in_root(kernel, &root_proxy, subdir_options, open_rights)
106}
107
108pub fn new_remotefs_in_root(
111 kernel: &Kernel,
112 root: &fio::DirectorySynchronousProxy,
113 options: FileSystemOptions,
114 rights: fio::Flags,
115) -> Result<FileSystemHandle, Errno> {
116 let root = syncio::directory_open_directory_async(
117 root,
118 std::str::from_utf8(&options.source)
119 .map_err(|_| errno!(EINVAL, "source path is not utf8"))?,
120 rights,
121 )
122 .map_err(|e| errno!(EIO, format!("Failed to open root: {e}")))?;
123 RemoteFs::new_fs(kernel, root.into_channel(), options, rights)
124}
125
126pub struct RemoteFs {
127 use_remote_ids: bool,
133
134 root_proxy: fio::DirectorySynchronousProxy,
135
136 root_rights: fio::Flags,
138
139 casefold: bool,
141
142 name: &'static str,
143}
144
145impl RemoteFs {
146 fn from_fs(fs: &FileSystem) -> &RemoteFs {
153 if let Some(remote_vol) = fs.downcast_ops::<RemoteVolume>() {
154 remote_vol.remotefs()
155 } else {
156 fs.downcast_ops::<RemoteFs>().unwrap()
157 }
158 }
159}
160
161const REMOTE_FS_MAGIC: u32 = u32::from_be_bytes(*b"f.io");
162const DYNAMIC_FS_BYTES_PER_INODE_FALLBACK: u64 = 16384; const SYNC_IOC_FILE_INFO: u8 = 4;
164const SYNC_IOC_MERGE: u8 = 3;
165
166impl FileSystemOps for RemoteFs {
167 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
168 let (status, info) = self
169 .root_proxy
170 .query_filesystem(zx::MonotonicInstant::INFINITE)
171 .map_err(|_| errno!(EIO))?;
172 if status == 0 {
174 if let Some(info) = info {
175 let (total_blocks, free_blocks) = if info.block_size > 0 {
176 (
177 (info.total_bytes / u64::from(info.block_size))
178 .try_into()
179 .unwrap_or(i64::MAX),
180 ((info.total_bytes.saturating_sub(info.used_bytes))
181 / u64::from(info.block_size))
182 .try_into()
183 .unwrap_or(i64::MAX),
184 )
185 } else {
186 (0, 0)
187 };
188
189 let total_nodes = std::cmp::min(
190 info.total_nodes,
191 info.total_bytes / DYNAMIC_FS_BYTES_PER_INODE_FALLBACK,
192 );
193 let free_nodes = total_nodes.saturating_sub(info.used_nodes);
194
195 let fsid = __kernel_fsid_t {
196 val: [
197 (info.fs_id & 0xffffffff) as i32,
198 ((info.fs_id >> 32) & 0xffffffff) as i32,
199 ],
200 };
201
202 return Ok(statfs {
203 f_type: info.fs_type as i64,
204 f_bsize: info.block_size.into(),
205 f_blocks: total_blocks,
206 f_bfree: free_blocks,
207 f_bavail: free_blocks,
208 f_files: total_nodes.try_into().unwrap_or(i64::MAX),
209 f_ffree: free_nodes.try_into().unwrap_or(i64::MAX),
210 f_fsid: fsid,
211 f_namelen: info.max_filename_size.try_into().unwrap_or(0),
212 f_frsize: info.block_size.into(),
213 ..statfs::default()
214 });
215 }
216 }
217 Ok(default_statfs(REMOTE_FS_MAGIC))
218 }
219
220 fn name(&self) -> &'static FsStr {
221 self.name.into()
222 }
223
224 fn uses_external_node_ids(&self) -> bool {
225 self.use_remote_ids
226 }
227
228 fn has_casefold_support(&self) -> bool {
229 self.casefold
230 }
231
232 fn rename(
233 &self,
234 _fs: &FileSystem,
235 current_task: &CurrentTask,
236 context: &mut RenameContext<'_>,
237 old_name: &FsStr,
238 new_name: &FsStr,
239 ) -> Result<(), Errno> {
240 let renamed = &context.renamed.node;
241 let replaced = context.replaced.map(|r| &r.node);
242 let old_parent = &context.old_parent().node;
243 let new_parent = &context.new_parent().node;
244 let old_parent_info = context.old_parent_info();
245 let new_parent_info = context.new_parent_info();
246 old_parent.fail_if_locked(current_task, old_parent_info)?;
249 if let Some(info) = new_parent_info {
250 new_parent.fail_if_locked(current_task, info)?;
251 }
252
253 let Some((old_parent_ops, new_parent_ops)) =
254 old_parent.downcast_ops::<RemoteNode>().zip(new_parent.downcast_ops::<RemoteNode>())
255 else {
256 return error!(EXDEV);
257 };
258
259 let mut nodes: SmallVec<[&FsNode; 4]> =
260 smallvec![&***old_parent, &***new_parent, &***renamed];
261 if let Some(r) = replaced {
262 nodes.push(r);
263 }
264
265 will_dirty(&nodes, || {
266 old_parent_ops
267 .node
268 .io
269 .rename(get_name_str(old_name)?, &new_parent_ops.node.io, get_name_str(new_name)?)
270 .map_err(|status| match status {
271 zx::Status::BAD_STATE => errno!(EXDEV),
272 zx::Status::ACCESS_DENIED => errno!(ENOKEY),
273 s => map_sync_io_client_error(s),
274 })
275 })
276 }
277
278 fn sync(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<(), Errno> {
279 self.root_proxy
280 .sync(zx::MonotonicInstant::INFINITE)
281 .map_err(|_| errno!(EIO))?
282 .map_err(|status| map_sync_error(zx::Status::err_from_raw(status)))
283 }
284
285 fn manages_timestamps(&self) -> bool {
286 true
287 }
288
289 fn reconfigure(
290 &self,
291 fs: &FileSystem,
292 _current_task: &CurrentTask,
293 mut flags: FileSystemFlags,
294 params: &MountParams,
295 ) -> Result<(), Errno> {
296 if !params.is_empty() {
297 track_stub!(TODO("https://fxbug.dev/322875506"), "MS_REMOUNT: Updating data");
298 }
299 if !self.root_rights.contains(fio::PERM_WRITABLE) {
300 flags |= FileSystemFlags::RDONLY;
301 }
302 fs.options.flags.store(flags, Ordering::Relaxed);
303 Ok(())
304 }
305}
306
307struct Factory<'a> {
311 node_info: &'a mut FsNodeInfo,
312 assume_special: bool,
313}
314
315impl<'a> sync_io_client::Factory for Factory<'a> {
316 type Result = (Box<dyn FsNodeOps>, u64);
317
318 fn create_node(self, io: RemoteIo, info: fio::NodeInfo) -> Self::Result {
319 let dirty = info.attributes.is_none();
320 let attrs = get_attributes(&info.attributes);
321 let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
322 update_info_from_fidl(
323 self.node_info,
324 &attrs.mutable_attributes,
325 &attrs.immutable_attributes,
326 );
327 (Box::new(RemoteNode::new(io, dirty)), id)
328 }
329
330 fn create_directory(self, io: RemoteIo, info: fio::DirectoryInfo) -> Self::Result {
331 let dirty = info.attributes.is_none();
332 let attrs = get_attributes(&info.attributes);
333 let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
334 update_info_from_fidl(
335 self.node_info,
336 &attrs.mutable_attributes,
337 &attrs.immutable_attributes,
338 );
339 (Box::new(RemoteNode::new(io, dirty)), id)
340 }
341
342 fn create_file(self, io: RemoteIo, info: fio::FileInfo) -> Self::Result {
343 let dirty = info.attributes.is_none();
344 let is_special_node = self.assume_special || is_special(&info);
345 let attrs = get_attributes(&info.attributes);
346 let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
347 let ops: Box<dyn FsNodeOps> = if is_special_node {
348 Box::new(RemoteSpecialNode { node: BaseNode::new(io, dirty) })
349 } else {
350 Box::new(RemoteNode::new(io, dirty))
351 };
352 update_info_from_fidl(
353 self.node_info,
354 &attrs.mutable_attributes,
355 &attrs.immutable_attributes,
356 );
357 (ops, id)
358 }
359
360 fn create_symlink(self, io: RemoteIo, info: fio::SymlinkInfo) -> Self::Result {
361 let dirty = info.attributes.is_none();
362 let attrs = get_attributes(&info.attributes);
363 let id = attrs.immutable_attributes.id.unwrap_or(fio::INO_UNKNOWN);
364 let target = info.target.unwrap_or_default();
365 update_info_from_fidl(
366 self.node_info,
367 &attrs.mutable_attributes,
368 &attrs.immutable_attributes,
369 );
370 (Box::new(RemoteSymlink::new(BaseNode::new(io, dirty), target)), id)
371 }
372}
373
374struct LookupFactory<'a> {
378 fs: &'a FileSystemHandle,
379 current_task: &'a CurrentTask,
380}
381
382impl<'a> LookupFactory<'a> {
383 fn get_node(
384 &self,
385 io: RemoteIo,
386 attributes: &fio::NodeAttributes2,
387 create_ops: impl FnOnce(RemoteIo) -> Box<dyn FsNodeOps>,
388 ) -> Result<FsNodeHandle, Errno> {
389 let fs = self.fs;
390 let fs_ops = RemoteFs::from_fs(fs);
391 let fio::NodeAttributes2 { mutable_attributes: mutable, immutable_attributes: immutable } =
392 attributes;
393
394 let id = immutable.id.unwrap_or(fio::INO_UNKNOWN);
395 let node_id = if fs_ops.use_remote_ids {
396 if id == fio::INO_UNKNOWN {
397 return error!(ENOTSUP);
398 }
399 id
400 } else {
401 fs.allocate_ino()
402 };
403
404 let node = fs.get_or_create_node(node_id, || {
405 let uid = mutable.uid.unwrap_or(0);
406 let gid = mutable.gid.unwrap_or(0);
407 let owner = FsCred { uid, gid };
408 let rdev = DeviceId::from_bits(mutable.rdev.unwrap_or(0));
409 let fsverity_enabled = immutable.verity_enabled.unwrap_or(false);
410 let protocols = immutable.protocols.unwrap_or(fio::NodeProtocolKinds::empty());
411 if fsverity_enabled && !protocols.contains(fio::NodeProtocolKinds::FILE) {
413 return error!(EINVAL);
414 }
415
416 let ops = create_ops(io);
417 let child = FsNode::new_uncached(
418 node_id,
419 ops,
420 fs,
421 FsNodeInfo {
422 rdev,
423 ..FsNodeInfo::new(
424 get_mode_from_fidl(mutable, immutable, fs_ops.root_rights),
425 owner,
426 )
427 },
428 FsNodeFlags::empty(),
429 );
430 if fsverity_enabled {
431 *child.fsverity.lock() = FsVerityState::FsVerity;
432 }
433 if let Some(fio::SelinuxContext::Data(data)) = mutable.selinux_context.as_ref() {
436 let _ = security::fs_node_notify_security_context(
437 self.current_task,
438 &child,
439 FsStr::new(data),
440 );
441 }
442 Ok(child)
443 })?;
444
445 node.update_info(|info| update_info_from_fidl(info, mutable, immutable));
446
447 Ok(node)
448 }
449}
450
451impl<'a> sync_io_client::Factory for LookupFactory<'a> {
452 type Result = Result<FsNodeHandle, Errno>;
453
454 fn create_node(self, io: RemoteIo, info: fio::NodeInfo) -> Self::Result {
455 self.get_node(io, get_attributes(&info.attributes), |io| {
456 Box::new(RemoteNode::new(io, false))
457 })
458 }
459
460 fn create_directory(self, io: RemoteIo, info: fio::DirectoryInfo) -> Self::Result {
461 self.get_node(io, get_attributes(&info.attributes), |io| {
462 Box::new(RemoteNode::new(io, false))
463 })
464 }
465
466 fn create_file(self, io: RemoteIo, info: fio::FileInfo) -> Self::Result {
467 let is_special_node = is_special(&info);
468 self.get_node(io, get_attributes(&info.attributes), |io| {
469 if is_special_node {
470 Box::new(RemoteSpecialNode { node: BaseNode::new(io, false) })
471 } else {
472 Box::new(RemoteNode::new(io, false))
473 }
474 })
475 }
476
477 fn create_symlink(self, io: RemoteIo, mut info: fio::SymlinkInfo) -> Self::Result {
478 let mut target = info.target.take();
479 if target.is_none() {
480 return error!(EIO);
481 }
482 let node = self.get_node(io, get_attributes(&info.attributes), |io| {
483 Box::new(RemoteSymlink::new(BaseNode::new(io, false), target.take().unwrap()))
484 })?;
485 if let Some(target) = target
494 && let Some(symlink) = node.downcast_ops::<RemoteSymlink>()
495 {
496 *symlink.target.write() = target.into_boxed_slice();
497 }
498 Ok(node)
499 }
500}
501
502fn get_attributes(attrs: &Option<fio::NodeAttributes2>) -> &fio::NodeAttributes2 {
504 static DEFAULT_NODE_ATTRIBUTES: LazyLock<fio::NodeAttributes2> =
505 LazyLock::new(|| fio::NodeAttributes2 {
506 mutable_attributes: Default::default(),
507 immutable_attributes: Default::default(),
508 });
509 attrs.as_ref().unwrap_or_else(|| &*DEFAULT_NODE_ATTRIBUTES)
510}
511
512impl RemoteFs {
513 pub fn new(
514 root: zx::Channel,
515 root_rights: fio::Flags,
516 name: &'static str,
517 ) -> Result<(RemoteFs, Box<dyn FsNodeOps>, FsNodeInfo, u64), Errno> {
518 let (client_end, server_end) = zx::Channel::create();
519 let root_proxy = fio::DirectorySynchronousProxy::new(root);
520 root_proxy
521 .open(
522 ".",
523 fio::Flags::PROTOCOL_DIRECTORY
524 | fio::PERM_READABLE
525 | fio::Flags::PERM_INHERIT_WRITE
526 | fio::Flags::PERM_INHERIT_EXECUTE
527 | fio::Flags::FLAG_SEND_REPRESENTATION,
528 &fio::Options {
529 attributes: Some(
530 NODE_INFO_ATTRIBUTES
531 | fio::NodeAttributesQuery::ID
532 | fio::NodeAttributesQuery::ENCRYPTION_POLICY,
533 ),
534 ..Default::default()
535 },
536 server_end,
537 )
538 .map_err(|_| errno!(EIO))?;
539
540 let (status, info) =
546 root_proxy.query_filesystem(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
547
548 let vfs_type = (status == 0)
550 .then_some(info)
551 .flatten()
552 .and_then(|i| fidl_fuchsia_fs::VfsType::from_primitive(i.fs_type));
553 let (use_remote_ids, casefold) = match vfs_type {
554 Some(fidl_fuchsia_fs::VfsType::Fxfs) => (true, true),
555 Some(fidl_fuchsia_fs::VfsType::Erofs) => (true, false),
556 _ => (false, false),
557 };
558
559 let mut node_info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
561 let (remote_node, node_id) = create_with_on_representation(
562 client_end.into(),
563 Factory { node_info: &mut node_info, assume_special: false },
564 )
565 .map_err(map_sync_io_client_error)?;
566
567 Ok((
568 RemoteFs { use_remote_ids, root_proxy, root_rights, casefold, name },
569 remote_node,
570 node_info,
571 node_id,
572 ))
573 }
574
575 pub fn new_fs(
576 kernel: &Kernel,
577 root: zx::Channel,
578 options: FileSystemOptions,
579 rights: fio::Flags,
580 ) -> Result<FileSystemHandle, Errno> {
581 let (remotefs, root_node, info, node_id) = RemoteFs::new(root, rights, "remotefs")?;
582
583 if !rights.contains(fio::PERM_WRITABLE) {
584 options.flags.fetch_or(FileSystemFlags::RDONLY, Ordering::Relaxed);
585 }
586 let use_remote_ids = remotefs.use_remote_ids;
587 let fs = FileSystem::new(
588 kernel,
589 CacheMode::Cached(kernel.fs_cache_config()),
590 remotefs,
591 options,
592 )?;
593
594 let node_id = if use_remote_ids { node_id } else { fs.allocate_ino() };
595 fs.create_root_with_info(node_id, root_node, info);
596
597 Ok(fs)
598 }
599
600 pub(super) fn use_remote_ids(&self) -> bool {
601 self.use_remote_ids
602 }
603}
604
605struct BaseNode {
609 io: RemoteIo,
611
612 info_state: InfoState,
615}
616
617impl BaseNode {
618 fn new(io: RemoteIo, dirty: bool) -> Self {
619 Self { io, info_state: InfoState::new(dirty) }
620 }
621
622 fn fetch_and_refresh_info<'a>(
623 &self,
624 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
625 ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
626 self.info_state.maybe_refresh(
627 info,
628 |info| {
629 let mut query = NODE_INFO_ATTRIBUTES;
630 if info.read().pending_time_access_update {
631 query |= fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE;
632 }
633 let (mutable, immutable) =
634 self.io.attr_get(query).map_err(map_sync_io_client_error)?;
635 let mut info = info.write();
636 info.pending_time_access_update = false;
637 update_info_from_fidl(&mut info, &mutable, &immutable);
638 Ok(LockDepWriteGuard::downgrade(info))
639 },
640 |info| Ok(info.read()),
641 )
642 }
643
644 fn update_attributes(&self, info: &FsNodeInfo, has: zxio_node_attr_has_t) -> Result<(), Errno> {
645 will_dirty(&[self], || {
647 let res = self.io.attr_set(fio::MutableNodeAttributes {
648 modification_time: has
649 .modification_time
650 .then_some(info.time_modify.into_nanos() as u64),
651 access_time: has.access_time.then_some(info.time_access.into_nanos() as u64),
652 mode: has.mode.then_some(info.mode.bits()),
653 uid: has.uid.then_some(info.uid),
654 gid: has.gid.then_some(info.gid),
655 rdev: has.rdev.then_some(info.rdev.bits()),
656 casefold: has.casefold.then_some(info.casefold),
657 encryption_policy: if has.encryption_policy {
658 info.encryption_policy.map(|p| fio::FscryptPolicy {
659 key_identifier: p.key_identifier,
660 flags: fio::FscryptPolicyFlags::from_bits_retain(p.flags.bits()),
661 })
662 } else {
663 None
664 },
665 ..Default::default()
666 });
667 res.map_err(|status| from_status_like_fdio!(status))
668 })
669 }
670}
671
672impl<'a> TryFrom<&'a FsNode> for &'a BaseNode {
673 type Error = ();
674 fn try_from(value: &FsNode) -> Result<&BaseNode, ()> {
675 value
676 .downcast_ops::<RemoteNode>()
677 .map(|n| &n.node)
678 .or_else(|| value.downcast_ops::<RemoteSpecialNode>().map(|n| &n.node))
679 .or_else(|| value.downcast_ops::<RemoteSymlink>().map(|n| &n.node))
680 .ok_or(())
681 }
682}
683
684struct RemoteNode {
687 node: BaseNode,
688}
689
690impl RemoteNode {
691 fn new(io: RemoteIo, dirty: bool) -> Self {
692 Self { node: BaseNode::new(io, dirty) }
693 }
694}
695
696pub fn new_remote_file(
707 current_task: &CurrentTask,
708 handle: zx::NullableHandle,
709 flags: OpenFlags,
710) -> Result<FileHandle, Errno> {
711 let remote_creds = current_task.current_creds().clone();
712 let (attrs, ops) = remote_file_attrs_and_ops(current_task, handle, remote_creds)?;
713 let mut rights = fio::Flags::empty();
714 if flags.can_read() {
715 rights |= fio::PERM_READABLE;
716 }
717 if flags.can_write() {
718 rights |= fio::PERM_WRITABLE;
719 }
720 let mode = get_mode(&attrs, rights);
721 let mut info = FsNodeInfo::new(mode, FsCred::root());
723 update_info_from_attrs(&mut info, &attrs);
724 Ok(Anon::new_private_file_extended(current_task, ops, flags, "[fuchsia:remote]", info))
725}
726
727pub fn new_remote_file_ops(
731 current_task: &CurrentTask,
732 handle: zx::NullableHandle,
733 creds: Arc<Credentials>,
734) -> Result<Box<dyn FileOps>, Errno> {
735 let (_, ops) = remote_file_attrs_and_ops(current_task, handle, creds)?;
736 Ok(ops)
737}
738
739fn remote_file_attrs_and_ops(
740 current_task: &CurrentTask,
741 mut handle: zx::NullableHandle,
742 remote_creds: Arc<Credentials>,
743) -> Result<(zxio_node_attr, Box<dyn FileOps>), Errno> {
744 let handle_type =
745 handle.basic_info().map_err(|status| from_status_like_fdio!(status))?.object_type;
746
747 if handle_type == zx::ObjectType::CHANNEL {
748 let channel = zx::Channel::from(handle);
749 let queryable = funknown::QueryableSynchronousProxy::new(channel);
750 let protocol = queryable.query(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
751 const UNIX_DOMAIN_SOCKET_PROTOCOL: &[u8] =
752 fbinder::UnixDomainSocketMarker::PROTOCOL_NAME.as_bytes();
753 const FILE_PROTOCOL: &[u8] = fio::FileMarker::PROTOCOL_NAME.as_bytes();
754 const DIRECTORY_PROTOCOL: &[u8] = fio::DirectoryMarker::PROTOCOL_NAME.as_bytes();
755 match &protocol[..] {
756 UNIX_DOMAIN_SOCKET_PROTOCOL => {
757 let socket_ops =
758 RemoteUnixDomainSocket::new(queryable.into_channel(), remote_creds)?;
759 let socket = Socket::new_with_ops(Box::new(socket_ops))?;
760 let file_ops = SocketFile::new(socket);
761 let attr = zxio_node_attr {
762 has: zxio_node_attr_has_t { mode: true, ..zxio_node_attr_has_t::default() },
763 mode: 0o777 | FileMode::IFSOCK.bits(),
764 ..zxio_node_attr::default()
765 };
766 return Ok((attr, file_ops));
767 }
768 FILE_PROTOCOL => {
769 let file_proxy = fio::FileSynchronousProxy::from(queryable.into_channel());
770 let info =
771 file_proxy.describe(zx::MonotonicInstant::INFINITE).map_err(|_| errno!(EIO))?;
772 let io = RemoteIo::with_stream(
773 file_proxy.into_channel().into(),
774 info.stream.unwrap_or_else(|| zx::NullableHandle::invalid().into()),
775 );
776 let attr = io
777 .attr_get_zxio(MODE_ATTRIBUTES | NODE_INFO_ATTRIBUTES)
778 .map_err(map_sync_io_client_error)?;
779 return Ok((attr, Box::new(AnonymousRemoteFileObject::new(io))));
780 }
781 DIRECTORY_PROTOCOL => {
782 let io = RemoteIo::new(queryable.into_channel().into());
783 let attr = io
784 .attr_get_zxio(MODE_ATTRIBUTES | NODE_INFO_ATTRIBUTES)
785 .map_err(map_sync_io_client_error)?;
786 return Ok((
787 attr,
788 Box::new(RemoteDirectoryObject::new(io.into_proxy().into_channel().into())),
789 ));
790 }
791 _ => {
792 handle = queryable.into_channel().into_handle();
793 }
795 }
796 } else if handle_type == zx::ObjectType::COUNTER {
797 let attr = zxio_node_attr::default();
798 let file_ops = Box::new(RemoteCounter::new(handle.into()));
799 return Ok((attr, file_ops));
800 }
801
802 let zxio = Zxio::create(handle).map_err(|status| from_status_like_fdio!(status))?;
807 let mut attrs = zxio
808 .attr_get(zxio_node_attr_has_t {
809 protocols: true,
810 content_size: true,
811 storage_size: true,
812 link_count: true,
813 object_type: true,
814 ..Default::default()
815 })
816 .map_err(|status| from_status_like_fdio!(status))?;
817 let ops: Box<dyn FileOps> = match (handle_type, attrs.object_type) {
818 (zx::ObjectType::VMO, _) | (zx::ObjectType::DEBUGLOG, _) | (_, ZXIO_OBJECT_TYPE_NONE) => {
819 Box::new(RemoteZxioFileObject::new(zxio))
820 }
821 (zx::ObjectType::SOCKET, _)
822 | (_, ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET)
823 | (_, ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET)
824 | (_, ZXIO_OBJECT_TYPE_STREAM_SOCKET)
825 | (_, ZXIO_OBJECT_TYPE_RAW_SOCKET)
826 | (_, ZXIO_OBJECT_TYPE_PACKET_SOCKET) => {
827 let socket_ops = ZxioBackedSocket::new_with_zxio(current_task, zxio);
828 let socket = Socket::new_with_ops(Box::new(socket_ops))?;
829 attrs.has.mode = true;
830 attrs.mode = FileMode::IFSOCK.bits();
831 SocketFile::new(socket)
832 }
833 _ => return error!(ENOTSUP),
834 };
835 Ok((attrs, ops))
836}
837
838pub fn create_fuchsia_pipe(
839 current_task: &CurrentTask,
840 socket: zx::Socket,
841 flags: OpenFlags,
842) -> Result<FileHandle, Errno> {
843 new_remote_file(current_task, socket.into(), flags)
844}
845
846const NODE_INFO_ATTRIBUTES: fio::NodeAttributesQuery = fio::NodeAttributesQuery::CONTENT_SIZE
849 .union(fio::NodeAttributesQuery::STORAGE_SIZE)
850 .union(fio::NodeAttributesQuery::LINK_COUNT)
851 .union(fio::NodeAttributesQuery::MODIFICATION_TIME)
852 .union(fio::NodeAttributesQuery::CHANGE_TIME)
853 .union(fio::NodeAttributesQuery::ACCESS_TIME);
854
855pub(super) fn update_info_from_attrs(info: &mut FsNodeInfo, attrs: &zxio_node_attributes_t) {
859 if attrs.has.content_size {
861 info.size = attrs.content_size.try_into().unwrap_or(usize::MAX);
862 }
863 if attrs.has.storage_size {
864 info.blocks = usize::try_from(attrs.storage_size)
865 .unwrap_or(usize::MAX)
866 .div_ceil(DEFAULT_BYTES_PER_BLOCK)
867 }
868 info.blksize = DEFAULT_BYTES_PER_BLOCK;
869 if attrs.has.link_count {
870 info.link_count = attrs.link_count.try_into().unwrap_or(usize::MAX);
871 }
872 if attrs.has.modification_time {
873 info.time_modify =
874 UtcInstant::from_nanos(attrs.modification_time.try_into().unwrap_or(i64::MAX));
875 }
876 if attrs.has.change_time {
877 info.time_status_change =
878 UtcInstant::from_nanos(attrs.change_time.try_into().unwrap_or(i64::MAX));
879 }
880 if attrs.has.access_time {
881 info.time_access = UtcInstant::from_nanos(attrs.access_time.try_into().unwrap_or(i64::MAX));
882 }
883 if attrs.has.casefold {
885 info.casefold = attrs.casefold;
886 }
887}
888
889fn update_info_from_fidl(
891 info: &mut FsNodeInfo,
892 mutable: &fio::MutableNodeAttributes,
893 immutable: &fio::ImmutableNodeAttributes,
894) {
895 if let Some(content_size) = immutable.content_size {
896 info.size = content_size.try_into().unwrap_or(usize::MAX);
897 }
898 if let Some(storage_size) = immutable.storage_size {
899 info.blocks =
900 usize::try_from(storage_size).unwrap_or(usize::MAX).div_ceil(DEFAULT_BYTES_PER_BLOCK);
901 }
902 info.blksize = DEFAULT_BYTES_PER_BLOCK;
903 if let Some(link_count) = immutable.link_count {
904 info.link_count = link_count.try_into().unwrap_or(usize::MAX);
905 }
906 if let Some(modification_time) = mutable.modification_time {
907 info.time_modify = UtcInstant::from_nanos(modification_time.try_into().unwrap_or(i64::MAX));
908 }
909 if let Some(change_time) = immutable.change_time {
910 info.time_status_change =
911 UtcInstant::from_nanos(change_time.try_into().unwrap_or(i64::MAX));
912 }
913 if !info.pending_time_access_update
914 && let Some(access_time) = mutable.access_time
915 {
916 info.time_access = UtcInstant::from_nanos(access_time.try_into().unwrap_or(i64::MAX));
917 }
918 if let Some(casefold) = mutable.casefold {
920 info.casefold = casefold;
921 }
922 if let Some(policy) = mutable.encryption_policy {
923 info.encryption_policy = Some(crate::vfs::FscryptNodePolicy {
924 key_identifier: policy.key_identifier,
925 flags: crate::vfs::FscryptPolicyFlags::from_bits_retain(policy.flags.bits()),
926 });
927 }
928}
929
930const MODE_ATTRIBUTES: fio::NodeAttributesQuery =
932 fio::NodeAttributesQuery::PROTOCOLS.union(fio::NodeAttributesQuery::MODE);
933
934fn get_mode(attrs: &zxio_node_attributes_t, rights: fio::Flags) -> FileMode {
936 if attrs.protocols & ZXIO_NODE_PROTOCOL_SYMLINK != 0 {
937 FileMode::IFLNK | FileMode::ALLOW_ALL
939 } else if attrs.has.mode {
940 FileMode::from_bits(attrs.mode)
942 } else {
943 let is_directory =
946 attrs.protocols & ZXIO_NODE_PROTOCOL_DIRECTORY == ZXIO_NODE_PROTOCOL_DIRECTORY;
947 let mode = if is_directory { FileMode::IFDIR } else { FileMode::IFREG };
948 let mut permissions = FileMode::EMPTY;
949 if rights.contains(fio::PERM_READABLE) {
950 permissions |= FileMode::IRUSR;
951 }
952 if rights.contains(fio::PERM_WRITABLE) {
953 permissions |= FileMode::IWUSR;
954 }
955 if rights.contains(fio::PERM_EXECUTABLE) {
956 permissions |= FileMode::IXUSR;
957 }
958 permissions |= FileMode::from_bits((permissions.bits() >> 3) | (permissions.bits() >> 6));
960 mode | permissions
961 }
962}
963
964fn get_mode_from_fidl(
966 mutable: &fio::MutableNodeAttributes,
967 immutable: &fio::ImmutableNodeAttributes,
968 rights: fio::Flags,
969) -> FileMode {
970 let protocols = immutable.protocols.unwrap_or(fio::NodeProtocolKinds::empty());
971 if protocols.contains(fio::NodeProtocolKinds::SYMLINK) {
972 FileMode::IFLNK | FileMode::ALLOW_ALL
974 } else if let Some(mode) = mutable.mode {
975 FileMode::from_bits(mode)
977 } else {
978 let is_directory = protocols.contains(fio::NodeProtocolKinds::DIRECTORY);
981 let mode = if is_directory { FileMode::IFDIR } else { FileMode::IFREG };
982 let mut permissions = FileMode::EMPTY;
983 if rights.contains(fio::PERM_READABLE) {
984 permissions |= FileMode::IRUSR;
985 }
986 if rights.contains(fio::PERM_WRITABLE) {
987 permissions |= FileMode::IWUSR;
988 }
989 if rights.contains(fio::PERM_EXECUTABLE) {
990 permissions |= FileMode::IXUSR;
991 }
992 permissions |= FileMode::from_bits((permissions.bits() >> 3) | (permissions.bits() >> 6));
994 mode | permissions
995 }
996}
997
998fn get_name_str<'a>(name_bytes: &'a FsStr) -> Result<&'a str, Errno> {
999 std::str::from_utf8(name_bytes.as_ref()).map_err(|_| {
1000 log_warn!("bad utf8 in pathname! remote filesystems can't handle this");
1001 errno!(EINVAL)
1002 })
1003}
1004
1005impl XattrStorage for BaseNode {
1006 fn get_xattr(&self, name: &FsStr) -> Result<FsString, Errno> {
1007 Ok(self
1008 .io
1009 .xattr_get(name)
1010 .map_err(|status| match status {
1011 zx::Status::NOT_FOUND => errno!(ENODATA),
1012 status => from_status_like_fdio!(status),
1013 })?
1014 .into())
1015 }
1016
1017 fn set_xattr(&self, name: &FsStr, value: &FsStr, op: XattrOp) -> Result<(), Errno> {
1018 let mode = match op {
1019 XattrOp::Set => XattrSetMode::Set,
1020 XattrOp::Create => XattrSetMode::Create,
1021 XattrOp::Replace => XattrSetMode::Replace,
1022 };
1023
1024 will_dirty(&[self], || {
1025 self.io.xattr_set(name, value, mode).map_err(|status| match status {
1026 zx::Status::NOT_FOUND => errno!(ENODATA),
1027 status => from_status_like_fdio!(status),
1028 })
1029 })
1030 }
1031
1032 fn remove_xattr(&self, name: &FsStr) -> Result<(), Errno> {
1033 will_dirty(&[self], || {
1034 self.io.xattr_remove(name).map_err(|status| match status {
1035 zx::Status::NOT_FOUND => errno!(ENODATA),
1036 _ => from_status_like_fdio!(status),
1037 })
1038 })
1039 }
1040
1041 fn list_xattrs(&self) -> Result<Vec<FsString>, Errno> {
1042 self.io
1043 .xattr_list()
1044 .map(|attrs| attrs.into_iter().map(FsString::new).collect::<Vec<_>>())
1045 .map_err(map_sync_io_client_error)
1046 }
1047}
1048
1049impl FsNodeOps for RemoteNode {
1050 fs_node_impl_xattr_delegate!(self, self.node);
1051
1052 fn create_file_ops(
1053 &self,
1054 node: &FsNode,
1055 current_task: &CurrentTask,
1056 flags: OpenFlags,
1057 ) -> Result<Box<dyn FileOps>, Errno> {
1058 {
1059 let node_info = node.info();
1063 if node_info.mode.is_dir() {
1064 if let Some(policy) = node_info.encryption_policy {
1065 if flags.can_write() {
1066 let crypt_service =
1068 node.fs().crypt_service().ok_or_else(|| errno!(ENOKEY))?;
1069 if !crypt_service.contains_key(EncryptionKeyId::from(policy.key_identifier))
1070 {
1071 return error!(ENOKEY);
1072 }
1073 }
1074 }
1075 return Ok(Box::new(RemoteDirectoryObject::new(
1078 self.node
1079 .io
1080 .clone_proxy()
1081 .map(|p| p.into_channel().into())
1082 .map_err(map_sync_io_client_error)?,
1083 )));
1084 }
1085 }
1086
1087 node.fail_if_locked(current_task, &node.info())?;
1089
1090 if flags.can_write() {
1092 node.fsverity.lock().check_writable()?;
1093 }
1094
1095 Ok(Box::new(RemoteFileObject::default()))
1096 }
1097
1098 fn sync(&self, _node: &FsNode, _current_task: &CurrentTask) -> Result<(), Errno> {
1099 self.node.io.sync().map_err(map_sync_io_client_error)
1100 }
1101
1102 fn mknod(
1103 &self,
1104 node: &FsNode,
1105 current_task: &CurrentTask,
1106 name: &FsStr,
1107 mode: FileMode,
1108 dev: DeviceId,
1109 owner: FsCred,
1110 ) -> Result<FsNodeHandle, Errno> {
1111 node.fail_if_locked(current_task, &node.info())?;
1112 let name = get_name_str(name)?;
1113
1114 let fs = node.fs();
1115 let fs_ops = RemoteFs::from_fs(&fs);
1116
1117 if !(mode.is_reg() || mode.is_chr() || mode.is_blk() || mode.is_fifo() || mode.is_sock()) {
1118 return error!(EINVAL, name);
1119 }
1120
1121 let mut node_info = FsNodeInfo { rdev: dev, ..FsNodeInfo::new(mode, owner) };
1122 let (ops, node_id) = will_dirty(&[&self.node], || {
1123 self.node
1124 .io
1125 .open(
1126 name,
1127 fio::Flags::FLAG_MUST_CREATE
1128 | fio::Flags::PROTOCOL_FILE
1129 | fio::PERM_READABLE
1130 | fio::PERM_WRITABLE,
1131 Some(fio::MutableNodeAttributes {
1132 mode: Some(mode.bits()),
1133 uid: Some(owner.uid),
1134 gid: Some(owner.gid),
1135 rdev: Some(dev.bits()),
1136 ..Default::default()
1137 }),
1138 NODE_INFO_ATTRIBUTES
1139 | fio::NodeAttributesQuery::ID
1140 | fio::NodeAttributesQuery::ENCRYPTION_POLICY,
1141 Factory { node_info: &mut node_info, assume_special: !mode.is_reg() },
1142 )
1143 .map_err(|status| from_status_like_fdio!(status, name))
1144 })?;
1145
1146 let node_id = if fs_ops.use_remote_ids { node_id } else { fs.allocate_ino() };
1147
1148 let child = fs.create_node(node_id, ops, node_info);
1149 Ok(child)
1150 }
1151
1152 fn mkdir(
1153 &self,
1154 node: &FsNode,
1155 current_task: &CurrentTask,
1156 name: &FsStr,
1157 mode: FileMode,
1158 owner: FsCred,
1159 ) -> Result<FsNodeHandle, Errno> {
1160 node.fail_if_locked(current_task, &node.info())?;
1161 let name = get_name_str(name)?;
1162
1163 let fs = node.fs();
1164 let fs_ops = RemoteFs::from_fs(&fs);
1165
1166 let mut node_info = FsNodeInfo::new(mode, owner);
1167 let (ops, node_id) = will_dirty(&[&self.node], || {
1168 self.node
1169 .io
1170 .open(
1171 name,
1172 fio::Flags::FLAG_MUST_CREATE
1173 | fio::Flags::PROTOCOL_DIRECTORY
1174 | fio::PERM_READABLE
1175 | fio::PERM_WRITABLE,
1176 Some(fio::MutableNodeAttributes {
1177 mode: Some(mode.bits()),
1178 uid: Some(owner.uid),
1179 gid: Some(owner.gid),
1180 ..Default::default()
1181 }),
1182 NODE_INFO_ATTRIBUTES
1183 | fio::NodeAttributesQuery::ID
1184 | fio::NodeAttributesQuery::ENCRYPTION_POLICY,
1185 Factory { node_info: &mut node_info, assume_special: false },
1186 )
1187 .map_err(|status| from_status_like_fdio!(status, name))
1188 })?;
1189
1190 let node_id = if fs_ops.use_remote_ids { node_id } else { fs.allocate_ino() };
1191
1192 let child = fs.create_node(node_id, ops, node_info);
1193 Ok(child)
1194 }
1195
1196 fn lookup(
1197 &self,
1198 entry: &DirEntry,
1199 current_task: &CurrentTask,
1200 name: &FsStr,
1201 ) -> Result<FsNodeHandle, Errno> {
1202 let name = get_name_str(name)?;
1203
1204 let fs = entry.node.fs();
1205 let fs_ops = RemoteFs::from_fs(&fs);
1206
1207 let mut query = MODE_ATTRIBUTES
1208 | NODE_INFO_ATTRIBUTES
1209 | fio::NodeAttributesQuery::ID
1210 | fio::NodeAttributesQuery::UID
1211 | fio::NodeAttributesQuery::GID
1212 | fio::NodeAttributesQuery::RDEV
1213 | fio::NodeAttributesQuery::ENCRYPTION_POLICY
1214 | fio::NodeAttributesQuery::VERITY_ENABLED
1215 | fio::NodeAttributesQuery::CASEFOLD;
1216
1217 if security::fs_is_xattr_labeled(fs.clone()) {
1218 query |= fio::NodeAttributesQuery::SELINUX_CONTEXT;
1219 }
1220
1221 self.node
1222 .io
1223 .open(name, fs_ops.root_rights, None, query, LookupFactory { fs: &fs, current_task })
1224 .map_err(|status| from_status_like_fdio!(status, name))?
1225 }
1226
1227 fn has_lookup_pipelined(&self) -> bool {
1228 true
1229 }
1230
1231 fn lookup_pipelined(
1232 &self,
1233 entry: &DirEntry,
1234 current_task: &CurrentTask,
1235 names: &[&FsStr],
1236 ) -> LookupVec<Result<FsNodeHandle, Errno>> {
1237 let fs = entry.node.fs();
1238 let fs_ops = RemoteFs::from_fs(&fs);
1239
1240 let mut query = MODE_ATTRIBUTES
1241 | NODE_INFO_ATTRIBUTES
1242 | fio::NodeAttributesQuery::ID
1243 | fio::NodeAttributesQuery::UID
1244 | fio::NodeAttributesQuery::GID
1245 | fio::NodeAttributesQuery::RDEV
1246 | fio::NodeAttributesQuery::ENCRYPTION_POLICY
1247 | fio::NodeAttributesQuery::VERITY_ENABLED
1248 | fio::NodeAttributesQuery::CASEFOLD;
1249
1250 if security::fs_is_xattr_labeled(fs.clone()) {
1251 query |= fio::NodeAttributesQuery::SELINUX_CONTEXT;
1252 }
1253
1254 let names_str =
1255 match names.iter().map(|n| get_name_str(n)).collect::<Result<LookupVec<_>, Errno>>() {
1256 Ok(names_str) => names_str,
1257 Err(e) => return vec![Err(e)].into(),
1258 };
1259
1260 self.node
1261 .io
1262 .open_pipelined(&names_str, fs_ops.root_rights, query, || LookupFactory {
1263 fs: &fs,
1264 current_task,
1265 })
1266 .map(|r| r.map_err(|status| from_status_like_fdio!(status)).flatten())
1267 .collect()
1268 }
1269
1270 fn truncate(
1271 &self,
1272 _guard: &AppendLockWriteGuard<'_>,
1273 node: &FsNode,
1274 current_task: &CurrentTask,
1275 length: u64,
1276 ) -> Result<(), Errno> {
1277 node.fail_if_locked(current_task, &node.info())?;
1278
1279 let _guard = self.node.info_state.dirty_op_guard(true);
1280
1281 self.node.io.truncate(length).map_err(|status| from_status_like_fdio!(status))
1282 }
1283
1284 fn allocate(
1285 &self,
1286 _guard: &AppendLockWriteGuard<'_>,
1287 node: &FsNode,
1288 current_task: &CurrentTask,
1289 mode: FallocMode,
1290 offset: u64,
1291 length: u64,
1292 ) -> Result<(), Errno> {
1293 match mode {
1294 FallocMode::Allocate { keep_size } => {
1295 node.fail_if_locked(current_task, &node.info())?;
1296
1297 let allocate_mode =
1298 if keep_size { AllocateMode::KEEP_SIZE } else { AllocateMode::empty() };
1299
1300 will_dirty(&[&self.node], || {
1301 self.node
1302 .io
1303 .allocate(offset, length, allocate_mode)
1304 .map_err(|status| from_status_like_fdio!(status))
1305 })?;
1306 Ok(())
1307 }
1308 FallocMode::PunchHole => {
1309 node.fail_if_locked(current_task, &node.info())?;
1310
1311 will_dirty(&[&self.node], || {
1312 match self.node.io.allocate(
1313 offset,
1314 length,
1315 AllocateMode::PUNCH_HOLE | AllocateMode::KEEP_SIZE,
1316 ) {
1317 Ok(()) => Ok(()),
1318 Err(zx::Status::NOT_SUPPORTED) => Ok(()),
1319 Err(status) => Err(from_status_like_fdio!(status)),
1320 }
1321 })?;
1322 Ok(())
1323 }
1324 _ => error!(EINVAL),
1325 }
1326 }
1327
1328 fn fetch_and_refresh_info<'a>(
1329 &self,
1330 _node: &FsNode,
1331 _current_task: &CurrentTask,
1332 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
1333 flags: StatxFlags,
1334 ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
1335 if flags.contains(StatxFlags::AT_STATX_DONT_SYNC) {
1336 return Ok(info.read());
1337 }
1338 self.node.fetch_and_refresh_info(info)
1339 }
1340
1341 fn update_attributes(
1342 &self,
1343 _node: &FsNode,
1344 _current_task: &CurrentTask,
1345 info: &FsNodeInfo,
1346 has: zxio_node_attr_has_t,
1347 ) -> Result<(), Errno> {
1348 self.node.update_attributes(info, has)
1351 }
1352
1353 fn unlink(
1354 &self,
1355 node: &FsNode,
1356 _current_task: &CurrentTask,
1357 name: &FsStr,
1358 child: &FsNodeHandle,
1359 ) -> Result<(), Errno> {
1360 let name = get_name_str(name)?;
1364 will_dirty(&[node, child], || {
1365 self.node
1366 .io
1367 .unlink(name, fio::UnlinkFlags::empty())
1368 .map_err(|status| from_status_like_fdio!(status))
1369 })
1370 }
1371
1372 fn create_symlink(
1373 &self,
1374 node: &FsNode,
1375 current_task: &CurrentTask,
1376 name: &FsStr,
1377 target: &FsStr,
1378 owner: FsCred,
1379 ) -> Result<FsNodeHandle, Errno> {
1380 node.fail_if_locked(current_task, &node.info())?;
1381
1382 let name = get_name_str(name)?;
1383 let io = will_dirty(&[&self.node], || {
1384 self.node
1385 .io
1386 .create_symlink(name, target)
1387 .map_err(|status| from_status_like_fdio!(status))
1388 })?;
1389
1390 let fs = node.fs();
1391 let fs_ops = RemoteFs::from_fs(&fs);
1392
1393 let node_id = if fs_ops.use_remote_ids {
1394 io.attr_get(fio::NodeAttributesQuery::ID)
1395 .map_err(|status| from_status_like_fdio!(status))?
1396 .1
1397 .id
1398 .unwrap_or_default()
1399 } else {
1400 fs.allocate_ino()
1401 };
1402 Ok(fs.create_node(
1403 node_id,
1404 RemoteSymlink::new(BaseNode::new(io, true), target.as_bytes()),
1405 FsNodeInfo {
1406 size: target.len(),
1407 ..FsNodeInfo::new(FileMode::IFLNK | FileMode::ALLOW_ALL, owner)
1408 },
1409 ))
1410 }
1411
1412 fn create_tmpfile(
1413 &self,
1414 node: &FsNode,
1415 _current_task: &CurrentTask,
1416 mode: FileMode,
1417 owner: FsCred,
1418 ) -> Result<FsNodeHandle, Errno> {
1419 let fs = node.fs();
1420 let fs_ops = RemoteFs::from_fs(&fs);
1421
1422 if !mode.is_reg() {
1423 return error!(EINVAL);
1424 }
1425
1426 let mut node_info = FsNodeInfo::new(mode, owner);
1433 let (ops, node_id) = will_dirty(&[&self.node], || {
1434 self.node
1435 .io
1436 .open(
1437 ".",
1438 fio::Flags::PROTOCOL_FILE
1439 | fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY
1440 | fio::PERM_READABLE
1441 | fio::PERM_WRITABLE,
1442 Some(fio::MutableNodeAttributes {
1443 mode: Some(mode.bits()),
1444 uid: Some(owner.uid),
1445 gid: Some(owner.gid),
1446 ..Default::default()
1447 }),
1448 NODE_INFO_ATTRIBUTES
1449 | fio::NodeAttributesQuery::ID
1450 | fio::NodeAttributesQuery::ENCRYPTION_POLICY,
1451 Factory { node_info: &mut node_info, assume_special: false },
1452 )
1453 .map_err(|status| from_status_like_fdio!(status))
1454 })?;
1455
1456 let node_id = if fs_ops.use_remote_ids { node_id } else { fs.allocate_ino() };
1457 Ok(fs.create_node(node_id, ops, node_info))
1458 }
1459
1460 fn link(
1461 &self,
1462 node: &FsNode,
1463 _current_task: &CurrentTask,
1464 name: &FsStr,
1465 child: &FsNodeHandle,
1466 ) -> Result<(), Errno> {
1467 if !RemoteFs::from_fs(&node.fs()).use_remote_ids {
1468 return error!(EPERM);
1469 }
1470 let name = get_name_str(name)?;
1471
1472 will_dirty(&[node, child], || {
1473 if let Some(child) = child.downcast_ops::<RemoteNode>() {
1474 child.node.io.link_into(&self.node.io, name).map_err(|status| match status {
1475 zx::Status::BAD_STATE => errno!(EXDEV),
1476 zx::Status::ACCESS_DENIED => errno!(ENOKEY),
1477 s => from_status_like_fdio!(s),
1478 })
1479 } else if let Some(child) = child.downcast_ops::<RemoteSymlink>() {
1480 child.node.io.link_into(&self.node.io, name).map_err(|status| match status {
1481 zx::Status::BAD_STATE => errno!(EXDEV),
1482 zx::Status::ACCESS_DENIED => errno!(ENOKEY),
1483 s => from_status_like_fdio!(s),
1484 })
1485 } else {
1486 error!(EXDEV)
1487 }
1488 })
1489 }
1490
1491 fn forget(self: Box<Self>, _current_task: &CurrentTask, info: FsNodeInfo) -> Result<(), Errno> {
1492 if info.pending_time_access_update {
1494 self.node
1495 .io
1496 .attr_get(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
1497 .map_err(|status| from_status_like_fdio!(status))?;
1498 }
1499 Ok(())
1500 }
1501
1502 fn enable_fsverity(
1503 &self,
1504 _node: &FsNode,
1505 _current_task: &CurrentTask,
1506 descriptor: &fsverity_descriptor,
1507 ) -> Result<(), Errno> {
1508 let descr = zxio_fsverity_descriptor_t {
1509 hash_algorithm: descriptor.hash_algorithm,
1510 salt_size: descriptor.salt_size,
1511 salt: descriptor.salt,
1512 };
1513 will_dirty(&[&self.node], || {
1514 self.node.io.enable_verity(&descr).map_err(|status| from_status_like_fdio!(status))
1515 })
1516 }
1517
1518 fn get_fsverity_descriptor(&self, log_blocksize: u8) -> Result<fsverity_descriptor, Errno> {
1519 let (_, attrs) = self
1520 .node
1521 .io
1522 .attr_get(
1523 fio::NodeAttributesQuery::CONTENT_SIZE
1524 | fio::NodeAttributesQuery::OPTIONS
1525 | fio::NodeAttributesQuery::ROOT_HASH,
1526 )
1527 .map_err(|status| from_status_like_fdio!(status))?;
1528 let fio::ImmutableNodeAttributes {
1529 content_size: Some(data_size),
1530 options:
1531 Some(fio::VerificationOptions {
1532 hash_algorithm: Some(hash_algorithm),
1533 salt: Some(salt),
1534 ..
1535 }),
1536 root_hash: Some(root_hash),
1537 ..
1538 } = attrs
1539 else {
1540 return error!(ENODATA);
1541 };
1542 let mut descriptor = fsverity_descriptor {
1543 version: 1,
1544 hash_algorithm: hash_algorithm.into_primitive(),
1545 log_blocksize,
1546 __reserved_0x04: 0u32,
1547 data_size,
1548 ..Default::default()
1549 };
1550 if salt.len() > std::mem::size_of_val(&descriptor.salt)
1551 || root_hash.len() > std::mem::size_of_val(&descriptor.root_hash)
1552 {
1553 return error!(EIO);
1554 }
1555 descriptor.salt_size = salt.len() as u8;
1556 descriptor.salt[..salt.len()].copy_from_slice(&salt);
1557 descriptor.root_hash[..root_hash.len()].copy_from_slice(&root_hash);
1558 Ok(descriptor)
1559 }
1560
1561 fn get_size(&self, node: &FsNode, current_task: &CurrentTask) -> Result<usize, Errno> {
1562 if self.node.info_state.is_size_accurate() {
1563 node.info()
1564 } else {
1565 node.fetch_and_refresh_info(current_task)?
1566 }
1567 .size
1568 .try_into()
1569 .map_err(|_| errno!(EINVAL))
1570 }
1571}
1572
1573struct RemoteSpecialNode {
1574 node: BaseNode,
1575}
1576
1577impl FsNodeOps for RemoteSpecialNode {
1578 fs_node_impl_not_dir!();
1579 fs_node_impl_xattr_delegate!(self, self.node);
1580
1581 fn create_file_ops(
1582 &self,
1583 _node: &FsNode,
1584 _current_task: &CurrentTask,
1585 _flags: OpenFlags,
1586 ) -> Result<Box<dyn FileOps>, Errno> {
1587 unreachable!("Special nodes cannot be opened.");
1588 }
1589
1590 fn update_attributes(
1591 &self,
1592 _node: &FsNode,
1593 _current_task: &CurrentTask,
1594 info: &FsNodeInfo,
1595 has: zxio_node_attr_has_t,
1596 ) -> Result<(), Errno> {
1597 self.node.update_attributes(info, has)
1600 }
1601}
1602
1603struct RemoteDirectoryObject(sync_io_client::RemoteDirectory);
1604
1605impl RemoteDirectoryObject {
1606 fn new(proxy: fio::DirectorySynchronousProxy) -> Self {
1607 Self(sync_io_client::RemoteDirectory::new(proxy))
1608 }
1609}
1610
1611impl FileOps for RemoteDirectoryObject {
1612 fileops_impl_directory!();
1613
1614 fn seek(
1615 &self,
1616 _file: &FileObject,
1617 _current_task: &CurrentTask,
1618 current_offset: off_t,
1619 target: SeekTarget,
1620 ) -> Result<off_t, Errno> {
1621 Ok(self
1622 .0
1623 .seek(default_seek(current_offset, target, || error!(EINVAL))? as u64)
1624 .map_err(map_sync_io_client_error)? as i64)
1625 }
1626
1627 fn readdir(
1628 &self,
1629 file: &FileObject,
1630 _current_task: &CurrentTask,
1631 sink: &mut dyn DirentSink,
1632 ) -> Result<(), Errno> {
1633 match self
1634 .0
1635 .readdir(|mut inode_num, entry_type, name| {
1636 if name == b"." {
1637 inode_num = file.name.entry.node.ino;
1638 } else if name == b".." {
1639 inode_num = if let Some(parent) = file.name.parent_within_mount() {
1640 parent.node.ino
1641 } else {
1642 file.name.entry.node.ino
1644 };
1645 }
1646 let entry_type = match entry_type {
1647 fio::DirentType::Directory => DirectoryEntryType::DIR,
1648 fio::DirentType::File => DirectoryEntryType::REG,
1649 fio::DirentType::Symlink => DirectoryEntryType::LNK,
1650 _ => DirectoryEntryType::UNKNOWN,
1651 };
1652 match sink.add(inode_num, sink.offset() + 1, entry_type, name.into()) {
1653 Ok(()) => ControlFlow::Continue(()),
1654 Err(e) => ControlFlow::Break(e),
1655 }
1656 })
1657 .map_err(map_sync_io_client_error)?
1658 {
1659 None => Ok(()),
1660 Some(e) => Err(e),
1661 }
1662 }
1663
1664 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1665 self.0.sync().map_err(map_sync_error)
1666 }
1667
1668 fn to_handle(
1669 &self,
1670 _file: &FileObject,
1671 _current_task: &CurrentTask,
1672 ) -> Result<Option<zx::NullableHandle>, Errno> {
1673 self.0
1676 .clone_proxy()
1677 .map_err(map_sync_io_client_error)
1678 .map(|p| Some(p.into_channel().into()))
1679 }
1680}
1681
1682#[derive(Default)]
1683pub struct RemoteFileObject {
1684 read_only_memory: OnceCell<Arc<MemoryObject>>,
1686
1687 read_exec_memory: OnceCell<Arc<MemoryObject>>,
1689}
1690
1691impl RemoteFileObject {
1692 fn io(file: &FileObject) -> &RemoteIo {
1697 &file.node().downcast_ops::<RemoteNode>().unwrap().node.io
1698 }
1699}
1700
1701trait RemoteIoExt {
1702 fn read_to_output_buffer(
1703 &self,
1704 offset: u64,
1705 buffer: &mut dyn OutputBuffer,
1706 ) -> Result<usize, Errno>;
1707 fn write_from_input_buffer(
1708 &self,
1709 offset: u64,
1710 buffer: &mut dyn InputBuffer,
1711 ) -> Result<usize, Errno>;
1712 fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno>;
1713}
1714
1715impl RemoteIoExt for RemoteIo {
1716 fn read_to_output_buffer(
1717 &self,
1718 offset: u64,
1719 buffer: &mut dyn OutputBuffer,
1720 ) -> Result<usize, Errno> {
1721 if self.supports_vectored()
1722 && let Some(actual) = with_iovec_segments(buffer, |iovecs| {
1723 unsafe { self.readv(offset, iovecs).map_err(map_stream_error) }
1726 })
1727 {
1728 let actual = actual?;
1729 unsafe { buffer.advance(actual) }?;
1732 Ok(actual)
1733 } else {
1734 self.read(
1735 offset,
1736 buffer.available(),
1737 |data| buffer.write(&data),
1738 map_sync_io_client_error,
1739 )
1740 }
1741 }
1742
1743 fn write_from_input_buffer(
1744 &self,
1745 offset: u64,
1746 buffer: &mut dyn InputBuffer,
1747 ) -> Result<usize, Errno> {
1748 let actual = if self.supports_vectored()
1749 && let Some(actual) = with_iovec_segments(buffer, |iovecs| {
1750 self.writev(offset, iovecs).map_err(map_stream_error)
1751 }) {
1752 actual?
1753 } else {
1754 self.write(offset as u64, &buffer.peek_all()?).map_err(map_sync_io_client_error)?
1755 };
1756 buffer.advance(actual)?;
1757 Ok(actual)
1758 }
1759
1760 fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno> {
1761 let without_exec = self
1762 .vmo_get(prot.to_vmar_flags() - zx::VmarFlags::PERM_EXECUTE)
1763 .map_err(|status| from_status_like_fdio!(status))?;
1764 let all_flags = if prot.contains(ProtectionFlags::EXEC) {
1765 without_exec.replace_as_executable(&VMEX_RESOURCE).map_err(impossible_error)?
1766 } else {
1767 without_exec
1768 };
1769 Ok(Arc::new(MemoryObject::from(all_flags)))
1770 }
1771}
1772
1773impl FileOps for RemoteFileObject {
1774 fileops_impl_seekable!();
1775
1776 fn read(
1777 &self,
1778 file: &FileObject,
1779 _current_task: &CurrentTask,
1780 offset: usize,
1781 data: &mut dyn OutputBuffer,
1782 ) -> Result<usize, Errno> {
1783 Self::io(file).read_to_output_buffer(offset as u64, data)
1784 }
1785
1786 fn write(
1787 &self,
1788 file: &FileObject,
1789 _current_task: &CurrentTask,
1790 offset: usize,
1791 data: &mut dyn InputBuffer,
1792 ) -> Result<usize, Errno> {
1793 will_dirty(&[&***file.node()], || {
1794 let written = Self::io(file).write_from_input_buffer(offset as u64, data)?;
1795
1796 if written > 0 {
1801 file.node().update_info(|info| {
1802 if offset + written > info.size {
1803 info.size = offset + written;
1804 }
1805 });
1806 }
1807
1808 Ok(written)
1809 })
1810 }
1811
1812 fn get_memory(
1813 &self,
1814 file: &FileObject,
1815 _current_task: &CurrentTask,
1816 _length: Option<usize>,
1817 prot: ProtectionFlags,
1818 ) -> Result<Arc<MemoryObject>, Errno> {
1819 fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
1820 let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
1821 Some(&self.read_exec_memory)
1822 } else if prot == ProtectionFlags::READ {
1823 Some(&self.read_only_memory)
1824 } else {
1825 None
1826 };
1827
1828 let io = Self::io(file);
1829
1830 memory_cache
1831 .map(|c| c.get_or_try_init(|| io.fetch_remote_memory(prot)).cloned())
1832 .unwrap_or_else(|| io.fetch_remote_memory(prot))
1833 }
1834
1835 fn to_handle(
1836 &self,
1837 file: &FileObject,
1838 current_task: &CurrentTask,
1839 ) -> Result<Option<zx::NullableHandle>, Errno> {
1840 serve_file_tagged(current_task, file, current_task.current_creds().clone(), "remote_files")
1843 .map(|c| Some(c.0.into_channel().into()))
1844 }
1845}
1846
1847pub struct AnonymousRemoteFileObject {
1849 io: RemoteIo,
1850
1851 read_only_memory: OnceCell<Arc<MemoryObject>>,
1853
1854 read_exec_memory: OnceCell<Arc<MemoryObject>>,
1856}
1857
1858impl AnonymousRemoteFileObject {
1859 fn new(io: RemoteIo) -> Self {
1860 Self { io, read_only_memory: Default::default(), read_exec_memory: Default::default() }
1861 }
1862}
1863
1864impl FileOps for AnonymousRemoteFileObject {
1865 fileops_impl_seekable!();
1866
1867 fn read(
1868 &self,
1869 _file: &FileObject,
1870 _current_task: &CurrentTask,
1871 offset: usize,
1872 data: &mut dyn OutputBuffer,
1873 ) -> Result<usize, Errno> {
1874 self.io.read_to_output_buffer(offset as u64, data)
1875 }
1876
1877 fn write(
1878 &self,
1879 _file: &FileObject,
1880 _current_task: &CurrentTask,
1881 offset: usize,
1882 data: &mut dyn InputBuffer,
1883 ) -> Result<usize, Errno> {
1884 self.io.write_from_input_buffer(offset as u64, data)
1887 }
1888
1889 fn get_memory(
1890 &self,
1891 _file: &FileObject,
1892 _current_task: &CurrentTask,
1893 _length: Option<usize>,
1894 prot: ProtectionFlags,
1895 ) -> Result<Arc<MemoryObject>, Errno> {
1896 fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
1897 let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
1898 Some(&self.read_exec_memory)
1899 } else if prot == ProtectionFlags::READ {
1900 Some(&self.read_only_memory)
1901 } else {
1902 None
1903 };
1904
1905 memory_cache
1906 .map(|c| c.get_or_try_init(|| self.io.fetch_remote_memory(prot)).cloned())
1907 .unwrap_or_else(|| self.io.fetch_remote_memory(prot))
1908 }
1909
1910 fn to_handle(
1911 &self,
1912 _file: &FileObject,
1913 _current_task: &CurrentTask,
1914 ) -> Result<Option<zx::NullableHandle>, Errno> {
1915 self.io
1918 .clone_proxy()
1919 .map_err(map_sync_io_client_error)
1920 .map(|p| Some(p.into_channel().into()))
1921 }
1922
1923 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1924 self.io.sync().map_err(map_sync_io_client_error)
1925 }
1926}
1927
1928pub struct RemoteZxioFileObject {
1929 zxio: Zxio,
1932
1933 read_only_memory: OnceCell<Arc<MemoryObject>>,
1935
1936 read_exec_memory: OnceCell<Arc<MemoryObject>>,
1938}
1939
1940impl RemoteZxioFileObject {
1941 fn new(zxio: Zxio) -> RemoteZxioFileObject {
1942 RemoteZxioFileObject {
1943 zxio,
1944 read_only_memory: Default::default(),
1945 read_exec_memory: Default::default(),
1946 }
1947 }
1948
1949 fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno> {
1950 let without_exec = self
1951 .zxio
1952 .vmo_get(prot.to_vmar_flags() - zx::VmarFlags::PERM_EXECUTE)
1953 .map_err(|status| from_status_like_fdio!(status))?;
1954 let all_flags = if prot.contains(ProtectionFlags::EXEC) {
1955 without_exec.replace_as_executable(&VMEX_RESOURCE).map_err(impossible_error)?
1956 } else {
1957 without_exec
1958 };
1959 Ok(Arc::new(MemoryObject::from(all_flags)))
1960 }
1961}
1962
1963impl FileOps for RemoteZxioFileObject {
1964 fileops_impl_seekable!();
1965
1966 fn read(
1967 &self,
1968 _file: &FileObject,
1969 _current_task: &CurrentTask,
1970 offset: usize,
1971 data: &mut dyn OutputBuffer,
1972 ) -> Result<usize, Errno> {
1973 let offset = offset as u64;
1974 let read_bytes = with_iovec_segments::<_, syncio::zxio::zx_iovec, _>(data, |iovecs| {
1975 unsafe { self.zxio.readv_at(offset, iovecs).map_err(map_stream_error) }
1977 });
1978
1979 match read_bytes {
1980 Some(actual) => {
1981 let actual = actual?;
1982 unsafe { data.advance(actual) }?;
1985 Ok(actual)
1986 }
1987 None => {
1988 let total = data.available();
1990 let mut bytes = vec![0u8; total];
1991 let actual = self
1992 .zxio
1993 .read_at(offset, &mut bytes)
1994 .map_err(|status| from_status_like_fdio!(status))?;
1995 data.write_all(&bytes[0..actual])
1996 }
1997 }
1998 }
1999
2000 fn write(
2001 &self,
2002 _file: &FileObject,
2003 _current_task: &CurrentTask,
2004 offset: usize,
2005 data: &mut dyn InputBuffer,
2006 ) -> Result<usize, Errno> {
2007 let offset = offset as u64;
2008 let write_bytes = with_iovec_segments::<_, syncio::zxio::zx_iovec, _>(data, |iovecs| {
2009 unsafe { self.zxio.writev_at(offset, iovecs).map_err(map_stream_error) }
2011 });
2012
2013 match write_bytes {
2014 Some(actual) => {
2015 let actual = actual?;
2016 data.advance(actual)?;
2017 Ok(actual)
2018 }
2019 None => {
2020 let bytes = data.peek_all()?;
2022 let actual = self
2023 .zxio
2024 .write_at(offset, &bytes)
2025 .map_err(|status| from_status_like_fdio!(status))?;
2026 data.advance(actual)?;
2027 Ok(actual)
2028 }
2029 }
2030 }
2031
2032 fn get_memory(
2033 &self,
2034 _file: &FileObject,
2035 _current_task: &CurrentTask,
2036 _length: Option<usize>,
2037 prot: ProtectionFlags,
2038 ) -> Result<Arc<MemoryObject>, Errno> {
2039 fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
2040 let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
2041 Some(&self.read_exec_memory)
2042 } else if prot == ProtectionFlags::READ {
2043 Some(&self.read_only_memory)
2044 } else {
2045 None
2046 };
2047
2048 memory_cache
2049 .map(|c| c.get_or_try_init(|| self.fetch_remote_memory(prot)).cloned())
2050 .unwrap_or_else(|| self.fetch_remote_memory(prot))
2051 }
2052
2053 fn to_handle(
2054 &self,
2055 _file: &FileObject,
2056 _current_task: &CurrentTask,
2057 ) -> Result<Option<zx::NullableHandle>, Errno> {
2058 self.zxio.clone_handle().map(Some).map_err(|status| from_status_like_fdio!(status))
2059 }
2060
2061 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
2062 self.zxio.sync().map_err(map_sync_error)
2063 }
2064}
2065
2066struct RemoteSymlink {
2067 node: BaseNode,
2068 target: LockDepRwLock<Box<[u8]>, FuchsiaRemoteTargetLock>,
2069}
2070
2071impl RemoteSymlink {
2072 fn new(node: BaseNode, target: impl Into<Box<[u8]>>) -> Self {
2073 Self { node, target: LockDepRwLock::new(target.into()) }
2074 }
2075}
2076
2077impl FsNodeOps for RemoteSymlink {
2078 fs_node_impl_symlink!();
2079 fs_node_impl_xattr_delegate!(self, self.node);
2080
2081 fn readlink(
2082 &self,
2083 _node: &FsNode,
2084 _current_task: &CurrentTask,
2085 ) -> Result<SymlinkTarget, Errno> {
2086 Ok(SymlinkTarget::Path(FsString::new(self.target.read().to_vec())))
2087 }
2088
2089 fn fetch_and_refresh_info<'a>(
2090 &self,
2091 _node: &FsNode,
2092 _current_task: &CurrentTask,
2093 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
2094 flags: StatxFlags,
2095 ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
2096 if flags.contains(StatxFlags::AT_STATX_DONT_SYNC) {
2097 return Ok(info.read());
2098 }
2099 self.node.fetch_and_refresh_info(info)
2100 }
2101
2102 fn forget(self: Box<Self>, _current_task: &CurrentTask, info: FsNodeInfo) -> Result<(), Errno> {
2103 if info.pending_time_access_update {
2105 self.node
2106 .io
2107 .attr_get(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
2108 .map_err(|status| from_status_like_fdio!(status))?;
2109 }
2110 Ok(())
2111 }
2112}
2113
2114pub struct RemoteCounter {
2115 counter: Counter,
2116 koid: std::sync::OnceLock<zx::Koid>,
2117}
2118
2119impl RemoteCounter {
2120 fn new(counter: Counter) -> Self {
2121 Self { counter, koid: std::sync::OnceLock::new() }
2122 }
2123
2124 pub fn duplicate_handle(&self) -> Result<Counter, Errno> {
2125 self.counter.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)
2126 }
2127
2128 pub fn koid(&self) -> zx::Koid {
2129 *self.koid.get_or_init(|| self.counter.koid().unwrap())
2130 }
2131}
2132
2133impl FileOps for RemoteCounter {
2134 fileops_impl_nonseekable!();
2135 fileops_impl_noop_sync!();
2136
2137 fn read(
2138 &self,
2139 _file: &FileObject,
2140 _current_task: &CurrentTask,
2141 _offset: usize,
2142 _data: &mut dyn OutputBuffer,
2143 ) -> Result<usize, Errno> {
2144 error!(ENOTSUP)
2145 }
2146
2147 fn write(
2148 &self,
2149 _file: &FileObject,
2150 _current_task: &CurrentTask,
2151 _offset: usize,
2152 _data: &mut dyn InputBuffer,
2153 ) -> Result<usize, Errno> {
2154 error!(ENOTSUP)
2155 }
2156
2157 fn ioctl(
2158 &self,
2159 file: &FileObject,
2160 current_task: &CurrentTask,
2161 request: u32,
2162 arg: SyscallArg,
2163 ) -> Result<SyscallResult, Errno> {
2164 let ioctl_type = (request >> 8) as u8;
2165 let ioctl_number = request as u8;
2166 if ioctl_type == SYNC_IOC_MAGIC
2167 && (ioctl_number == SYNC_IOC_FILE_INFO || ioctl_number == SYNC_IOC_MERGE)
2168 {
2169 let mut sync_points = Vec::with_capacity(1);
2170 let counter = self.duplicate_handle()?;
2171 let sp = if ioctl_number == SYNC_IOC_MERGE {
2174 SyncPoint::with_koid(Timeline::Hwc, counter.into(), self.koid())
2175 } else {
2176 SyncPoint::new(Timeline::Hwc, counter.into())
2177 };
2178 sync_points.push(sp);
2179 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";
2180 let sync_file = SyncFile::new(*sync_file_name, SyncFence { sync_points });
2181 return sync_file.ioctl(file, current_task, request, arg);
2182 }
2183
2184 error!(EINVAL)
2185 }
2186}
2187
2188#[track_caller]
2189fn map_sync_error(status: zx::Status) -> Errno {
2190 match status {
2191 zx::Status::NO_RESOURCES | zx::Status::NO_MEMORY | zx::Status::NO_SPACE => {
2192 errno!(ENOSPC)
2193 }
2194 zx::Status::INVALID_ARGS | zx::Status::NOT_FILE => errno!(EINVAL),
2195 zx::Status::BAD_HANDLE => errno!(EBADFD),
2196 zx::Status::NOT_SUPPORTED => errno!(ENOTSUP),
2197 zx::Status::INTERRUPTED_RETRY => errno!(EINTR),
2198 _ => errno!(EIO),
2199 }
2200}
2201
2202#[track_caller]
2203fn map_stream_error(status: zx::Status) -> Errno {
2204 match status {
2205 zx::Status::INVALID_ARGS | zx::Status::NOT_FOUND => errno!(EFAULT),
2208 status => from_status_like_fdio!(status),
2209 }
2210}
2211
2212#[track_caller]
2213fn map_sync_io_client_error(status: zx::Status) -> Errno {
2214 from_status_like_fdio!(status)
2215}
2216
2217struct InfoState(AtomicU32);
2222
2223impl InfoState {
2224 const IN_SYNC: u32 = 0x8000_0000;
2227
2228 const PENDING_REFRESH: u32 = 0x4000_0000;
2231
2232 const TRUNCATED: u32 = 0x2000_0000;
2235
2236 const COUNT_MASK: u32 = Self::TRUNCATED - 1;
2238
2239 fn new(dirty: bool) -> Self {
2240 Self(AtomicU32::new(if dirty { 0 } else { Self::IN_SYNC }))
2241 }
2242
2243 fn dirty_op_guard(&self, for_truncate: bool) -> DirtyOpGuard<'_> {
2246 let mut current = self.0.load(Ordering::Relaxed);
2250 let for_truncate = if for_truncate { Self::TRUNCATED } else { 0 };
2251 loop {
2252 assert!(current & Self::COUNT_MASK != Self::COUNT_MASK); match self.0.compare_exchange_weak(
2254 current,
2255 ((current & !Self::IN_SYNC) + 1) | for_truncate,
2256 Ordering::Relaxed,
2257 Ordering::Relaxed,
2258 ) {
2259 Ok(_) => break,
2260 Err(old) => current = old,
2261 }
2262 }
2263 DirtyOpGuard(self)
2264 }
2265
2266 fn maybe_refresh<'a, T: 'a>(
2269 &self,
2270 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
2271 refresh: impl FnOnce(&'a DynamicLockDepRwLock<FsNodeInfo>) -> Result<T, Errno>,
2272 not_needed: impl FnOnce(&'a DynamicLockDepRwLock<FsNodeInfo>) -> Result<T, Errno>,
2273 ) -> Result<T, Errno> {
2274 let mut current = self.0.load(Ordering::Relaxed);
2275
2276 let mut did_set_pending_refresh = false;
2284 while current & !Self::TRUNCATED == 0 {
2285 match self.0.compare_exchange_weak(
2286 current,
2287 current | Self::IN_SYNC | Self::PENDING_REFRESH,
2288 Ordering::Relaxed,
2289 Ordering::Relaxed,
2290 ) {
2291 Ok(_) => {
2292 did_set_pending_refresh = true;
2293 break;
2294 }
2295 Err(old) => current = old,
2296 }
2297 }
2298
2299 if current == Self::IN_SYNC {
2303 return not_needed(info);
2304 }
2305
2306 let result = refresh(info);
2307
2308 if did_set_pending_refresh {
2309 if result.is_ok() {
2310 if current & Self::TRUNCATED != 0 {
2313 let mut current = Self::TRUNCATED | Self::IN_SYNC | Self::PENDING_REFRESH;
2316 while current == Self::TRUNCATED | Self::IN_SYNC | Self::PENDING_REFRESH {
2317 match self.0.compare_exchange_weak(
2318 current,
2319 Self::IN_SYNC,
2320 Ordering::Relaxed,
2321 Ordering::Relaxed,
2322 ) {
2323 Ok(_) => return result,
2324 Err(old) => current = old,
2325 }
2326 }
2327 }
2330 self.0.fetch_and(!Self::PENDING_REFRESH, Ordering::Relaxed);
2331 } else {
2332 self.0.fetch_and(!(Self::IN_SYNC | Self::PENDING_REFRESH), Ordering::Relaxed);
2335 }
2336 }
2337
2338 result
2339 }
2340
2341 fn is_size_accurate(&self) -> bool {
2343 let state = self.0.load(Ordering::Relaxed);
2357 state & (Self::TRUNCATED | Self::COUNT_MASK) == 0
2358 }
2359}
2360
2361struct DirtyOpGuard<'a>(&'a InfoState);
2362
2363impl Drop for DirtyOpGuard<'_> {
2364 fn drop(&mut self) {
2365 self.0.0.fetch_sub(1, Ordering::Relaxed);
2367 }
2368}
2369
2370fn will_dirty<'a, N: TryInto<&'a BaseNode> + Copy, T>(nodes: &[N], f: impl FnOnce() -> T) -> T {
2372 let _guards: SmallVec<[_; 4]> = nodes
2386 .iter()
2387 .filter_map(|n| N::try_into(*n).ok())
2388 .map(|n| n.info_state.dirty_op_guard(false))
2389 .collect();
2390
2391 f()
2392}
2393
2394#[cfg(test)]
2395mod test {
2396 use super::*;
2397 use crate::mm::PAGE_SIZE;
2398 use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
2399 use crate::testing::*;
2400 use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
2401 use crate::vfs::socket::{SocketFile, SocketMessageFlags};
2402 use crate::vfs::{EpollFileObject, LookupContext, Namespace, SymlinkMode, TimeUpdateType};
2403 use assert_matches::assert_matches;
2404 use fidl::endpoints::{ServerEnd, create_request_stream};
2405 use fidl_fuchsia_io as fio;
2406 use flyweights::FlyByteStr;
2407 use fuchsia_async as fasync;
2408 use fuchsia_runtime::UtcDuration;
2409 use futures::{FutureExt, StreamExt};
2410 use fxfs_testing::{TestFixture, TestFixtureOptions};
2411 use starnix_sync::{FsNodeInfoLevel, Mutex};
2412 use starnix_uapi::auth::Credentials;
2413 use starnix_uapi::errors::EINVAL;
2414 use starnix_uapi::file_mode::mode;
2415 use starnix_uapi::ino_t;
2416 use starnix_uapi::mount_flags::MountpointFlags;
2417 use starnix_uapi::open_flags::OpenFlags;
2418 use starnix_uapi::vfs::{EpollEvent, FdEvents};
2419 use std::sync::Barrier;
2420 use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
2421 use storage_device::DeviceHolder;
2422 use storage_device::fake_device::FakeDevice;
2423
2424 #[::fuchsia::test]
2425 async fn test_remote_uds() {
2426 spawn_kernel_and_run(async |current_task| {
2427 let (s1, s2) = zx::Socket::create_datagram();
2428 s2.write(&vec![0]).expect("write");
2429 let file = new_remote_file(¤t_task, s1.into(), OpenFlags::RDWR)
2430 .expect("new_remote_file");
2431 assert!(file.node().is_sock());
2432 let socket_ops = file.downcast_file::<SocketFile>().unwrap();
2433 let flags = SocketMessageFlags::CTRUNC
2434 | SocketMessageFlags::TRUNC
2435 | SocketMessageFlags::NOSIGNAL
2436 | SocketMessageFlags::CMSG_CLOEXEC;
2437 let mut buffer = VecOutputBuffer::new(1024);
2438 let info = socket_ops
2439 .recvmsg(¤t_task, &file, &mut buffer, flags, None)
2440 .expect("recvmsg");
2441 assert!(info.ancillary_data.is_empty());
2442 assert_eq!(info.message_length, 1);
2443 })
2444 .await;
2445 }
2446
2447 #[::fuchsia::test]
2448 async fn test_tree() {
2449 spawn_kernel_and_run(async |current_task| {
2450 let kernel = current_task.kernel();
2451 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
2452 let (server, client) = zx::Channel::create();
2453 fdio::open("/pkg", rights, server).expect("failed to open /pkg");
2454 let fs = RemoteFs::new_fs(
2455 &kernel,
2456 client,
2457 FileSystemOptions { source: FlyByteStr::new(b"/pkg"), ..Default::default() },
2458 rights,
2459 )
2460 .unwrap();
2461 let ns = Namespace::new(fs);
2462 let root = ns.root();
2463 let mut context = LookupContext::default();
2464 assert_eq!(
2465 root.lookup_child(¤t_task, &mut context, "nib".into()).err(),
2466 Some(errno!(ENOENT))
2467 );
2468 let mut context = LookupContext::default();
2469 root.lookup_child(¤t_task, &mut context, "lib".into()).unwrap();
2470
2471 let mut context = LookupContext::default();
2472 let _test_file = root
2473 .lookup_child(¤t_task, &mut context, "data/tests/hello_starnix".into())
2474 .unwrap()
2475 .open(¤t_task, OpenFlags::RDONLY)
2476 .unwrap();
2477 })
2478 .await;
2479 }
2480
2481 #[::fuchsia::test]
2482 async fn test_blocking_io() {
2483 spawn_kernel_and_run(async |current_task| {
2484 let (client, server) = zx::Socket::create_stream();
2485 let pipe = create_fuchsia_pipe(¤t_task, client, OpenFlags::RDWR).unwrap();
2486
2487 let bytes = [0u8; 64];
2488 assert_eq!(bytes.len(), server.write(&bytes).unwrap());
2489
2490 let bytes_read = pipe.read(¤t_task, &mut VecOutputBuffer::new(64)).unwrap();
2492
2493 assert_eq!(bytes_read, bytes.len());
2494 })
2495 .await;
2496 }
2497
2498 #[::fuchsia::test]
2499 async fn test_poll() {
2500 spawn_kernel_and_run(async |current_task| {
2501 let (client, server) = zx::Socket::create_stream();
2502 let pipe = create_fuchsia_pipe(¤t_task, client, OpenFlags::RDWR)
2503 .expect("create_fuchsia_pipe");
2504 let server_zxio = Zxio::create(server.into_handle()).expect("Zxio::create");
2505
2506 assert_eq!(
2507 pipe.query_events(¤t_task),
2508 Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
2509 );
2510
2511 let epoll_object = EpollFileObject::new_file(¤t_task);
2512 let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
2513 let event = EpollEvent::new(FdEvents::POLLIN, 0);
2514 epoll_file.add(¤t_task, &pipe, &epoll_object, event).expect("poll_file.add");
2515
2516 let fds = epoll_file.wait(¤t_task, 1, zx::MonotonicInstant::ZERO).expect("wait");
2517 assert!(fds.is_empty());
2518
2519 assert_eq!(server_zxio.write(&[0]).expect("write"), 1);
2520
2521 assert_eq!(
2522 pipe.query_events(¤t_task),
2523 Ok(FdEvents::POLLOUT
2524 | FdEvents::POLLWRNORM
2525 | FdEvents::POLLIN
2526 | FdEvents::POLLRDNORM)
2527 );
2528 let fds = epoll_file.wait(¤t_task, 1, zx::MonotonicInstant::ZERO).expect("wait");
2529 assert_eq!(fds.len(), 1);
2530
2531 assert_eq!(pipe.read(¤t_task, &mut VecOutputBuffer::new(64)).expect("read"), 1);
2532
2533 assert_eq!(
2534 pipe.query_events(¤t_task),
2535 Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
2536 );
2537 let fds = epoll_file.wait(¤t_task, 1, zx::MonotonicInstant::ZERO).expect("wait");
2538 assert!(fds.is_empty());
2539 })
2540 .await;
2541 }
2542
2543 #[::fuchsia::test]
2544 async fn test_new_remote_directory() {
2545 spawn_kernel_and_run(async |current_task| {
2546 let (server, client) = zx::Channel::create();
2547 fdio::open("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE, server)
2548 .expect("failed to open /pkg");
2549
2550 let fd = new_remote_file(¤t_task, client.into(), OpenFlags::RDWR)
2551 .expect("new_remote_file");
2552 assert!(fd.node().is_dir());
2553 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2554 })
2555 .await;
2556 }
2557
2558 #[::fuchsia::test]
2559 async fn test_new_remote_file() {
2560 spawn_kernel_and_run(async |current_task| {
2561 let (server, client) = zx::Channel::create();
2562 fdio::open("/pkg/meta/contents", fio::PERM_READABLE, server)
2563 .expect("failed to open /pkg/meta/contents");
2564
2565 let fd = new_remote_file(¤t_task, client.into(), OpenFlags::RDONLY)
2566 .expect("new_remote_file");
2567 assert!(!fd.node().is_dir());
2568 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2569 })
2570 .await;
2571 }
2572
2573 #[::fuchsia::test]
2574 async fn test_new_remote_counter() {
2575 spawn_kernel_and_run(async |current_task| {
2576 let counter = zx::Counter::create();
2577
2578 let fd = new_remote_file(¤t_task, counter.into(), OpenFlags::RDONLY)
2579 .expect("new_remote_file");
2580 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2581 })
2582 .await;
2583 }
2584
2585 #[::fuchsia::test]
2586 async fn test_new_remote_vmo() {
2587 spawn_kernel_and_run(async |current_task| {
2588 let vmo = zx::Vmo::create(*PAGE_SIZE).expect("Vmo::create");
2589 let fd = new_remote_file(¤t_task, vmo.into(), OpenFlags::RDWR)
2590 .expect("new_remote_file");
2591 assert!(!fd.node().is_dir());
2592 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2593 })
2594 .await;
2595 }
2596
2597 #[::fuchsia::test(threads = 2)]
2598 async fn test_symlink() {
2599 let fixture = TestFixture::new().await;
2600 let (server, client) = zx::Channel::create();
2601 fixture.root().clone(server.into()).expect("clone failed");
2602
2603 const LINK_PATH: &'static str = "symlink";
2604 const LINK_TARGET: &'static str = "私は「UTF8」です";
2605 const LINK_SIZE: usize = 22;
2608 assert_eq!(LINK_SIZE, LINK_TARGET.len());
2609
2610 spawn_kernel_and_run(async move |current_task| {
2611 let kernel = current_task.kernel();
2612 let fs = RemoteFs::new_fs(
2613 &kernel,
2614 client,
2615 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2616 fio::PERM_READABLE | fio::PERM_WRITABLE,
2617 )
2618 .expect("new_fs failed");
2619 let ns = Namespace::new(fs);
2620 let root = ns.root();
2621 let symlink_node = root
2622 .create_symlink(¤t_task, LINK_PATH.into(), LINK_TARGET.into())
2623 .expect("symlink failed");
2624 assert_matches!(&*symlink_node.entry.node.info(), FsNodeInfo { size: LINK_SIZE, .. });
2625
2626 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2627 let child = root
2628 .lookup_child(¤t_task, &mut context, "symlink".into())
2629 .expect("lookup_child failed");
2630
2631 match child.readlink(¤t_task).expect("readlink failed") {
2632 SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2633 SymlinkTarget::Node(_) => panic!("readlink returned SymlinkTarget::Node"),
2634 }
2635 let stat_result = child.entry.node.stat(¤t_task).expect("stat failed");
2637 assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2638 })
2639 .await;
2640
2641 let fixture = TestFixture::open(
2643 fixture.close().await,
2644 TestFixtureOptions { format: false, ..Default::default() },
2645 )
2646 .await;
2647 let (server, client) = zx::Channel::create();
2648 fixture.root().clone(server.into()).expect("clone failed after remount");
2649
2650 spawn_kernel_and_run(async move |current_task| {
2651 let kernel = current_task.kernel();
2652 let fs = RemoteFs::new_fs(
2653 &kernel,
2654 client,
2655 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2656 fio::PERM_READABLE | fio::PERM_WRITABLE,
2657 )
2658 .expect("new_fs failed after remount");
2659 let ns = Namespace::new(fs);
2660 let root = ns.root();
2661 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2662 let child = root
2663 .lookup_child(¤t_task, &mut context, "symlink".into())
2664 .expect("lookup_child failed after remount");
2665
2666 match child.readlink(¤t_task).expect("readlink failed after remount") {
2667 SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2668 SymlinkTarget::Node(_) => {
2669 panic!("readlink returned SymlinkTarget::Node after remount")
2670 }
2671 }
2672 let stat_result =
2674 child.entry.node.stat(¤t_task).expect("stat failed after remount");
2675 assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2676 })
2677 .await;
2678
2679 fixture.close().await;
2680 }
2681
2682 #[::fuchsia::test]
2683 async fn test_mode_uid_gid_and_dev_persists() {
2684 const FILE_MODE: FileMode = mode!(IFREG, 0o467);
2685 const DIR_MODE: FileMode = mode!(IFDIR, 0o647);
2686 const BLK_MODE: FileMode = mode!(IFBLK, 0o746);
2687
2688 let fixture = TestFixture::new().await;
2689 let (server, client) = zx::Channel::create();
2690 fixture.root().clone(server.into()).expect("clone failed");
2691
2692 spawn_kernel_and_run(async move |current_task| {
2694 let kernel = current_task.kernel();
2695 let creds = Credentials::clone(¤t_task.current_creds());
2696 current_task.set_creds(Credentials { euid: 1, fsuid: 1, egid: 2, fsgid: 2, ..creds });
2697 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2698 let fs = RemoteFs::new_fs(
2699 &kernel,
2700 client,
2701 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2702 rights,
2703 )
2704 .expect("new_fs failed");
2705 let ns = Namespace::new(fs);
2706 current_task.fs().set_umask(FileMode::from_bits(0));
2707 ns.root()
2708 .create_node(¤t_task, "file".into(), FILE_MODE, DeviceId::NONE)
2709 .expect("create_node failed");
2710 ns.root()
2711 .create_node(¤t_task, "dir".into(), DIR_MODE, DeviceId::NONE)
2712 .expect("create_node failed");
2713 ns.root()
2714 .create_node(¤t_task, "dev".into(), BLK_MODE, DeviceId::RANDOM)
2715 .expect("create_node failed");
2716 })
2717 .await;
2718
2719 let fixture = TestFixture::open(
2721 fixture.close().await,
2722 TestFixtureOptions { format: false, ..Default::default() },
2723 )
2724 .await;
2725
2726 let (server, client) = zx::Channel::create();
2727 fixture.root().clone(server.into()).expect("clone failed");
2728
2729 spawn_kernel_and_run(async move |current_task| {
2730 let kernel = current_task.kernel();
2731 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2732 let fs = RemoteFs::new_fs(
2733 &kernel,
2734 client,
2735 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2736 rights,
2737 )
2738 .expect("new_fs failed");
2739 let ns = Namespace::new(fs);
2740 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2741 let child = ns
2742 .root()
2743 .lookup_child(¤t_task, &mut context, "file".into())
2744 .expect("lookup_child failed");
2745 assert_matches!(
2746 &*child.entry.node.info(),
2747 FsNodeInfo { mode: FILE_MODE, uid: 1, gid: 2, rdev: DeviceId::NONE, .. }
2748 );
2749 let child = ns
2750 .root()
2751 .lookup_child(¤t_task, &mut context, "dir".into())
2752 .expect("lookup_child failed");
2753 assert_matches!(
2754 &*child.entry.node.info(),
2755 FsNodeInfo { mode: DIR_MODE, uid: 1, gid: 2, rdev: DeviceId::NONE, .. }
2756 );
2757 let child = ns
2758 .root()
2759 .lookup_child(¤t_task, &mut context, "dev".into())
2760 .expect("lookup_child failed");
2761 assert_matches!(
2762 &*child.entry.node.info(),
2763 FsNodeInfo { mode: BLK_MODE, uid: 1, gid: 2, rdev: DeviceId::RANDOM, .. }
2764 );
2765 })
2766 .await;
2767 fixture.close().await;
2768 }
2769
2770 #[::fuchsia::test]
2771 async fn test_dot_dot_inode_numbers() {
2772 let fixture = TestFixture::new().await;
2773 let (server, client) = zx::Channel::create();
2774 fixture.root().clone(server.into()).expect("clone failed");
2775
2776 const MODE: FileMode = FileMode::from_bits(FileMode::IFDIR.bits() | 0o777);
2777
2778 spawn_kernel_and_run(async |current_task| {
2779 let kernel = current_task.kernel();
2780 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2781 let fs = RemoteFs::new_fs(
2782 &kernel,
2783 client,
2784 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2785 rights,
2786 )
2787 .expect("new_fs failed");
2788 let ns = Namespace::new(fs);
2789 current_task.fs().set_umask(FileMode::from_bits(0));
2790 let sub_dir1 = ns
2791 .root()
2792 .create_node(¤t_task, "dir".into(), MODE, DeviceId::NONE)
2793 .expect("create_node failed");
2794 let sub_dir2 = sub_dir1
2795 .create_node(¤t_task, "dir".into(), MODE, DeviceId::NONE)
2796 .expect("create_node failed");
2797
2798 let dir_handle = ns
2799 .root()
2800 .entry
2801 .open_anonymous(¤t_task, OpenFlags::RDONLY)
2802 .expect("open failed");
2803
2804 #[derive(Default)]
2805 struct Sink {
2806 offset: off_t,
2807 dot_inode_num: u64,
2808 dot_dot_inode_num: u64,
2809 }
2810 impl DirentSink for Sink {
2811 fn add(
2812 &mut self,
2813 inode_num: ino_t,
2814 offset: off_t,
2815 entry_type: DirectoryEntryType,
2816 name: &FsStr,
2817 ) -> Result<(), Errno> {
2818 if name == "." {
2819 self.dot_inode_num = inode_num;
2820 assert_eq!(entry_type, DirectoryEntryType::DIR);
2821 } else if name == ".." {
2822 self.dot_dot_inode_num = inode_num;
2823 assert_eq!(entry_type, DirectoryEntryType::DIR);
2824 }
2825 self.offset = offset;
2826 Ok(())
2827 }
2828 fn offset(&self) -> off_t {
2829 self.offset
2830 }
2831 }
2832 let mut sink = Sink::default();
2833 dir_handle.readdir(¤t_task, &mut sink).expect("readdir failed");
2834
2835 assert_eq!(sink.dot_inode_num, ns.root().entry.node.ino);
2837 assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2838
2839 let dir_handle = sub_dir1
2840 .entry
2841 .open_anonymous(¤t_task, OpenFlags::RDONLY)
2842 .expect("open failed");
2843 let mut sink = Sink::default();
2844 dir_handle.readdir(¤t_task, &mut sink).expect("readdir failed");
2845
2846 assert_eq!(sink.dot_inode_num, sub_dir1.entry.node.ino);
2848 assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2849
2850 let dir_handle = sub_dir2
2851 .entry
2852 .open_anonymous(¤t_task, OpenFlags::RDONLY)
2853 .expect("open failed");
2854 let mut sink = Sink::default();
2855 dir_handle.readdir(¤t_task, &mut sink).expect("readdir failed");
2856
2857 assert_eq!(sink.dot_inode_num, sub_dir2.entry.node.ino);
2859 assert_eq!(sink.dot_dot_inode_num, sub_dir1.entry.node.ino);
2860 })
2861 .await;
2862 fixture.close().await;
2863 }
2864
2865 #[::fuchsia::test]
2866 async fn test_remote_special_node() {
2867 let fixture = TestFixture::new().await;
2868 let (server, client) = zx::Channel::create();
2869 fixture.root().clone(server.into()).expect("clone failed");
2870
2871 const FIFO_MODE: FileMode = FileMode::from_bits(FileMode::IFIFO.bits() | 0o777);
2872 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2873
2874 spawn_kernel_and_run(async |current_task| {
2875 let kernel = current_task.kernel();
2876 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2877 let fs = RemoteFs::new_fs(
2878 &kernel,
2879 client,
2880 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2881 rights,
2882 )
2883 .expect("new_fs failed");
2884 let ns = Namespace::new(fs);
2885 current_task.fs().set_umask(FileMode::from_bits(0));
2886 let root = ns.root();
2887
2888 root.create_node(¤t_task, "fifo".into(), FIFO_MODE, DeviceId::NONE)
2890 .expect("create_node failed");
2891 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2892 let fifo_node = root
2893 .lookup_child(¤t_task, &mut context, "fifo".into())
2894 .expect("lookup_child failed");
2895
2896 match fifo_node.truncate(¤t_task, 0) {
2899 Ok(_) => {
2900 panic!("truncate passed for special node")
2901 }
2902 Err(errno) if errno == EINVAL => {}
2903 Err(e) => {
2904 panic!("truncate failed with error {:?}", e)
2905 }
2906 };
2907
2908 root.create_node(¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
2910 .expect("create_node failed");
2911 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2912 let reg_node = root
2913 .lookup_child(¤t_task, &mut context, "file".into())
2914 .expect("lookup_child failed");
2915
2916 reg_node.truncate(¤t_task, 0).expect("truncate failed");
2918 })
2919 .await;
2920 fixture.close().await;
2921 }
2922
2923 #[::fuchsia::test]
2924 async fn test_hard_link() {
2925 let fixture = TestFixture::new().await;
2926 let (server, client) = zx::Channel::create();
2927 fixture.root().clone(server.into()).expect("clone failed");
2928
2929 spawn_kernel_and_run(async move |current_task| {
2930 let kernel = current_task.kernel();
2931 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2932 let fs = RemoteFs::new_fs(
2933 &kernel,
2934 client,
2935 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2936 rights,
2937 )
2938 .expect("new_fs failed");
2939 let ns = Namespace::new(fs);
2940 current_task.fs().set_umask(FileMode::from_bits(0));
2941 let node = ns
2942 .root()
2943 .create_node(¤t_task, "file1".into(), mode!(IFREG, 0o666), DeviceId::NONE)
2944 .expect("create_node failed");
2945 ns.root()
2946 .entry
2947 .node
2948 .link(¤t_task, &ns.root().mount, "file2".into(), &node.entry.node)
2949 .expect("link failed");
2950 })
2951 .await;
2952
2953 let fixture = TestFixture::open(
2954 fixture.close().await,
2955 TestFixtureOptions { format: false, ..Default::default() },
2956 )
2957 .await;
2958
2959 let (server, client) = zx::Channel::create();
2960 fixture.root().clone(server.into()).expect("clone failed");
2961
2962 spawn_kernel_and_run(async move |current_task| {
2963 let kernel = current_task.kernel();
2964 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2965 let fs = RemoteFs::new_fs(
2966 &kernel,
2967 client,
2968 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2969 rights,
2970 )
2971 .expect("new_fs failed");
2972 let ns = Namespace::new(fs);
2973 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2974 let child1 = ns
2975 .root()
2976 .lookup_child(¤t_task, &mut context, "file1".into())
2977 .expect("lookup_child failed");
2978 let child2 = ns
2979 .root()
2980 .lookup_child(¤t_task, &mut context, "file2".into())
2981 .expect("lookup_child failed");
2982 assert!(Arc::ptr_eq(&child1.entry.node, &child2.entry.node));
2983 })
2984 .await;
2985 fixture.close().await;
2986 }
2987
2988 #[::fuchsia::test]
2989 async fn test_lookup_on_fsverity_enabled_file() {
2990 let fixture = TestFixture::new().await;
2991 let (server, client) = zx::Channel::create();
2992 fixture.root().clone(server.into()).expect("clone failed");
2993
2994 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
2995
2996 spawn_kernel_and_run(async move |current_task| {
2997 let kernel = current_task.kernel();
2998 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2999 let fs = RemoteFs::new_fs(
3000 &kernel,
3001 client,
3002 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3003 rights,
3004 )
3005 .expect("new_fs failed");
3006 let ns = Namespace::new(fs);
3007 current_task.fs().set_umask(FileMode::from_bits(0));
3008 let file = ns
3009 .root()
3010 .create_node(¤t_task, "file".into(), MODE, DeviceId::NONE)
3011 .expect("create_node failed");
3012 let desc = fsverity_descriptor {
3014 version: 1,
3015 hash_algorithm: 1,
3016 salt_size: 32,
3017 log_blocksize: 12,
3018 ..Default::default()
3019 };
3020 file.entry.node.enable_fsverity(current_task, &desc).expect("enable fsverity failed");
3021 })
3022 .await;
3023
3024 let fixture = TestFixture::open(
3027 fixture.close().await,
3028 TestFixtureOptions { format: false, ..Default::default() },
3029 )
3030 .await;
3031 let (server, client) = zx::Channel::create();
3032 fixture.root().clone(server.into()).expect("clone failed");
3033
3034 spawn_kernel_and_run(async move |current_task| {
3035 let kernel = current_task.kernel();
3036 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3037 let fs = RemoteFs::new_fs(
3038 &kernel,
3039 client,
3040 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3041 rights,
3042 )
3043 .expect("new_fs failed");
3044 let ns = Namespace::new(fs);
3045 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3046 let _child = ns
3047 .root()
3048 .lookup_child(¤t_task, &mut context, "file".into())
3049 .expect("lookup_child failed");
3050 })
3051 .await;
3052 fixture.close().await;
3053 }
3054
3055 #[::fuchsia::test]
3056 async fn test_update_attributes_persists() {
3057 let fixture = TestFixture::new().await;
3058 let (server, client) = zx::Channel::create();
3059 fixture.root().clone(server.into()).expect("clone failed");
3060
3061 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3062
3063 spawn_kernel_and_run(async move |current_task| {
3064 let kernel = current_task.kernel();
3065 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3066 let fs = RemoteFs::new_fs(
3067 &kernel,
3068 client,
3069 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3070 rights,
3071 )
3072 .expect("new_fs failed");
3073 let ns = Namespace::new(fs);
3074 current_task.fs().set_umask(FileMode::from_bits(0));
3075 let file = ns
3076 .root()
3077 .create_node(¤t_task, "file".into(), MODE, DeviceId::NONE)
3078 .expect("create_node failed");
3079 file.entry
3081 .node
3082 .chmod(¤t_task, &file.mount, MODE | FileMode::ALLOW_ALL)
3083 .expect("chmod failed");
3084 })
3085 .await;
3086
3087 let fixture = TestFixture::open(
3089 fixture.close().await,
3090 TestFixtureOptions { format: false, ..Default::default() },
3091 )
3092 .await;
3093 let (server, client) = zx::Channel::create();
3094 fixture.root().clone(server.into()).expect("clone failed");
3095
3096 spawn_kernel_and_run(async move |current_task| {
3097 let kernel = current_task.kernel();
3098 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3099 let fs = RemoteFs::new_fs(
3100 &kernel,
3101 client,
3102 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3103 rights,
3104 )
3105 .expect("new_fs failed");
3106 let ns = Namespace::new(fs);
3107 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3108 let child = ns
3109 .root()
3110 .lookup_child(¤t_task, &mut context, "file".into())
3111 .expect("lookup_child failed");
3112 assert_eq!(child.entry.node.info().mode, MODE | FileMode::ALLOW_ALL);
3113 })
3114 .await;
3115 fixture.close().await;
3116 }
3117
3118 #[::fuchsia::test]
3119 async fn test_statfs() {
3120 let fixture = TestFixture::new().await;
3121 let (server, client) = zx::Channel::create();
3122 fixture.root().clone(server.into()).expect("clone failed");
3123
3124 spawn_kernel_and_run(async move |current_task| {
3125 let kernel = current_task.kernel();
3126 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3127 let fs = RemoteFs::new_fs(
3128 &kernel,
3129 client,
3130 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3131 rights,
3132 )
3133 .expect("new_fs failed");
3134
3135 let statfs = fs.statfs(¤t_task).expect("statfs failed");
3136 assert!(statfs.f_type != 0);
3137 assert!(statfs.f_bsize > 0);
3138 assert!(statfs.f_blocks > 0);
3139 assert!(statfs.f_bfree > 0 && statfs.f_bfree <= statfs.f_blocks);
3140 assert!(statfs.f_files > 0);
3141 assert!(statfs.f_ffree > 0 && statfs.f_ffree <= statfs.f_files);
3142 assert!(statfs.f_fsid.val[0] != 0 || statfs.f_fsid.val[1] != 0);
3143 assert!(statfs.f_namelen > 0);
3144 assert!(statfs.f_frsize > 0);
3145 })
3146 .await;
3147
3148 fixture.close().await;
3149 }
3150
3151 #[::fuchsia::test]
3152 async fn test_allocate() {
3153 let fixture = TestFixture::new().await;
3154 let (server, client) = zx::Channel::create();
3155 fixture.root().clone(server.into()).expect("clone failed");
3156
3157 spawn_kernel_and_run(async move |current_task| {
3158 let kernel = current_task.kernel();
3159 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3160 let fs = RemoteFs::new_fs(
3161 &kernel,
3162 client,
3163 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3164 rights,
3165 )
3166 .expect("new_fs failed");
3167 let ns = Namespace::new(fs);
3168 current_task.fs().set_umask(FileMode::from_bits(0));
3169 let root = ns.root();
3170
3171 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
3172 root.create_node(¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
3173 .expect("create_node failed");
3174 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3175 let reg_node = root
3176 .lookup_child(¤t_task, &mut context, "file".into())
3177 .expect("lookup_child failed");
3178
3179 reg_node
3180 .entry
3181 .node
3182 .fallocate(¤t_task, FallocMode::Allocate { keep_size: false }, 0, 20)
3183 .expect("truncate failed");
3184 })
3185 .await;
3186 fixture.close().await;
3187 }
3188
3189 #[::fuchsia::test]
3190 async fn test_allocate_overflow() {
3191 let fixture = TestFixture::new().await;
3192 let (server, client) = zx::Channel::create();
3193 fixture.root().clone(server.into()).expect("clone failed");
3194
3195 spawn_kernel_and_run(async move |current_task| {
3196 let kernel = current_task.kernel();
3197 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3198 let fs = RemoteFs::new_fs(
3199 &kernel,
3200 client,
3201 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3202 rights,
3203 )
3204 .expect("new_fs failed");
3205 let ns = Namespace::new(fs);
3206 current_task.fs().set_umask(FileMode::from_bits(0));
3207 let root = ns.root();
3208
3209 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
3210 root.create_node(¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
3211 .expect("create_node failed");
3212 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3213 let reg_node = root
3214 .lookup_child(¤t_task, &mut context, "file".into())
3215 .expect("lookup_child failed");
3216
3217 reg_node
3218 .entry
3219 .node
3220 .fallocate(¤t_task, FallocMode::Allocate { keep_size: false }, 1, u64::MAX)
3221 .expect_err("truncate unexpectedly passed");
3222 })
3223 .await;
3224 fixture.close().await;
3225 }
3226
3227 #[::fuchsia::test]
3228 async fn test_time_modify_persists() {
3229 let fixture = TestFixture::new().await;
3230 let (server, client) = zx::Channel::create();
3231 fixture.root().clone(server.into()).expect("clone failed");
3232
3233 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3234
3235 let last_modified = spawn_kernel_and_run(async move |current_task| {
3236 let kernel = current_task.kernel();
3237 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3238 let fs = RemoteFs::new_fs(
3239 &kernel,
3240 client,
3241 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3242 rights,
3243 )
3244 .expect("new_fs failed");
3245 let ns: Arc<Namespace> = Namespace::new(fs);
3246 current_task.fs().set_umask(FileMode::from_bits(0));
3247 let child = ns
3248 .root()
3249 .create_node(¤t_task, "file".into(), MODE, DeviceId::NONE)
3250 .expect("create_node failed");
3251 let file = child.open(¤t_task, OpenFlags::RDWR).expect("open failed");
3253 let time_before_write = child
3256 .entry
3257 .node
3258 .fetch_and_refresh_info(¤t_task)
3259 .expect("fetch_and_refresh_info failed")
3260 .time_modify;
3261 let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3262 let written = file
3263 .write(¤t_task, &mut VecInputBuffer::new(&write_bytes))
3264 .expect("write failed");
3265 assert_eq!(written, write_bytes.len());
3266 let last_modified = child
3267 .entry
3268 .node
3269 .fetch_and_refresh_info(¤t_task)
3270 .expect("fetch_and_refresh_info failed")
3271 .time_modify;
3272 assert!(last_modified > time_before_write);
3273 last_modified
3274 })
3275 .await;
3276
3277 let fixture = TestFixture::open(
3280 fixture.close().await,
3281 TestFixtureOptions { format: false, ..Default::default() },
3282 )
3283 .await;
3284 let (server, client) = zx::Channel::create();
3285 fixture.root().clone(server.into()).expect("clone failed");
3286 let refreshed_modified_time = spawn_kernel_and_run(async move |current_task| {
3287 let kernel = current_task.kernel();
3288 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3289 let fs = RemoteFs::new_fs(
3290 &kernel,
3291 client,
3292 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3293 rights,
3294 )
3295 .expect("new_fs failed");
3296 let ns = Namespace::new(fs);
3297 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3298 let child = ns
3299 .root()
3300 .lookup_child(¤t_task, &mut context, "file".into())
3301 .expect("lookup_child failed");
3302 let last_modified = child
3303 .entry
3304 .node
3305 .fetch_and_refresh_info(¤t_task)
3306 .expect("fetch_and_refresh_info failed")
3307 .time_modify;
3308 last_modified
3309 })
3310 .await;
3311 assert_eq!(last_modified, refreshed_modified_time);
3312
3313 fixture.close().await;
3314 }
3315
3316 #[::fuchsia::test]
3317 async fn test_update_atime_mtime() {
3318 let fixture = TestFixture::new().await;
3319 let (server, client) = zx::Channel::create();
3320 fixture.root().clone(server.into()).expect("clone failed");
3321
3322 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3323
3324 spawn_kernel_and_run(async move |current_task| {
3325 let kernel = current_task.kernel();
3326 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3327 let fs = RemoteFs::new_fs(
3328 &kernel,
3329 client,
3330 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3331 rights,
3332 )
3333 .expect("new_fs failed");
3334 let ns: Arc<Namespace> = Namespace::new(fs);
3335 current_task.fs().set_umask(FileMode::from_bits(0));
3336 let child = ns
3337 .root()
3338 .create_node(¤t_task, "file".into(), MODE, DeviceId::NONE)
3339 .expect("create_node failed");
3340
3341 let info_original = child
3342 .entry
3343 .node
3344 .fetch_and_refresh_info(¤t_task)
3345 .expect("fetch_and_refresh_info failed")
3346 .clone();
3347
3348 child
3349 .entry
3350 .node
3351 .update_atime_mtime(
3352 ¤t_task,
3353 &child.mount,
3354 TimeUpdateType::Time(UtcInstant::from_nanos(30)),
3355 TimeUpdateType::Omit,
3356 )
3357 .expect("update_atime_mtime failed");
3358 let info_after_update = child
3359 .entry
3360 .node
3361 .fetch_and_refresh_info(¤t_task)
3362 .expect("fetch_and_refresh_info failed")
3363 .clone();
3364 assert_eq!(info_after_update.time_modify, info_original.time_modify);
3365 assert_eq!(info_after_update.time_access, UtcInstant::from_nanos(30));
3366
3367 child
3368 .entry
3369 .node
3370 .update_atime_mtime(
3371 ¤t_task,
3372 &child.mount,
3373 TimeUpdateType::Omit,
3374 TimeUpdateType::Time(UtcInstant::from_nanos(50)),
3375 )
3376 .expect("update_atime_mtime failed");
3377 let info_after_update2 = child
3378 .entry
3379 .node
3380 .fetch_and_refresh_info(¤t_task)
3381 .expect("fetch_and_refresh_info failed")
3382 .clone();
3383 assert_eq!(info_after_update2.time_modify, UtcInstant::from_nanos(50));
3384 assert_eq!(info_after_update2.time_access, UtcInstant::from_nanos(30));
3385 })
3386 .await;
3387 fixture.close().await;
3388 }
3389
3390 #[::fuchsia::test]
3391 async fn test_write_updates_mtime_ctime() {
3392 let fixture = TestFixture::new().await;
3393 let (server, client) = zx::Channel::create();
3394 fixture.root().clone(server.into()).expect("clone failed");
3395
3396 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3397
3398 spawn_kernel_and_run(async move |current_task| {
3399 let kernel = current_task.kernel();
3400 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3401 let fs = RemoteFs::new_fs(
3402 &kernel,
3403 client,
3404 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3405 rights,
3406 )
3407 .expect("new_fs failed");
3408 let ns: Arc<Namespace> = Namespace::new(fs);
3409 current_task.fs().set_umask(FileMode::from_bits(0));
3410 let child = ns
3411 .root()
3412 .create_node(¤t_task, "file".into(), MODE, DeviceId::NONE)
3413 .expect("create_node failed");
3414 let file = child.open(¤t_task, OpenFlags::RDWR).expect("open failed");
3415 let (ctime_before_write, mtime_before_write) = {
3418 let info = child
3419 .entry
3420 .node
3421 .fetch_and_refresh_info(¤t_task)
3422 .expect("fetch_and_refresh_info failed");
3423 (info.time_status_change, info.time_modify)
3424 };
3425
3426 let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3428 let written = file
3429 .write(¤t_task, &mut VecInputBuffer::new(&write_bytes))
3430 .expect("write failed");
3431 assert_eq!(written, write_bytes.len());
3432
3433 let (ctime_after_write_no_refresh, mtime_after_write_no_refresh) = {
3437 let info = child.entry.node.info();
3438 (info.time_status_change, info.time_modify)
3439 };
3440 assert_eq!(ctime_after_write_no_refresh, ctime_before_write);
3441 assert_eq!(mtime_after_write_no_refresh, mtime_before_write);
3442
3443 let (ctime_after_write_refresh, mtime_after_write_refresh) = {
3447 let info = child
3448 .entry
3449 .node
3450 .fetch_and_refresh_info(¤t_task)
3451 .expect("fetch_and_refresh_info failed");
3452 (info.time_status_change, info.time_modify)
3453 };
3454 assert_eq!(ctime_after_write_refresh, mtime_after_write_refresh);
3455 assert!(ctime_after_write_refresh > ctime_after_write_no_refresh);
3456 })
3457 .await;
3458 fixture.close().await;
3459 }
3460
3461 #[::fuchsia::test]
3462 async fn test_casefold_persists() {
3463 let fixture = TestFixture::new().await;
3464 let (server, client) = zx::Channel::create();
3465 fixture.root().clone(server.into()).expect("clone failed");
3466
3467 spawn_kernel_and_run(async move |current_task| {
3468 let kernel = current_task.kernel();
3469 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3470 let fs = RemoteFs::new_fs(
3471 &kernel,
3472 client,
3473 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3474 rights,
3475 )
3476 .expect("new_fs failed");
3477 let ns: Arc<Namespace> = Namespace::new(fs);
3478 let child = ns
3479 .root()
3480 .create_node(
3481 ¤t_task,
3482 "dir".into(),
3483 FileMode::ALLOW_ALL.with_type(FileMode::IFDIR),
3484 DeviceId::NONE,
3485 )
3486 .expect("create_node failed");
3487 child
3488 .entry
3489 .node
3490 .update_attributes(¤t_task, |info| {
3491 info.casefold = true;
3492 Ok(())
3493 })
3494 .expect("enable casefold")
3495 })
3496 .await;
3497
3498 let fixture = TestFixture::open(
3500 fixture.close().await,
3501 TestFixtureOptions { format: false, ..Default::default() },
3502 )
3503 .await;
3504 let (server, client) = zx::Channel::create();
3505 fixture.root().clone(server.into()).expect("clone failed");
3506 let casefold = spawn_kernel_and_run(async move |current_task| {
3507 let kernel = current_task.kernel();
3508 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3509 let fs = RemoteFs::new_fs(
3510 &kernel,
3511 client,
3512 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3513 rights,
3514 )
3515 .expect("new_fs failed");
3516 let ns = Namespace::new(fs);
3517 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3518 let child = ns
3519 .root()
3520 .lookup_child(¤t_task, &mut context, "dir".into())
3521 .expect("lookup_child failed");
3522 let casefold = child
3523 .entry
3524 .node
3525 .fetch_and_refresh_info(¤t_task)
3526 .expect("fetch_and_refresh_info failed")
3527 .casefold;
3528 casefold
3529 })
3530 .await;
3531 assert!(casefold);
3532
3533 fixture.close().await;
3534 }
3535
3536 #[::fuchsia::test]
3537 async fn test_pending_access_time() {
3538 const TEST_FILE: &str = "test_file";
3539
3540 let fixture = TestFixture::new().await;
3541 let (server, client) = zx::Channel::create();
3542 fixture.root().clone(server.into()).expect("clone failed");
3543 let (server, client2) = zx::Channel::create();
3544 fixture.root().clone(server.into()).expect("clone failed");
3545
3546 spawn_kernel_and_run(async move |current_task| {
3547 let kernel = current_task.kernel.clone();
3548
3549 let atime3 = {
3550 let fs = RemoteFs::new_fs(
3551 &kernel,
3552 client,
3553 FileSystemOptions {
3554 source: FlyByteStr::new(b"/"),
3555 flags: FileSystemFlags::empty().into(),
3556 ..Default::default()
3557 },
3558 fio::PERM_READABLE | fio::PERM_WRITABLE,
3559 )
3560 .expect("new_fs failed");
3561
3562 let ns = Namespace::new_with_flags(fs, MountpointFlags::RELATIME);
3563 let (child, _status) = ns
3564 .root()
3565 .open_create_node(
3566 ¤t_task,
3567 TEST_FILE.into(),
3568 FileMode::ALLOW_ALL.with_type(FileMode::IFREG),
3569 DeviceId::NONE,
3570 )
3571 .expect("create_node failed");
3572
3573 let atime1 = child.entry.node.info().time_access;
3574
3575 std::thread::sleep(std::time::Duration::from_micros(1));
3576
3577 let file_handle = child.open(¤t_task, OpenFlags::RDWR).expect("open failed");
3578
3579 file_handle
3580 .read(¤t_task, &mut VecOutputBuffer::new(10))
3581 .expect("read failed");
3582
3583 let atime2 = child.entry.node.info().time_access;
3585 assert!(atime2 > atime1);
3586
3587 std::thread::sleep(std::time::Duration::from_micros(1));
3588
3589 file_handle
3590 .read(¤t_task, &mut VecOutputBuffer::new(10))
3591 .expect("read failed");
3592
3593 let atime3 = child.entry.node.info().time_access;
3595 assert!(atime3 > atime2);
3596
3597 atime3
3598 };
3599
3600 kernel.delayed_releaser.apply(current_task);
3601
3602 let fs = RemoteFs::new_fs(
3604 &kernel,
3605 client2,
3606 FileSystemOptions {
3607 source: FlyByteStr::new(b"/"),
3608 flags: FileSystemFlags::empty().into(),
3609 ..Default::default()
3610 },
3611 fio::PERM_READABLE | fio::PERM_WRITABLE,
3612 )
3613 .expect("new_fs failed");
3614
3615 let ns = Namespace::new_with_flags(fs, MountpointFlags::RELATIME);
3616 let child = ns
3617 .root()
3618 .lookup_child(
3619 ¤t_task,
3620 &mut LookupContext::new(Default::default()),
3621 TEST_FILE.into(),
3622 )
3623 .expect("lookup_child failed");
3624
3625 let atime4 = child.entry.node.info().time_access;
3626
3627 assert!(atime4 >= atime3);
3628 })
3629 .await;
3630
3631 fixture.close().await;
3632 }
3633
3634 #[::fuchsia::test]
3635 async fn test_read_chunking() {
3636 use futures::StreamExt;
3637 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3638 let content = vec![0xAB; (fio::MAX_TRANSFER_SIZE + 100) as usize];
3639 let content_clone = content.clone();
3640
3641 let _server_task = fasync::Task::spawn(async move {
3642 while let Some(Ok(request)) = stream.next().await {
3643 match request {
3644 fio::FileRequest::ReadAt { count, offset, responder } => {
3645 let start = offset as usize;
3646 let end = std::cmp::min(start + count as usize, content_clone.len());
3647 let data = if start < content_clone.len() {
3648 &content_clone[start..end]
3649 } else {
3650 &[]
3651 };
3652 responder.send(Ok(data)).unwrap();
3653 }
3654 _ => panic!("Unexpected request: {:?}", request),
3655 }
3656 }
3657 });
3658
3659 fasync::unblock(move || {
3660 let io = RemoteIo::new(client.into_channel().into());
3661 let mut buffer = VecOutputBuffer::new(content.len());
3662 assert_eq!(
3663 io.read_to_output_buffer(0, &mut buffer).expect("read_at failed"),
3664 content.len()
3665 );
3666 assert_eq!(buffer.data(), content.as_slice());
3667 })
3668 .await;
3669 }
3670
3671 #[::fuchsia::test]
3672 async fn test_write_chunking() {
3673 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3674 let content = vec![0xCD; (fio::MAX_TRANSFER_SIZE + 100) as usize];
3675 let content2 = content.clone();
3676
3677 let server_task = fasync::Task::spawn(async move {
3678 let mut written = vec![0; content2.len()];
3679 while let Some(Ok(request)) = stream.next().await {
3680 match request {
3681 fio::FileRequest::WriteAt { offset, data, responder, .. } => {
3682 let offset = offset as usize;
3683 written[offset..offset + data.len()].copy_from_slice(&data);
3684 responder.send(Ok(data.len() as u64)).unwrap();
3685 }
3686 _ => panic!("Unexpected request: {:?}", request),
3687 }
3688 }
3689 assert_eq!(written, content2);
3690 });
3691
3692 fasync::unblock(move || {
3693 let io = RemoteIo::new(client.into_channel().into());
3694 let mut buffer = VecInputBuffer::new(&content);
3695 assert_eq!(
3696 io.write_from_input_buffer(0, &mut buffer).expect("write_at failed"),
3697 content.len()
3698 );
3699 })
3700 .await;
3701
3702 server_task.await;
3703 }
3704
3705 #[::fuchsia::test]
3706 async fn test_cached_attribute_refresh_behavior() {
3707 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3708 let barrier = Arc::new(Barrier::new(2));
3709 let barrier_clone = barrier.clone();
3710 let get_attrs_count = Arc::new(AtomicU32::new(0));
3711 let get_attrs_count_clone = get_attrs_count.clone();
3712
3713 let server_task = fasync::Task::spawn(async move {
3714 while let Some(Ok(request)) = stream.next().await {
3715 match request {
3716 fio::FileRequest::GetAttributes { query: _, responder } => {
3717 get_attrs_count_clone.fetch_add(1, Ordering::SeqCst);
3718 let mutable_attrs = fio::MutableNodeAttributes { ..Default::default() };
3719 let immutable_attrs = fio::ImmutableNodeAttributes {
3720 id: Some(1),
3721 link_count: Some(1),
3722 ..Default::default()
3723 };
3724 responder.send(Ok((&mutable_attrs, &immutable_attrs))).unwrap();
3725 }
3726 fio::FileRequest::Resize { length: _, responder } => {
3727 let barrier_clone = barrier_clone.clone();
3728 fasync::Task::spawn(async move {
3729 barrier_clone.async_wait().await;
3730 barrier_clone.async_wait().await;
3731 responder.send(Ok(())).unwrap();
3732 })
3733 .detach();
3734 }
3735 fio::FileRequest::Close { responder } => {
3736 responder.send(Ok(())).unwrap();
3737 }
3738 _ => panic!("Unexpected request: {:?}", request),
3739 }
3740 }
3741 });
3742
3743 fasync::unblock(move || {
3744 let io = RemoteIo::new(client.into_channel().into());
3745 let node = BaseNode::new(io, false);
3746 let info = DynamicLockDepRwLock::new::<FsNodeInfoLevel>(FsNodeInfo::default());
3747
3748 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 0);
3750 {
3751 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3752 }
3753 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 0);
3754
3755 std::thread::scope(|s| {
3757 s.spawn(|| {
3758 will_dirty(&[&node], || {
3759 node.io.truncate(0).expect("truncate failed");
3760 });
3761 });
3762
3763 barrier.wait();
3765
3766 {
3768 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3769 }
3770 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
3771
3772 {
3774 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3775 }
3776 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
3777
3778 barrier.wait();
3780 });
3781
3782 {
3784 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3785 }
3786 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3787
3788 {
3790 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3791 }
3792 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3793 })
3794 .await;
3795
3796 server_task.await;
3797 }
3798
3799 #[::fuchsia::test]
3800 async fn test_attribute_refresh_during_concurrent_dirty_operation() {
3801 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3802 let get_attrs_started = Arc::new(Barrier::new(2));
3803 let get_attrs_started_clone = get_attrs_started.clone();
3804 let finish_get_attrs = Arc::new(Barrier::new(2));
3805 let finish_get_attrs_clone = finish_get_attrs.clone();
3806
3807 let resize_started = Arc::new(Barrier::new(2));
3808 let resize_started_clone = resize_started.clone();
3809 let finish_resize = Arc::new(Barrier::new(2));
3810 let finish_resize_clone = finish_resize.clone();
3811
3812 let get_attrs_count = Arc::new(AtomicU32::new(0));
3813 let get_attrs_count_clone = get_attrs_count.clone();
3814
3815 let server_task = fasync::Task::spawn(async move {
3816 while let Some(Ok(request)) = stream.next().await {
3817 match request {
3818 fio::FileRequest::GetAttributes { query: _, responder } => {
3819 let count = get_attrs_count_clone.fetch_add(1, Ordering::SeqCst);
3820 let finish_get_attrs_clone = finish_get_attrs_clone.clone();
3821 let get_attrs_started_clone = get_attrs_started_clone.clone();
3822
3823 fasync::Task::spawn(async move {
3824 if count == 0 {
3825 fasync::unblock(move || {
3826 get_attrs_started_clone.wait();
3827 finish_get_attrs_clone.wait();
3828 })
3829 .await;
3830 }
3831 let mutable_attrs = fio::MutableNodeAttributes { ..Default::default() };
3832 let immutable_attrs = fio::ImmutableNodeAttributes {
3833 id: Some(1),
3834 link_count: Some(1),
3835 ..Default::default()
3836 };
3837 responder.send(Ok((&mutable_attrs, &immutable_attrs))).unwrap();
3838 })
3839 .detach();
3840 }
3841 fio::FileRequest::Resize { length: _, responder } => {
3842 let resize_started_clone = resize_started_clone.clone();
3843 let finish_resize_clone = finish_resize_clone.clone();
3844 fasync::Task::spawn(async move {
3845 fasync::unblock(move || {
3846 resize_started_clone.wait();
3847 finish_resize_clone.wait();
3848 })
3849 .await;
3850 responder.send(Ok(())).unwrap();
3851 })
3852 .detach();
3853 }
3854 fio::FileRequest::Close { responder } => {
3855 responder.send(Ok(())).unwrap();
3856 }
3857 _ => panic!("Unexpected request: {:?}", request),
3858 }
3859 }
3860 });
3861
3862 fasync::unblock(move || {
3863 let io = RemoteIo::new(client.into_channel().into());
3864 let node = BaseNode::new(io, true);
3865 let info = DynamicLockDepRwLock::new::<FsNodeInfoLevel>(FsNodeInfo::default());
3866
3867 std::thread::scope(|s| {
3868 let refresh_thread = s.spawn(|| {
3870 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3871 });
3872
3873 get_attrs_started.wait();
3874
3875 let dirty_thread = s.spawn(|| {
3877 will_dirty(&[&node], || {
3878 node.io.truncate(0).expect("truncate failed");
3879 });
3880 });
3881
3882 resize_started.wait();
3883
3884 finish_get_attrs.wait();
3886 refresh_thread.join().unwrap();
3887 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
3888
3889 {
3891 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3892 }
3893 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
3894
3895 finish_resize.wait();
3897 dirty_thread.join().unwrap();
3898
3899 {
3901 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3902 }
3903 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3904
3905 {
3907 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3908 }
3909 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3910 });
3911 })
3912 .await;
3913
3914 server_task.await;
3915 }
3916
3917 #[::fuchsia::test]
3918 async fn test_update_attributes_invalidates_cache() {
3919 let (client, mut stream) = create_request_stream::<fio::DirectoryMarker>();
3920 let get_attrs_count = Arc::new(AtomicU32::new(0));
3921 let get_attrs_count_clone = get_attrs_count.clone();
3922
3923 let server_task = fasync::Task::spawn(async move {
3924 let mut sub_tasks = Vec::new();
3925 while let Some(Ok(request)) = stream.next().await {
3926 match request {
3927 fio::DirectoryRequest::Open { path, object, flags, .. } => {
3928 assert_eq!(path, ".", "Unexpected open() for non-self");
3929 let get_attrs_count = get_attrs_count_clone.clone();
3930 sub_tasks.push(fasync::Task::spawn(async move {
3931 let (mut stream, control_handle) =
3932 ServerEnd::<fio::DirectoryMarker>::new(object)
3933 .into_stream_and_control_handle();
3934 assert!(flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION));
3935
3936 let mutable_attributes =
3938 fio::MutableNodeAttributes { ..Default::default() };
3939 let immutable_attributes = fio::ImmutableNodeAttributes {
3940 id: Some(1),
3941 link_count: Some(1),
3942 ..Default::default()
3943 };
3944 let info = fio::DirectoryInfo {
3945 attributes: Some(fio::NodeAttributes2 {
3946 mutable_attributes,
3947 immutable_attributes,
3948 }),
3949 ..Default::default()
3950 };
3951 let _ = control_handle
3952 .send_on_representation(fio::Representation::Directory(info));
3953
3954 while let Some(Ok(request)) = stream.next().await {
3955 match request {
3956 fio::DirectoryRequest::GetAttributes {
3957 query: _,
3958 responder,
3959 } => {
3960 get_attrs_count.fetch_add(1, Ordering::SeqCst);
3961 let mutable_attrs =
3962 fio::MutableNodeAttributes { ..Default::default() };
3963 let immutable_attrs = fio::ImmutableNodeAttributes {
3964 id: Some(1),
3965 link_count: Some(1),
3966 ..Default::default()
3967 };
3968 responder
3969 .send(Ok((&mutable_attrs, &immutable_attrs)))
3970 .unwrap();
3971 }
3972 fio::DirectoryRequest::UpdateAttributes {
3973 payload: _,
3974 responder,
3975 } => {
3976 responder.send(Ok(())).unwrap();
3977 }
3978 fio::DirectoryRequest::Close { responder } => {
3979 responder.send(Ok(())).unwrap();
3980 }
3981 _ => {
3982 panic!("Unexpected request: {:?}", request)
3983 }
3984 }
3985 }
3986 }));
3987 }
3988 fio::DirectoryRequest::Close { responder } => {
3989 responder.send(Ok(())).unwrap();
3990 }
3991 fio::DirectoryRequest::QueryFilesystem { responder } => {
3992 responder.send(0i32, None).unwrap();
3993 }
3994 _ => panic!("Unexpected request: {:?}", request),
3995 }
3996 }
3997
3998 for sub_task in sub_tasks {
3999 let _ = sub_task.await;
4000 }
4001 });
4002
4003 spawn_kernel_and_run(async move |current_task| {
4004 let fs = RemoteFs::new_fs(
4005 ¤t_task.kernel(),
4006 client.into_channel(),
4007 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4008 fio::PERM_READABLE | fio::PERM_WRITABLE,
4009 )
4010 .expect("failed to mount test remote FS");
4011
4012 {
4014 let _info =
4015 fs.root().node.fetch_and_refresh_info(current_task).expect("fetch failed");
4016 }
4017 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 0);
4018
4019 fs.root()
4021 .node
4022 .update_attributes(current_task, |attrs| {
4023 attrs.time_modify += UtcDuration::from_seconds(1);
4024 Ok(())
4025 })
4026 .expect("update_attributes failed");
4027
4028 {
4030 let _info =
4031 fs.root().node.fetch_and_refresh_info(current_task).expect("fetch failed");
4032 }
4033 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
4034
4035 {
4037 let _info =
4038 fs.root().node.fetch_and_refresh_info(current_task).expect("fetch failed");
4039 }
4040 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
4041 })
4042 .await;
4043
4044 server_task.await;
4045 }
4046
4047 trait AsyncBarrier {
4048 async fn async_wait(&self);
4049 }
4050
4051 impl AsyncBarrier for Arc<Barrier> {
4052 async fn async_wait(&self) {
4053 let this = self.clone();
4054 fasync::unblock(move || this.wait()).await;
4055 }
4056 }
4057
4058 #[derive(Default)]
4059 struct MockRemoteFs {
4060 get_attrs_count: AtomicU32,
4061 file_size: AtomicUsize,
4062 write_offsets: Mutex<Vec<u64>>,
4063 open_paths: Mutex<Vec<String>>,
4064 data: Mutex<Vec<u8>>,
4065 get_attrs_hook: Mutex<Option<futures::future::BoxFuture<'static, ()>>>,
4066 write_hook: Mutex<Option<futures::future::BoxFuture<'static, ()>>>,
4067 }
4068
4069 impl MockRemoteFs {
4070 async fn handle_file_requests(
4071 self: Arc<Self>,
4072 mut stream: fio::FileRequestStream,
4073 control_handle: fio::FileControlHandle,
4074 ) {
4075 let size = self.file_size.load(Ordering::SeqCst) as u64;
4076 let info = fio::FileInfo {
4077 attributes: Some(fio::NodeAttributes2 {
4078 mutable_attributes: fio::MutableNodeAttributes { ..Default::default() },
4079 immutable_attributes: fio::ImmutableNodeAttributes {
4080 id: Some(2),
4081 link_count: Some(1),
4082 content_size: Some(size),
4083 storage_size: Some(size),
4084 ..Default::default()
4085 },
4086 }),
4087 ..Default::default()
4088 };
4089 let _ = control_handle.send_on_representation(fio::Representation::File(info));
4090 while let Some(Ok(request)) = stream.next().await {
4091 match request {
4092 fio::FileRequest::GetAttributes { responder, .. } => {
4093 let this = self.clone();
4095 fasync::Task::spawn(async move {
4096 this.get_attrs_count.fetch_add(1, Ordering::SeqCst);
4097 let size = this.file_size.load(Ordering::SeqCst) as u64;
4098 let hook = this.get_attrs_hook.lock().take();
4099 if let Some(hook) = hook {
4100 hook.await;
4101 }
4102 responder
4103 .send(Ok((
4104 &fio::MutableNodeAttributes { ..Default::default() },
4105 &fio::ImmutableNodeAttributes {
4106 id: Some(2),
4107 link_count: Some(1),
4108 content_size: Some(size),
4109 storage_size: Some(size),
4110 ..Default::default()
4111 },
4112 )))
4113 .unwrap();
4114 })
4115 .detach();
4116 }
4117 fio::FileRequest::ReadAt { count, offset, responder } => {
4118 let data = self.data.lock();
4119 let start = std::cmp::min(offset as usize, data.len());
4120 let end = std::cmp::min(start + count as usize, data.len());
4121 responder.send(Ok(&data[start..end])).unwrap();
4122 }
4123 fio::FileRequest::WriteAt { offset, data, responder, .. } => {
4124 let self_clone = Arc::clone(&self);
4126 fasync::Task::spawn(async move {
4127 self_clone.write_offsets.lock().push(offset);
4128 let end = offset as usize + data.len();
4129 {
4130 let mut mock_data = self_clone.data.lock();
4131 if end > mock_data.len() {
4132 mock_data.resize(end, 0);
4133 }
4134 mock_data[offset as usize..end].copy_from_slice(&data);
4135 }
4136 let mut current_size = self_clone.file_size.load(Ordering::SeqCst);
4137 while end > current_size {
4138 match self_clone.file_size.compare_exchange_weak(
4139 current_size,
4140 end,
4141 Ordering::SeqCst,
4142 Ordering::SeqCst,
4143 ) {
4144 Ok(_) => break,
4145 Err(actual) => current_size = actual,
4146 }
4147 }
4148 let hook = self_clone.write_hook.lock().take();
4149 if let Some(hook) = hook {
4150 hook.await;
4151 }
4152 responder.send(Ok(data.len() as u64)).unwrap();
4153 })
4154 .detach();
4155 }
4156 fio::FileRequest::Resize { length, responder, .. } => {
4157 self.file_size.store(length as usize, Ordering::SeqCst);
4158 responder.send(Ok(())).unwrap();
4159 }
4160 fio::FileRequest::Seek { origin, offset, responder } => {
4161 let new_offset = match origin {
4162 fio::SeekOrigin::Start => offset as u64,
4163 fio::SeekOrigin::Current => 0,
4164 fio::SeekOrigin::End => {
4165 (self.file_size.load(Ordering::SeqCst) as i64 + offset) as u64
4166 }
4167 };
4168 responder.send(Ok(new_offset)).unwrap();
4169 }
4170 fio::FileRequest::GetExtendedAttribute { responder, .. } => {
4171 responder.send(Err(zx::Status::NOT_FOUND.into_raw())).unwrap();
4172 }
4173 fio::FileRequest::Close { responder } => {
4174 responder.send(Ok(())).unwrap();
4175 }
4176 _ => {}
4177 }
4178 }
4179 }
4180
4181 fn handle_directory_requests(
4182 self: Arc<Self>,
4183 mut stream: fio::DirectoryRequestStream,
4184 control_handle: fio::DirectoryControlHandle,
4185 ) -> futures::future::BoxFuture<'static, ()> {
4186 async move {
4187 let info = fio::DirectoryInfo {
4188 attributes: Some(fio::NodeAttributes2 {
4189 mutable_attributes: fio::MutableNodeAttributes { ..Default::default() },
4190 immutable_attributes: fio::ImmutableNodeAttributes {
4191 id: Some(1),
4192 link_count: Some(1),
4193 protocols: Some(fio::NodeProtocolKinds::DIRECTORY),
4194 ..Default::default()
4195 },
4196 }),
4197 ..Default::default()
4198 };
4199 let _ = control_handle.send_on_representation(fio::Representation::Directory(info));
4200 let mut sub_tasks = Vec::new();
4201 while let Some(Ok(request)) = stream.next().await {
4202 match request {
4203 fio::DirectoryRequest::Open { path, flags, object, .. } => {
4204 self.open_paths.lock().push(path.clone());
4205 if flags.contains(fio::Flags::FLAG_MUST_CREATE) {
4206 let self_clone = Arc::clone(&self);
4207 if flags.contains(fio::Flags::PROTOCOL_DIRECTORY) {
4208 sub_tasks.push(fasync::Task::spawn(async move {
4209 let (stream, control_handle) =
4210 ServerEnd::<fio::DirectoryMarker>::new(object)
4211 .into_stream_and_control_handle();
4212 self_clone
4213 .handle_directory_requests(stream, control_handle)
4214 .await;
4215 }));
4216 } else {
4217 sub_tasks.push(fasync::Task::spawn(async move {
4218 let (stream, control_handle) =
4219 ServerEnd::<fio::FileMarker>::new(object)
4220 .into_stream_and_control_handle();
4221 self_clone
4222 .handle_file_requests(stream, control_handle)
4223 .await;
4224 }));
4225 }
4226 } else if path == "file"
4227 || flags.contains(fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY)
4228 {
4229 let self_clone = Arc::clone(&self);
4230 sub_tasks.push(fasync::Task::spawn(async move {
4231 let (stream, control_handle) =
4232 ServerEnd::<fio::FileMarker>::new(object)
4233 .into_stream_and_control_handle();
4234 self_clone.handle_file_requests(stream, control_handle).await;
4235 }));
4236 } else if path == "dir" || path == "dir2" || path == "sub" {
4237 let self_clone = Arc::clone(&self);
4238 sub_tasks.push(fasync::Task::spawn(async move {
4239 let (stream, control_handle) =
4240 ServerEnd::<fio::DirectoryMarker>::new(object)
4241 .into_stream_and_control_handle();
4242 self_clone
4243 .handle_directory_requests(stream, control_handle)
4244 .await;
4245 }));
4246 } else {
4247 let _ = ServerEnd::<fio::NodeMarker>::new(object)
4248 .close_with_epitaph(zx::Status::NOT_FOUND);
4249 }
4250 }
4251 fio::DirectoryRequest::GetAttributes { responder, .. } => {
4252 self.get_attrs_count.fetch_add(1, Ordering::SeqCst);
4253 responder
4254 .send(Ok((
4255 &fio::MutableNodeAttributes { ..Default::default() },
4256 &fio::ImmutableNodeAttributes {
4257 id: Some(1),
4258 link_count: Some(1),
4259 protocols: Some(fio::NodeProtocolKinds::DIRECTORY),
4260 ..Default::default()
4261 },
4262 )))
4263 .unwrap();
4264 }
4265 fio::DirectoryRequest::Close { responder } => {
4266 responder.send(Ok(())).unwrap();
4267 }
4268 _ => {}
4269 }
4270 }
4271 for task in sub_tasks {
4272 let _ = task.await;
4273 }
4274 }
4275 .boxed()
4276 }
4277
4278 async fn run(self: Arc<Self>, mut stream: fio::DirectoryRequestStream) {
4279 let mut sub_tasks = Vec::new();
4280 while let Some(Ok(request)) = stream.next().await {
4281 match request {
4282 fio::DirectoryRequest::Open { path, object, .. } => {
4283 if path == "." {
4284 let self_clone = Arc::clone(&self);
4285 sub_tasks.push(fasync::Task::spawn(async move {
4286 let (stream, control_handle) =
4287 ServerEnd::<fio::DirectoryMarker>::new(object)
4288 .into_stream_and_control_handle();
4289 self_clone.handle_directory_requests(stream, control_handle).await;
4290 }));
4291 }
4292 }
4293 fio::DirectoryRequest::Close { responder } => {
4294 responder.send(Ok(())).unwrap();
4295 }
4296 fio::DirectoryRequest::QueryFilesystem { responder } => {
4297 responder.send(0i32, None).unwrap();
4298 }
4299 _ => {}
4300 }
4301 }
4302 for sub_task in sub_tasks {
4303 let _ = sub_task.await;
4304 }
4305 }
4306 }
4307
4308 #[::fuchsia::test]
4309 async fn test_mknod_and_mkdir_do_not_trigger_get_attributes() {
4310 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4311 let state = Arc::new(MockRemoteFs::default());
4312
4313 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4314
4315 spawn_kernel_and_run(async move |current_task| {
4316 let fs = RemoteFs::new_fs(
4317 ¤t_task.kernel(),
4318 client.into_channel(),
4319 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4320 fio::PERM_READABLE | fio::PERM_WRITABLE,
4321 )
4322 .expect("failed to mount test remote FS");
4323
4324 let ns = Namespace::new(fs);
4325 let root = ns.root();
4326
4327 {
4329 let _info =
4330 root.entry.node.fetch_and_refresh_info(current_task).expect("fetch root info");
4331 }
4332 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4333
4334 let file_node = root
4336 .create_node(current_task, "new_file".into(), mode!(IFREG, 0o644), DeviceId::NONE)
4337 .expect("create_node file failed");
4338 {
4339 let _info = file_node
4340 .entry
4341 .node
4342 .fetch_and_refresh_info(current_task)
4343 .expect("fetch file info");
4344 }
4345 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4346
4347 let dir_node = root
4349 .create_node(current_task, "new_dir".into(), mode!(IFDIR, 0o755), DeviceId::NONE)
4350 .expect("create_node dir failed");
4351 {
4352 let _info = dir_node
4353 .entry
4354 .node
4355 .fetch_and_refresh_info(current_task)
4356 .expect("fetch dir info");
4357 }
4358 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4359
4360 let tmp_node = root
4362 .create_tmpfile(current_task, mode!(IFREG, 0o644), OpenFlags::RDWR)
4363 .expect("create_tmpfile failed");
4364 {
4365 let _info = tmp_node
4366 .entry
4367 .node
4368 .fetch_and_refresh_info(current_task)
4369 .expect("fetch tmpfile info");
4370 }
4371 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4372 })
4373 .await;
4374
4375 server_task.await;
4376 }
4377
4378 #[::fuchsia::test]
4379 async fn test_lookup_open_counts() {
4380 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4381 let state = Arc::new(MockRemoteFs::default());
4382
4383 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4384
4385 spawn_kernel_and_run(async move |current_task| {
4386 let fs = RemoteFs::new_fs(
4387 ¤t_task.kernel(),
4388 client.into_channel(),
4389 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4390 fio::PERM_READABLE | fio::PERM_WRITABLE,
4391 )
4392 .expect("failed to mount test remote FS");
4393
4394 let ns = Namespace::new(fs);
4395 let root = ns.root();
4396
4397 let mut context = LookupContext::default();
4399 assert_eq!(
4400 root.lookup_child(current_task, &mut context, "nonexistent".into())
4401 .expect_err("lookup should fail with ENOENT"),
4402 errno!(ENOENT)
4403 );
4404 assert_eq!(*state.open_paths.lock(), vec!["nonexistent"]);
4405 state.open_paths.lock().clear();
4406
4407 let results = root.entry.get_children_pipelined(
4409 current_task,
4410 &root.mount,
4411 &["dir".into(), "sub".into(), "file".into()],
4412 );
4413 assert_eq!(results.len(), 3);
4414 for res in &results {
4415 assert!(res.is_ok(), "expected Ok, got {res:?}");
4416 }
4417 assert_eq!(*state.open_paths.lock(), vec!["dir", "sub", "file"]);
4418 state.open_paths.lock().clear();
4419
4420 let results = root.entry.get_children_pipelined(
4422 current_task,
4423 &root.mount,
4424 &["dir2".into(), "missing".into()],
4425 );
4426 assert_eq!(results.len(), 2);
4427 assert!(results[0].is_ok());
4428 assert_eq!(results[1].as_ref().expect_err("expected ENOENT"), &errno!(ENOENT));
4429 assert_eq!(*state.open_paths.lock(), vec!["dir2", "missing"]);
4430 state.open_paths.lock().clear();
4431
4432 let results = root.entry.get_children_pipelined(
4435 current_task,
4436 &root.mount,
4437 &["dir".into(), "sub".into(), "file".into()],
4438 );
4439 assert_eq!(results.len(), 3);
4440 for res in &results {
4441 assert!(res.is_ok(), "expected Ok, got {res:?}");
4442 }
4443 assert_eq!(*state.open_paths.lock(), Vec::<String>::new());
4444 })
4445 .await;
4446
4447 server_task.await;
4448 }
4449
4450 #[::fuchsia::test]
4451 async fn test_get_size_uses_cache_unless_truncated() {
4452 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4453 let state = Arc::new(MockRemoteFs::default());
4454
4455 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4456
4457 spawn_kernel_and_run(async move |current_task| {
4458 let fs = RemoteFs::new_fs(
4459 ¤t_task.kernel(),
4460 client.into_channel(),
4461 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4462 fio::PERM_READABLE | fio::PERM_WRITABLE,
4463 )
4464 .expect("failed to mount test remote FS");
4465
4466 let ns = Namespace::new(fs);
4467 let root = ns.root();
4468
4469 let mut context = LookupContext::default();
4470 let file_node = root
4471 .lookup_child(current_task, &mut context, "file".into())
4472 .expect("lookup failed");
4473
4474 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4476 {
4477 let _size = file_node.entry.node.get_size(current_task).expect("get_size failed");
4478 }
4479 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4480
4481 let file_handle = file_node
4483 .open(current_task, OpenFlags::RDWR | OpenFlags::APPEND)
4484 .expect("open failed");
4485
4486 {
4487 let mut data = VecInputBuffer::new(b"foo");
4488 let written = file_handle.write(current_task, &mut data).expect("write failed");
4489 assert_eq!(written, 3);
4490 }
4491 assert_eq!(file_node.entry.node.get_size(current_task).expect("get_size failed"), 3);
4492 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4493 assert_eq!(*state.write_offsets.lock(), vec![0]);
4494
4495 file_node
4497 .entry
4498 .node
4499 .truncate(current_task, &file_node.mount, 0)
4500 .expect("truncate failed");
4501 assert_eq!(state.file_size.load(Ordering::SeqCst), 0);
4502
4503 {
4505 let size = file_node.entry.node.get_size(current_task).expect("get_size failed");
4506 assert_eq!(size, 0);
4507 }
4508 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 1);
4509
4510 {
4512 let mut data = VecInputBuffer::new(b"bar");
4513 let written = file_handle.write(current_task, &mut data).expect("write failed");
4514 assert_eq!(written, 3);
4515 }
4516 assert_eq!(file_node.entry.node.get_size(current_task).expect("get_size failed"), 3);
4517 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 1);
4520 assert_eq!(*state.write_offsets.lock(), vec![0, 0]);
4521
4522 file_node
4524 .entry
4525 .node
4526 .truncate(current_task, &file_node.mount, 10)
4527 .expect("truncate failed");
4528 {
4529 let mut data = VecInputBuffer::new(b"baz");
4530 let written = file_handle.write(current_task, &mut data).expect("write failed");
4531 assert_eq!(written, 3);
4532 }
4533 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 2);
4535 assert_eq!(file_node.entry.node.get_size(current_task).expect("get_size failed"), 13);
4536 assert_eq!(*state.write_offsets.lock(), vec![0, 0, 10]);
4537 })
4538 .await;
4539
4540 server_task.await;
4541 }
4542
4543 #[::fuchsia::test]
4544 async fn test_get_size_during_refresh_after_truncate() {
4545 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4546 let state = Arc::new(MockRemoteFs::default());
4547
4548 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4549
4550 spawn_kernel_and_run(async move |current_task| {
4551 let fs = RemoteFs::new_fs(
4552 ¤t_task.kernel(),
4553 client.into_channel(),
4554 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4555 fio::PERM_READABLE | fio::PERM_WRITABLE,
4556 )
4557 .expect("failed to mount test remote FS");
4558
4559 let ns = Namespace::new(fs);
4560 let root = ns.root();
4561
4562 let mut context = LookupContext::default();
4563 let file_node = root
4564 .lookup_child(current_task, &mut context, "file".into())
4565 .expect("lookup failed");
4566
4567 assert_eq!(file_node.entry.node.get_size(current_task).expect("get_size failed"), 0);
4569
4570 file_node
4572 .entry
4573 .node
4574 .truncate(current_task, &file_node.mount, 10)
4575 .expect("truncate failed");
4576
4577 let barrier = Arc::new(Barrier::new(2));
4579 {
4580 let barrier = barrier.clone();
4581 *state.get_attrs_hook.lock() = Some(Box::pin(async move {
4582 barrier.async_wait().await;
4583 barrier.async_wait().await;
4584 }));
4585 }
4586
4587 let file_node_clone = file_node.clone();
4589 let (result1, request) = SpawnRequestBuilder::new()
4590 .with_sync_closure(move |current_task| {
4591 let size =
4592 file_node_clone.entry.node.get_size(current_task).expect("get_size failed");
4593 assert_eq!(size, 10);
4594 })
4595 .build_with_async_result();
4596 current_task.kernel().kthreads.spawner().spawn_from_request(request);
4597
4598 barrier.async_wait().await;
4600
4601 *state.get_attrs_hook.lock() =
4603 Some(Box::pin(async move { barrier.async_wait().await }));
4604
4605 let (result2, request) = SpawnRequestBuilder::new()
4607 .with_sync_closure(move |current_task| {
4608 let size =
4609 file_node.entry.node.get_size(current_task).expect("get_size failed");
4610 assert_eq!(size, 10);
4611 })
4612 .build_with_async_result();
4613 current_task.kernel().kthreads.spawner().spawn_from_request(request);
4614
4615 result1.await.unwrap();
4616 result2.await.unwrap();
4617 })
4618 .await;
4619
4620 server_task.await;
4621 }
4622
4623 #[::fuchsia::test]
4624 async fn test_get_size_during_outstanding_write() {
4625 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4626 let state = Arc::new(MockRemoteFs::default());
4627
4628 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4629
4630 spawn_kernel_and_run(async move |current_task| {
4631 let fs = RemoteFs::new_fs(
4632 ¤t_task.kernel(),
4633 client.into_channel(),
4634 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4635 fio::PERM_READABLE | fio::PERM_WRITABLE,
4636 )
4637 .expect("failed to mount test remote FS");
4638
4639 let ns = Namespace::new(fs);
4640 let root = ns.root();
4641
4642 let mut context = LookupContext::default();
4643 let file_node = root
4644 .lookup_child(current_task, &mut context, "file".into())
4645 .expect("lookup failed");
4646
4647 let file_handle = file_node.open(current_task, OpenFlags::RDWR).expect("open failed");
4649
4650 let barrier = Arc::new(Barrier::new(2));
4652 {
4653 let barrier = barrier.clone();
4654 *state.write_hook.lock() = Some(Box::pin(async move {
4655 barrier.async_wait().await;
4656 barrier.async_wait().await;
4657 }));
4658 }
4659
4660 let file_handle_clone = file_handle.clone();
4662 current_task.kernel().kthreads.spawner().spawn_from_request(
4663 SpawnRequestBuilder::new()
4664 .with_sync_closure(move |current_task| {
4665 let mut data = VecInputBuffer::new(b"hello");
4666 file_handle_clone.write(current_task, &mut data).expect("write failed");
4667 })
4668 .build(),
4669 );
4670
4671 barrier.async_wait().await;
4673
4674 {
4676 let mut data = VecOutputBuffer::new(5);
4677 let read = file_handle.read_at(current_task, 0, &mut data).expect("read failed");
4678 assert_eq!(read, 5);
4679 assert_eq!(data.data(), b"hello");
4680 }
4681
4682 let size = file_node.entry.node.get_size(current_task).expect("get_size failed");
4684 assert_eq!(
4685 size, 5,
4686 "get_size should return the updated size even if a write is outstanding"
4687 );
4688
4689 barrier.async_wait().await;
4691 })
4692 .await;
4693
4694 server_task.await;
4695 }
4696
4697 #[test]
4698 fn test_info_state_initial_state() {
4699 let state = InfoState::new(true); assert_eq!(state.0.load(Ordering::Relaxed), 0);
4701
4702 let state = InfoState::new(false); assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4704 }
4705
4706 #[test]
4707 fn test_info_state_dirty_op_guard() {
4708 let state = InfoState::new(false);
4709 {
4710 let _guard = state.dirty_op_guard(false);
4711 assert_eq!(state.0.load(Ordering::Relaxed), 1); assert!(!state.is_size_accurate());
4713 }
4714 assert_eq!(state.0.load(Ordering::Relaxed), 0);
4715 assert!(state.is_size_accurate());
4716
4717 {
4718 let _guard = state.dirty_op_guard(true);
4719 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED | 1);
4720 assert!(!state.is_size_accurate());
4721 }
4722 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4723 assert!(!state.is_size_accurate());
4724
4725 {
4726 let _guard1 = state.dirty_op_guard(true);
4727 let _guard2 = state.dirty_op_guard(true);
4728 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED | 2);
4729 assert!(!state.is_size_accurate());
4730 }
4731 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4732 assert!(!state.is_size_accurate());
4733 }
4734
4735 #[test]
4736 fn test_info_state_refresh_clears_truncated() {
4737 let state = InfoState::new(true);
4738 {
4740 let _guard = state.dirty_op_guard(true);
4741 }
4742 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4743
4744 let info = DynamicLockDepRwLock::new::<FsNodeInfoLevel>(FsNodeInfo::default());
4745 state.maybe_refresh(&info, |_| Ok(()), |_| unreachable!()).unwrap();
4746
4747 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4748 assert!(state.is_size_accurate());
4749 }
4750
4751 #[test]
4752 fn test_info_state_maybe_refresh_success() {
4753 let state = InfoState::new(true);
4754 let info = DynamicLockDepRwLock::new::<FsNodeInfoLevel>(FsNodeInfo::default());
4755
4756 let res = state.maybe_refresh(&info, |_| Ok(42), |_| unreachable!());
4757 assert_eq!(res.unwrap(), 42);
4758 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4759 }
4760
4761 #[test]
4762 fn test_info_state_maybe_refresh_error() {
4763 let state = InfoState::new(true);
4764 let info = DynamicLockDepRwLock::new::<FsNodeInfoLevel>(FsNodeInfo::default());
4765
4766 let res: Result<u32, Errno> =
4767 state.maybe_refresh(&info, |_| error!(EIO), |_| unreachable!());
4768 assert!(res.is_err());
4769 assert_eq!(state.0.load(Ordering::Relaxed), 0); }
4771
4772 #[test]
4773 fn test_info_state_maybe_refresh_not_needed() {
4774 let state = InfoState::new(false); let info = DynamicLockDepRwLock::new::<FsNodeInfoLevel>(FsNodeInfo::default());
4776 let res = state.maybe_refresh(&info, |_| unreachable!(), |_| Ok(123));
4777 assert_eq!(res.unwrap(), 123);
4778 }
4779
4780 #[test]
4781 fn test_info_state_concurrent_dirty_op_during_refresh() {
4782 let state = InfoState::new(true);
4783 let info = DynamicLockDepRwLock::new::<FsNodeInfoLevel>(FsNodeInfo::default());
4784
4785 state
4786 .maybe_refresh(
4787 &info,
4788 |_| {
4789 let _guard = state.dirty_op_guard(false);
4791 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::PENDING_REFRESH | 1);
4792 Ok(())
4793 },
4794 |_| unreachable!(),
4795 )
4796 .unwrap();
4797
4798 assert_eq!(state.0.load(Ordering::Relaxed), 0);
4799 }
4800
4801 #[::fuchsia::test]
4802 async fn test_sync() {
4803 let fixture = TestFixture::new().await;
4804 let (server, client) = zx::Channel::create();
4805 fixture.root().clone(server.into()).expect("clone failed");
4806
4807 spawn_kernel_and_run(async move |current_task| {
4808 let kernel = current_task.kernel();
4809 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4810 let fs = RemoteFs::new_fs(
4811 &kernel,
4812 client,
4813 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
4814 rights,
4815 )
4816 .expect("new_fs failed");
4817 let ns = Namespace::new(fs);
4818 current_task.fs().set_umask(FileMode::from_bits(0));
4819 let root = ns.root();
4820
4821 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
4822 root.create_node(¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
4823 .expect("create_node failed");
4824 let mut context = LookupContext::new(SymlinkMode::NoFollow);
4825 let reg_node = root
4826 .lookup_child(¤t_task, &mut context, "file".into())
4827 .expect("lookup_child failed");
4828
4829 reg_node
4831 .entry
4832 .node
4833 .ops()
4834 .sync(®_node.entry.node, ¤t_task)
4835 .expect("sync failed");
4836 })
4837 .await;
4838 fixture.close().await;
4839 }
4840
4841 #[::fuchsia::test]
4842 async fn test_msync_propagates_to_fxfs() {
4843 use crate::mm::MemoryAccessor;
4844 use crate::mm::syscalls::{sys_mmap, sys_msync};
4845 use crate::vfs::FdFlags;
4846 use starnix_uapi::user_address::UserAddress;
4847 use starnix_uapi::{MAP_SHARED, MS_SYNC, PROT_READ, PROT_WRITE};
4848
4849 let commit_count = Arc::new(AtomicUsize::new(0));
4851 let commit_count_clone = commit_count.clone();
4852
4853 let (mut hooks, fs_hooks) = fxfs_testing::Hooks::new();
4854 hooks.set_pre_commit(move |_transaction| {
4855 commit_count_clone.fetch_add(1, Ordering::SeqCst);
4856 Ok(())
4857 });
4858
4859 let fixture = TestFixture::open(
4861 DeviceHolder::new(FakeDevice::new(1024 * 1024, 512)),
4862 TestFixtureOptions {
4863 format: true,
4864 as_blob: false,
4865 encrypted: true,
4866 hooks: Some(fs_hooks),
4867 ..Default::default()
4868 },
4869 )
4870 .await;
4871
4872 let (server, client) = zx::Channel::create();
4873 fixture.root().clone(server.into()).expect("clone channel");
4874
4875 spawn_kernel_and_run(async move |current_task| {
4876 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4878 let fs = RemoteFs::new_fs(
4879 current_task.kernel(),
4880 client,
4881 FileSystemOptions { source: FlyByteStr::new(b"/test"), ..Default::default() },
4882 rights,
4883 )
4884 .expect("new_fs");
4885 let ns = Namespace::new(fs);
4886 let root = ns.root();
4887
4888 let node = root
4890 .create_node(¤t_task, "test_file".into(), mode!(IFREG, 0o666), DeviceId::NONE)
4891 .expect("create_node");
4892 let file_handle = node.open(¤t_task, OpenFlags::RDWR).expect("open");
4893 let fd = current_task.add_file(file_handle, FdFlags::empty()).expect("add file");
4894
4895 let len = *PAGE_SIZE as usize * 4;
4897 let mmap_addr = sys_mmap(
4898 current_task,
4899 UserAddress::default(),
4900 len,
4901 PROT_READ | PROT_WRITE,
4902 MAP_SHARED,
4903 fd,
4904 0,
4905 )
4906 .expect("mmap");
4907
4908 for i in 0..4 {
4910 let data = [0xAAu8; 1];
4911 current_task
4912 .write_memory((mmap_addr + (i * *PAGE_SIZE as usize)).unwrap(), &data)
4913 .expect("write memory");
4914 }
4915
4916 let commits_before_msync = commit_count.load(Ordering::SeqCst);
4918
4919 sys_msync(current_task, mmap_addr, len, MS_SYNC).expect("msync");
4921
4922 let final_commits = commit_count.load(Ordering::SeqCst);
4924 assert!(
4925 final_commits > commits_before_msync,
4926 "msync should trigger Fxfs transaction. commits: {} -> {}",
4927 commits_before_msync,
4928 final_commits
4929 );
4930 })
4931 .await;
4932
4933 fixture.close().await;
4934 }
4935
4936 #[::fuchsia::test]
4937 async fn test_get_size() {
4938 let fixture = TestFixture::new().await;
4939 let (server, client) = zx::Channel::create();
4940 fixture.root().clone(server.into()).expect("clone failed");
4941
4942 spawn_kernel_and_run(async move |current_task| {
4943 let kernel = current_task.kernel();
4944 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4945 let fs = RemoteFs::new_fs(
4946 &kernel,
4947 client,
4948 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
4949 rights,
4950 )
4951 .expect("new_fs failed");
4952 let ns = Namespace::new(fs);
4953 let root = ns.root();
4954
4955 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o666);
4956 let node = root
4957 .create_node(¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
4958 .expect("create_node failed");
4959 let file = node.open(¤t_task, OpenFlags::RDWR).expect("open failed");
4960
4961 assert_eq!(node.entry.node.get_size(¤t_task).expect("get_size failed"), 0);
4963
4964 let mut data = VecInputBuffer::new(b"hello");
4966 file.write(¤t_task, &mut data).expect("write failed");
4967
4968 assert_eq!(node.entry.node.get_size(¤t_task).expect("get_size failed"), 5);
4970
4971 node.truncate(¤t_task, 10).expect("truncate failed");
4973
4974 assert_eq!(node.entry.node.get_size(¤t_task).expect("get_size failed"), 10);
4976
4977 node.truncate(¤t_task, 3).expect("truncate failed");
4979
4980 assert_eq!(node.entry.node.get_size(¤t_task).expect("get_size failed"), 3);
4982 })
4983 .await;
4984 fixture.close().await;
4985 }
4986
4987 #[fuchsia::test]
4988 async fn test_remote_fs_casefold_not_supported_on_non_fxfs() {
4989 spawn_kernel_and_run(async |current_task| {
4990 let kernel = current_task.kernel();
4991 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
4992 let (server, client) = zx::Channel::create();
4993 fdio::open("/pkg", rights, server).expect("failed to open /pkg");
4994 let fs = RemoteFs::new_fs(
4995 &kernel,
4996 client,
4997 FileSystemOptions { source: FlyByteStr::new(b"/pkg"), ..Default::default() },
4998 rights,
4999 )
5000 .unwrap();
5001 let ns = Namespace::new(fs);
5002 let root = ns.root();
5003
5004 assert!(!root.entry.node.fs().has_casefold_support());
5005 assert_eq!(
5006 root.entry.node.update_attributes(¤t_task, |info| {
5007 info.casefold = true;
5008 Ok(())
5009 }),
5010 error!(ENOTSUP)
5011 );
5012 assert_eq!(root.entry.set_casefold(¤t_task, true), error!(ENOTSUP));
5013 })
5014 .await;
5015 }
5016
5017 #[fuchsia::test]
5018 async fn test_remote_fs_casefold_supported_on_fxfs() {
5019 let fixture = TestFixture::new().await;
5020 let (server, client) = zx::Channel::create();
5021 fixture.root().clone(server.into()).expect("clone failed");
5022
5023 spawn_kernel_and_run(async move |current_task| {
5024 let kernel = current_task.kernel();
5025 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
5026 let fs = RemoteFs::new_fs(
5027 &kernel,
5028 client,
5029 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
5030 rights,
5031 )
5032 .expect("new_fs failed");
5033 let ns = Namespace::new(fs);
5034 let root = ns.root();
5035
5036 assert!(root.entry.node.fs().has_casefold_support());
5037
5038 assert_eq!(root.entry.set_casefold(¤t_task, true), Ok(()));
5040 assert!(root.entry.node.info().casefold);
5041
5042 assert_eq!(root.entry.set_casefold(¤t_task, true), Ok(()));
5044
5045 root.create_node(¤t_task, "child".into(), FileMode::IFREG, DeviceId::NONE)
5047 .expect("create child");
5048
5049 assert_eq!(root.entry.set_casefold(¤t_task, false), error!(ENOTEMPTY));
5051 })
5052 .await;
5053
5054 fixture.close().await;
5055 }
5056}