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