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