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