1use crate::fs::fuchsia::RemoteUnixDomainSocket;
6use crate::fs::fuchsia::remote_volume::RemoteVolume;
7use crate::fs::fuchsia::sync_file::{SyncFence, SyncFile, SyncPoint, Timeline};
8use crate::mm::memory::MemoryObject;
9use crate::mm::{ProtectionFlags, VMEX_RESOURCE};
10use crate::security;
11use crate::task::{CurrentTask, Kernel};
12use crate::vfs::buffers::{InputBuffer, OutputBuffer, with_iovec_segments};
13use crate::vfs::file_server::serve_file_tagged;
14use crate::vfs::fsverity::FsVerityState;
15use crate::vfs::socket::{Socket, SocketFile, ZxioBackedSocket};
16use crate::vfs::{
17 Anon, AppendLockWriteGuard, CacheMode, DEFAULT_BYTES_PER_BLOCK, DirectoryEntryType, DirentSink,
18 FallocMode, FileHandle, FileObject, FileOps, FileSystem, FileSystemHandle, FileSystemOps,
19 FileSystemOptions, FsNode, FsNodeFlags, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString,
20 LookupVec, RenameContext, SeekTarget, SymlinkTarget, XattrOp, XattrStorage, default_seek,
21 fileops_impl_directory, fileops_impl_nonseekable, fileops_impl_noop_sync,
22 fileops_impl_seekable, fs_node_impl_not_dir, fs_node_impl_symlink, fs_node_impl_xattr_delegate,
23};
24use bstr::ByteSlice;
25use fidl::endpoints::DiscoverableProtocolMarker as _;
26use fidl_fuchsia_io as fio;
27use fidl_fuchsia_starnix_binder as fbinder;
28use fidl_fuchsia_unknown as funknown;
29use fuchsia_runtime::UtcInstant;
30use linux_uapi::SYNC_IOC_MAGIC;
31use once_cell::sync::OnceCell;
32use smallvec::{SmallVec, smallvec};
33use starnix_crypt::EncryptionKeyId;
34use starnix_logging::{CATEGORY_STARNIX_MM, impossible_error, log_warn};
35use starnix_sync::{
36 DynamicLockDepRwLock, 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
1843pub struct AnonymousRemoteFileObject {
1845 io: RemoteIo,
1846
1847 read_only_memory: OnceCell<Arc<MemoryObject>>,
1849
1850 read_exec_memory: OnceCell<Arc<MemoryObject>>,
1852}
1853
1854impl AnonymousRemoteFileObject {
1855 fn new(io: RemoteIo) -> Self {
1856 Self { io, read_only_memory: Default::default(), read_exec_memory: Default::default() }
1857 }
1858}
1859
1860impl FileOps for AnonymousRemoteFileObject {
1861 fileops_impl_seekable!();
1862
1863 fn read(
1864 &self,
1865 _locked: &mut Locked<FileOpsCore>,
1866 _file: &FileObject,
1867 _current_task: &CurrentTask,
1868 offset: usize,
1869 data: &mut dyn OutputBuffer,
1870 ) -> Result<usize, Errno> {
1871 self.io.read_to_output_buffer(offset as u64, data)
1872 }
1873
1874 fn write(
1875 &self,
1876 _locked: &mut Locked<FileOpsCore>,
1877 _file: &FileObject,
1878 _current_task: &CurrentTask,
1879 offset: usize,
1880 data: &mut dyn InputBuffer,
1881 ) -> Result<usize, Errno> {
1882 self.io.write_from_input_buffer(offset as u64, data)
1885 }
1886
1887 fn get_memory(
1888 &self,
1889 _locked: &mut Locked<FileOpsCore>,
1890 _file: &FileObject,
1891 _current_task: &CurrentTask,
1892 _length: Option<usize>,
1893 prot: ProtectionFlags,
1894 ) -> Result<Arc<MemoryObject>, Errno> {
1895 fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
1896 let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
1897 Some(&self.read_exec_memory)
1898 } else if prot == ProtectionFlags::READ {
1899 Some(&self.read_only_memory)
1900 } else {
1901 None
1902 };
1903
1904 memory_cache
1905 .map(|c| c.get_or_try_init(|| self.io.fetch_remote_memory(prot)).cloned())
1906 .unwrap_or_else(|| self.io.fetch_remote_memory(prot))
1907 }
1908
1909 fn to_handle(
1910 &self,
1911 _file: &FileObject,
1912 _current_task: &CurrentTask,
1913 ) -> Result<Option<zx::NullableHandle>, Errno> {
1914 self.io
1917 .clone_proxy()
1918 .map_err(map_sync_io_client_error)
1919 .map(|p| Some(p.into_channel().into()))
1920 }
1921
1922 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
1923 self.io.sync().map_err(map_sync_io_client_error)
1924 }
1925}
1926
1927pub struct RemoteZxioFileObject {
1928 zxio: Zxio,
1931
1932 read_only_memory: OnceCell<Arc<MemoryObject>>,
1934
1935 read_exec_memory: OnceCell<Arc<MemoryObject>>,
1937}
1938
1939impl RemoteZxioFileObject {
1940 fn new(zxio: Zxio) -> RemoteZxioFileObject {
1941 RemoteZxioFileObject {
1942 zxio,
1943 read_only_memory: Default::default(),
1944 read_exec_memory: Default::default(),
1945 }
1946 }
1947
1948 fn fetch_remote_memory(&self, prot: ProtectionFlags) -> Result<Arc<MemoryObject>, Errno> {
1949 let without_exec = self
1950 .zxio
1951 .vmo_get(prot.to_vmar_flags() - zx::VmarFlags::PERM_EXECUTE)
1952 .map_err(|status| from_status_like_fdio!(status))?;
1953 let all_flags = if prot.contains(ProtectionFlags::EXEC) {
1954 without_exec.replace_as_executable(&VMEX_RESOURCE).map_err(impossible_error)?
1955 } else {
1956 without_exec
1957 };
1958 Ok(Arc::new(MemoryObject::from(all_flags)))
1959 }
1960}
1961
1962impl FileOps for RemoteZxioFileObject {
1963 fileops_impl_seekable!();
1964
1965 fn read(
1966 &self,
1967 _locked: &mut Locked<FileOpsCore>,
1968 _file: &FileObject,
1969 _current_task: &CurrentTask,
1970 offset: usize,
1971 data: &mut dyn OutputBuffer,
1972 ) -> Result<usize, Errno> {
1973 let offset = offset as u64;
1974 let read_bytes = with_iovec_segments::<_, syncio::zxio::zx_iovec, _>(data, |iovecs| {
1975 unsafe { self.zxio.readv_at(offset, iovecs).map_err(map_stream_error) }
1977 });
1978
1979 match read_bytes {
1980 Some(actual) => {
1981 let actual = actual?;
1982 unsafe { data.advance(actual) }?;
1985 Ok(actual)
1986 }
1987 None => {
1988 let total = data.available();
1990 let mut bytes = vec![0u8; total];
1991 let actual = self
1992 .zxio
1993 .read_at(offset, &mut bytes)
1994 .map_err(|status| from_status_like_fdio!(status))?;
1995 data.write_all(&bytes[0..actual])
1996 }
1997 }
1998 }
1999
2000 fn write(
2001 &self,
2002 _locked: &mut Locked<FileOpsCore>,
2003 _file: &FileObject,
2004 _current_task: &CurrentTask,
2005 offset: usize,
2006 data: &mut dyn InputBuffer,
2007 ) -> Result<usize, Errno> {
2008 let offset = offset as u64;
2009 let write_bytes = with_iovec_segments::<_, syncio::zxio::zx_iovec, _>(data, |iovecs| {
2010 unsafe { self.zxio.writev_at(offset, iovecs).map_err(map_stream_error) }
2012 });
2013
2014 match write_bytes {
2015 Some(actual) => {
2016 let actual = actual?;
2017 data.advance(actual)?;
2018 Ok(actual)
2019 }
2020 None => {
2021 let bytes = data.peek_all()?;
2023 let actual = self
2024 .zxio
2025 .write_at(offset, &bytes)
2026 .map_err(|status| from_status_like_fdio!(status))?;
2027 data.advance(actual)?;
2028 Ok(actual)
2029 }
2030 }
2031 }
2032
2033 fn get_memory(
2034 &self,
2035 _locked: &mut Locked<FileOpsCore>,
2036 _file: &FileObject,
2037 _current_task: &CurrentTask,
2038 _length: Option<usize>,
2039 prot: ProtectionFlags,
2040 ) -> Result<Arc<MemoryObject>, Errno> {
2041 fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "RemoteFileGetVmo");
2042 let memory_cache = if prot == (ProtectionFlags::READ | ProtectionFlags::EXEC) {
2043 Some(&self.read_exec_memory)
2044 } else if prot == ProtectionFlags::READ {
2045 Some(&self.read_only_memory)
2046 } else {
2047 None
2048 };
2049
2050 memory_cache
2051 .map(|c| c.get_or_try_init(|| self.fetch_remote_memory(prot)).cloned())
2052 .unwrap_or_else(|| self.fetch_remote_memory(prot))
2053 }
2054
2055 fn to_handle(
2056 &self,
2057 _file: &FileObject,
2058 _current_task: &CurrentTask,
2059 ) -> Result<Option<zx::NullableHandle>, Errno> {
2060 self.zxio.clone_handle().map(Some).map_err(|status| from_status_like_fdio!(status))
2061 }
2062
2063 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
2064 self.zxio.sync().map_err(map_sync_error)
2065 }
2066}
2067
2068struct RemoteSymlink {
2069 node: BaseNode,
2070 target: LockDepRwLock<Box<[u8]>, FuchsiaRemoteTargetLock>,
2071}
2072
2073impl RemoteSymlink {
2074 fn new(node: BaseNode, target: impl Into<Box<[u8]>>) -> Self {
2075 Self { node, target: LockDepRwLock::new(target.into()) }
2076 }
2077}
2078
2079impl FsNodeOps for RemoteSymlink {
2080 fs_node_impl_symlink!();
2081 fs_node_impl_xattr_delegate!(self, self.node);
2082
2083 fn readlink(
2084 &self,
2085 _locked: &mut Locked<FileOpsCore>,
2086 _node: &FsNode,
2087 _current_task: &CurrentTask,
2088 ) -> Result<SymlinkTarget, Errno> {
2089 Ok(SymlinkTarget::Path(FsString::new(self.target.read().to_vec())))
2090 }
2091
2092 fn fetch_and_refresh_info<'a>(
2093 &self,
2094 _locked: &mut Locked<FileOpsCore>,
2095 _node: &FsNode,
2096 _current_task: &CurrentTask,
2097 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
2098 ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
2099 self.node.fetch_and_refresh_info(info)
2100 }
2101
2102 fn forget(
2103 self: Box<Self>,
2104 _locked: &mut Locked<FileOpsCore>,
2105 _current_task: &CurrentTask,
2106 info: FsNodeInfo,
2107 ) -> Result<(), Errno> {
2108 if info.pending_time_access_update {
2110 self.node
2111 .io
2112 .attr_get(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
2113 .map_err(|status| from_status_like_fdio!(status))?;
2114 }
2115 Ok(())
2116 }
2117}
2118
2119pub struct RemoteCounter {
2120 counter: Counter,
2121 koid: std::sync::OnceLock<zx::Koid>,
2122}
2123
2124impl RemoteCounter {
2125 fn new(counter: Counter) -> Self {
2126 Self { counter, koid: std::sync::OnceLock::new() }
2127 }
2128
2129 pub fn duplicate_handle(&self) -> Result<Counter, Errno> {
2130 self.counter.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(impossible_error)
2131 }
2132
2133 pub fn koid(&self) -> zx::Koid {
2134 *self.koid.get_or_init(|| self.counter.koid().unwrap())
2135 }
2136}
2137
2138impl FileOps for RemoteCounter {
2139 fileops_impl_nonseekable!();
2140 fileops_impl_noop_sync!();
2141
2142 fn read(
2143 &self,
2144 _locked: &mut Locked<FileOpsCore>,
2145 _file: &FileObject,
2146 _current_task: &CurrentTask,
2147 _offset: usize,
2148 _data: &mut dyn OutputBuffer,
2149 ) -> Result<usize, Errno> {
2150 error!(ENOTSUP)
2151 }
2152
2153 fn write(
2154 &self,
2155 _locked: &mut Locked<FileOpsCore>,
2156 _file: &FileObject,
2157 _current_task: &CurrentTask,
2158 _offset: usize,
2159 _data: &mut dyn InputBuffer,
2160 ) -> Result<usize, Errno> {
2161 error!(ENOTSUP)
2162 }
2163
2164 fn ioctl(
2165 &self,
2166 locked: &mut Locked<Unlocked>,
2167 file: &FileObject,
2168 current_task: &CurrentTask,
2169 request: u32,
2170 arg: SyscallArg,
2171 ) -> Result<SyscallResult, Errno> {
2172 let ioctl_type = (request >> 8) as u8;
2173 let ioctl_number = request as u8;
2174 if ioctl_type == SYNC_IOC_MAGIC
2175 && (ioctl_number == SYNC_IOC_FILE_INFO || ioctl_number == SYNC_IOC_MERGE)
2176 {
2177 let mut sync_points = Vec::with_capacity(1);
2178 let counter = self.duplicate_handle()?;
2179 let sp = if ioctl_number == SYNC_IOC_MERGE {
2182 SyncPoint::with_koid(Timeline::Hwc, counter.into(), self.koid())
2183 } else {
2184 SyncPoint::new(Timeline::Hwc, counter.into())
2185 };
2186 sync_points.push(sp);
2187 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";
2188 let sync_file = SyncFile::new(*sync_file_name, SyncFence { sync_points });
2189 return sync_file.ioctl(locked, file, current_task, request, arg);
2190 }
2191
2192 error!(EINVAL)
2193 }
2194}
2195
2196#[track_caller]
2197fn map_sync_error(status: zx::Status) -> Errno {
2198 match status {
2199 zx::Status::NO_RESOURCES | zx::Status::NO_MEMORY | zx::Status::NO_SPACE => {
2200 errno!(ENOSPC)
2201 }
2202 zx::Status::INVALID_ARGS | zx::Status::NOT_FILE => errno!(EINVAL),
2203 zx::Status::BAD_HANDLE => errno!(EBADFD),
2204 zx::Status::NOT_SUPPORTED => errno!(ENOTSUP),
2205 zx::Status::INTERRUPTED_RETRY => errno!(EINTR),
2206 _ => errno!(EIO),
2207 }
2208}
2209
2210#[track_caller]
2211fn map_stream_error(status: zx::Status) -> Errno {
2212 match status {
2213 zx::Status::INVALID_ARGS | zx::Status::NOT_FOUND => errno!(EFAULT),
2216 status => from_status_like_fdio!(status),
2217 }
2218}
2219
2220#[track_caller]
2221fn map_sync_io_client_error(status: zx::Status) -> Errno {
2222 from_status_like_fdio!(status)
2223}
2224
2225struct InfoState(AtomicU32);
2230
2231impl InfoState {
2232 const IN_SYNC: u32 = 0x8000_0000;
2235
2236 const PENDING_REFRESH: u32 = 0x4000_0000;
2239
2240 const TRUNCATED: u32 = 0x2000_0000;
2243
2244 const COUNT_MASK: u32 = Self::TRUNCATED - 1;
2246
2247 fn new(dirty: bool) -> Self {
2248 Self(AtomicU32::new(if dirty { 0 } else { Self::IN_SYNC }))
2249 }
2250
2251 fn dirty_op_guard(&self, for_truncate: bool) -> DirtyOpGuard<'_> {
2254 let mut current = self.0.load(Ordering::Relaxed);
2258 let for_truncate = if for_truncate { Self::TRUNCATED } else { 0 };
2259 loop {
2260 assert!(current & Self::COUNT_MASK != Self::COUNT_MASK); match self.0.compare_exchange_weak(
2262 current,
2263 ((current & !Self::IN_SYNC) + 1) | for_truncate,
2264 Ordering::Relaxed,
2265 Ordering::Relaxed,
2266 ) {
2267 Ok(_) => break,
2268 Err(old) => current = old,
2269 }
2270 }
2271 DirtyOpGuard(self)
2272 }
2273
2274 fn maybe_refresh<'a, T: 'a>(
2277 &self,
2278 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
2279 refresh: impl FnOnce(&'a DynamicLockDepRwLock<FsNodeInfo>) -> Result<T, Errno>,
2280 not_needed: impl FnOnce(&'a DynamicLockDepRwLock<FsNodeInfo>) -> Result<T, Errno>,
2281 ) -> Result<T, Errno> {
2282 let mut current = self.0.load(Ordering::Relaxed);
2283
2284 let mut did_set_pending_refresh = false;
2292 while current & !Self::TRUNCATED == 0 {
2293 match self.0.compare_exchange_weak(
2294 current,
2295 current | Self::IN_SYNC | Self::PENDING_REFRESH,
2296 Ordering::Relaxed,
2297 Ordering::Relaxed,
2298 ) {
2299 Ok(_) => {
2300 did_set_pending_refresh = true;
2301 break;
2302 }
2303 Err(old) => current = old,
2304 }
2305 }
2306
2307 if current == Self::IN_SYNC {
2311 return not_needed(info);
2312 }
2313
2314 let result = refresh(info);
2315
2316 if did_set_pending_refresh {
2317 if result.is_ok() {
2318 if current & Self::TRUNCATED != 0 {
2321 let mut current = Self::TRUNCATED | Self::IN_SYNC | Self::PENDING_REFRESH;
2324 while current == Self::TRUNCATED | Self::IN_SYNC | Self::PENDING_REFRESH {
2325 match self.0.compare_exchange_weak(
2326 current,
2327 Self::IN_SYNC,
2328 Ordering::Relaxed,
2329 Ordering::Relaxed,
2330 ) {
2331 Ok(_) => return result,
2332 Err(old) => current = old,
2333 }
2334 }
2335 }
2338 self.0.fetch_and(!Self::PENDING_REFRESH, Ordering::Relaxed);
2339 } else {
2340 self.0.fetch_and(!(Self::IN_SYNC | Self::PENDING_REFRESH), Ordering::Relaxed);
2343 }
2344 }
2345
2346 result
2347 }
2348
2349 fn is_size_accurate(&self) -> bool {
2351 let state = self.0.load(Ordering::Relaxed);
2365 state & (Self::TRUNCATED | Self::COUNT_MASK) == 0
2366 }
2367}
2368
2369struct DirtyOpGuard<'a>(&'a InfoState);
2370
2371impl Drop for DirtyOpGuard<'_> {
2372 fn drop(&mut self) {
2373 self.0.0.fetch_sub(1, Ordering::Relaxed);
2375 }
2376}
2377
2378fn will_dirty<'a, N: TryInto<&'a BaseNode> + Copy, T>(nodes: &[N], f: impl FnOnce() -> T) -> T {
2380 let _guards: SmallVec<[_; 4]> = nodes
2394 .iter()
2395 .filter_map(|n| N::try_into(*n).ok())
2396 .map(|n| n.info_state.dirty_op_guard(false))
2397 .collect();
2398
2399 f()
2400}
2401
2402#[cfg(test)]
2403mod test {
2404 use super::*;
2405 use crate::mm::PAGE_SIZE;
2406 use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
2407 use crate::testing::*;
2408 use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
2409 use crate::vfs::socket::{SocketFile, SocketMessageFlags};
2410 use crate::vfs::{EpollFileObject, LookupContext, Namespace, SymlinkMode, TimeUpdateType};
2411 use assert_matches::assert_matches;
2412 use fidl::endpoints::{ServerEnd, create_request_stream};
2413 use fidl_fuchsia_io as fio;
2414 use flyweights::FlyByteStr;
2415 use fuchsia_async as fasync;
2416 use fuchsia_runtime::UtcDuration;
2417 use futures::StreamExt;
2418 use fxfs_testing::{TestFixture, TestFixtureOptions};
2419 use starnix_sync::Mutex;
2420 use starnix_uapi::auth::Credentials;
2421 use starnix_uapi::errors::EINVAL;
2422 use starnix_uapi::file_mode::{AccessCheck, mode};
2423 use starnix_uapi::ino_t;
2424 use starnix_uapi::mount_flags::MountpointFlags;
2425 use starnix_uapi::open_flags::OpenFlags;
2426 use starnix_uapi::vfs::{EpollEvent, FdEvents};
2427 use std::sync::Barrier;
2428 use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
2429 use storage_device::DeviceHolder;
2430 use storage_device::fake_device::FakeDevice;
2431
2432 #[::fuchsia::test]
2433 async fn test_remote_uds() {
2434 spawn_kernel_and_run(async |locked, current_task| {
2435 let (s1, s2) = zx::Socket::create_datagram();
2436 s2.write(&vec![0]).expect("write");
2437 let file = new_remote_file(locked, ¤t_task, s1.into(), OpenFlags::RDWR)
2438 .expect("new_remote_file");
2439 assert!(file.node().is_sock());
2440 let socket_ops = file.downcast_file::<SocketFile>().unwrap();
2441 let flags = SocketMessageFlags::CTRUNC
2442 | SocketMessageFlags::TRUNC
2443 | SocketMessageFlags::NOSIGNAL
2444 | SocketMessageFlags::CMSG_CLOEXEC;
2445 let mut buffer = VecOutputBuffer::new(1024);
2446 let info = socket_ops
2447 .recvmsg(locked, ¤t_task, &file, &mut buffer, flags, None)
2448 .expect("recvmsg");
2449 assert!(info.ancillary_data.is_empty());
2450 assert_eq!(info.message_length, 1);
2451 })
2452 .await;
2453 }
2454
2455 #[::fuchsia::test]
2456 async fn test_tree() {
2457 spawn_kernel_and_run(async |locked, current_task| {
2458 let kernel = current_task.kernel();
2459 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
2460 let (server, client) = zx::Channel::create();
2461 fdio::open("/pkg", rights, server).expect("failed to open /pkg");
2462 let fs = RemoteFs::new_fs(
2463 locked,
2464 &kernel,
2465 client,
2466 FileSystemOptions { source: FlyByteStr::new(b"/pkg"), ..Default::default() },
2467 rights,
2468 )
2469 .unwrap();
2470 let ns = Namespace::new(fs);
2471 let root = ns.root();
2472 let mut context = LookupContext::default();
2473 assert_eq!(
2474 root.lookup_child(locked, ¤t_task, &mut context, "nib".into()).err(),
2475 Some(errno!(ENOENT))
2476 );
2477 let mut context = LookupContext::default();
2478 root.lookup_child(locked, ¤t_task, &mut context, "lib".into()).unwrap();
2479
2480 let mut context = LookupContext::default();
2481 let _test_file = root
2482 .lookup_child(
2483 locked,
2484 ¤t_task,
2485 &mut context,
2486 "data/tests/hello_starnix".into(),
2487 )
2488 .unwrap()
2489 .open(locked, ¤t_task, OpenFlags::RDONLY, AccessCheck::default())
2490 .unwrap();
2491 })
2492 .await;
2493 }
2494
2495 #[::fuchsia::test]
2496 async fn test_blocking_io() {
2497 spawn_kernel_and_run(async |locked, current_task| {
2498 let (client, server) = zx::Socket::create_stream();
2499 let pipe = create_fuchsia_pipe(locked, ¤t_task, client, OpenFlags::RDWR).unwrap();
2500
2501 let bytes = [0u8; 64];
2502 assert_eq!(bytes.len(), server.write(&bytes).unwrap());
2503
2504 let bytes_read =
2506 pipe.read(locked, ¤t_task, &mut VecOutputBuffer::new(64)).unwrap();
2507
2508 assert_eq!(bytes_read, bytes.len());
2509 })
2510 .await;
2511 }
2512
2513 #[::fuchsia::test]
2514 async fn test_poll() {
2515 spawn_kernel_and_run(async |locked, current_task| {
2516 let (client, server) = zx::Socket::create_stream();
2517 let pipe = create_fuchsia_pipe(locked, ¤t_task, client, OpenFlags::RDWR)
2518 .expect("create_fuchsia_pipe");
2519 let server_zxio = Zxio::create(server.into_handle()).expect("Zxio::create");
2520
2521 assert_eq!(
2522 pipe.query_events(locked, ¤t_task),
2523 Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
2524 );
2525
2526 let epoll_object = EpollFileObject::new_file(locked, ¤t_task);
2527 let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
2528 let event = EpollEvent::new(FdEvents::POLLIN, 0);
2529 epoll_file
2530 .add(locked, ¤t_task, &pipe, &epoll_object, event)
2531 .expect("poll_file.add");
2532
2533 let fds = epoll_file
2534 .wait(locked, ¤t_task, 1, zx::MonotonicInstant::ZERO)
2535 .expect("wait");
2536 assert!(fds.is_empty());
2537
2538 assert_eq!(server_zxio.write(&[0]).expect("write"), 1);
2539
2540 assert_eq!(
2541 pipe.query_events(locked, ¤t_task),
2542 Ok(FdEvents::POLLOUT
2543 | FdEvents::POLLWRNORM
2544 | FdEvents::POLLIN
2545 | FdEvents::POLLRDNORM)
2546 );
2547 let fds = epoll_file
2548 .wait(locked, ¤t_task, 1, zx::MonotonicInstant::ZERO)
2549 .expect("wait");
2550 assert_eq!(fds.len(), 1);
2551
2552 assert_eq!(
2553 pipe.read(locked, ¤t_task, &mut VecOutputBuffer::new(64)).expect("read"),
2554 1
2555 );
2556
2557 assert_eq!(
2558 pipe.query_events(locked, ¤t_task),
2559 Ok(FdEvents::POLLOUT | FdEvents::POLLWRNORM)
2560 );
2561 let fds = epoll_file
2562 .wait(locked, ¤t_task, 1, zx::MonotonicInstant::ZERO)
2563 .expect("wait");
2564 assert!(fds.is_empty());
2565 })
2566 .await;
2567 }
2568
2569 #[::fuchsia::test]
2570 async fn test_new_remote_directory() {
2571 spawn_kernel_and_run(async |locked, current_task| {
2572 let (server, client) = zx::Channel::create();
2573 fdio::open("/pkg", fio::PERM_READABLE | fio::PERM_EXECUTABLE, server)
2574 .expect("failed to open /pkg");
2575
2576 let fd = new_remote_file(locked, ¤t_task, client.into(), OpenFlags::RDWR)
2577 .expect("new_remote_file");
2578 assert!(fd.node().is_dir());
2579 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2580 })
2581 .await;
2582 }
2583
2584 #[::fuchsia::test]
2585 async fn test_new_remote_file() {
2586 spawn_kernel_and_run(async |locked, current_task| {
2587 let (server, client) = zx::Channel::create();
2588 fdio::open("/pkg/meta/contents", fio::PERM_READABLE, server)
2589 .expect("failed to open /pkg/meta/contents");
2590
2591 let fd = new_remote_file(locked, ¤t_task, client.into(), OpenFlags::RDONLY)
2592 .expect("new_remote_file");
2593 assert!(!fd.node().is_dir());
2594 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2595 })
2596 .await;
2597 }
2598
2599 #[::fuchsia::test]
2600 async fn test_new_remote_counter() {
2601 spawn_kernel_and_run(async |locked, current_task| {
2602 let counter = zx::Counter::create();
2603
2604 let fd = new_remote_file(locked, ¤t_task, counter.into(), OpenFlags::RDONLY)
2605 .expect("new_remote_file");
2606 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2607 })
2608 .await;
2609 }
2610
2611 #[::fuchsia::test]
2612 async fn test_new_remote_vmo() {
2613 spawn_kernel_and_run(async |locked, current_task| {
2614 let vmo = zx::Vmo::create(*PAGE_SIZE).expect("Vmo::create");
2615 let fd = new_remote_file(locked, ¤t_task, vmo.into(), OpenFlags::RDWR)
2616 .expect("new_remote_file");
2617 assert!(!fd.node().is_dir());
2618 assert!(fd.to_handle(¤t_task).expect("to_handle").is_some());
2619 })
2620 .await;
2621 }
2622
2623 #[::fuchsia::test(threads = 2)]
2624 async fn test_symlink() {
2625 let fixture = TestFixture::new().await;
2626 let (server, client) = zx::Channel::create();
2627 fixture.root().clone(server.into()).expect("clone failed");
2628
2629 const LINK_PATH: &'static str = "symlink";
2630 const LINK_TARGET: &'static str = "私は「UTF8」です";
2631 const LINK_SIZE: usize = 22;
2634 assert_eq!(LINK_SIZE, LINK_TARGET.len());
2635
2636 spawn_kernel_and_run(async move |locked, current_task| {
2637 let kernel = current_task.kernel();
2638 let fs = RemoteFs::new_fs(
2639 locked,
2640 &kernel,
2641 client,
2642 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2643 fio::PERM_READABLE | fio::PERM_WRITABLE,
2644 )
2645 .expect("new_fs failed");
2646 let ns = Namespace::new(fs);
2647 let root = ns.root();
2648 let symlink_node = root
2649 .create_symlink(locked, ¤t_task, LINK_PATH.into(), LINK_TARGET.into())
2650 .expect("symlink failed");
2651 assert_matches!(&*symlink_node.entry.node.info(), FsNodeInfo { size: LINK_SIZE, .. });
2652
2653 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2654 let child = root
2655 .lookup_child(locked, ¤t_task, &mut context, "symlink".into())
2656 .expect("lookup_child failed");
2657
2658 match child.readlink(locked, ¤t_task).expect("readlink failed") {
2659 SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2660 SymlinkTarget::Node(_) => panic!("readlink returned SymlinkTarget::Node"),
2661 }
2662 let stat_result = child.entry.node.stat(locked, ¤t_task).expect("stat failed");
2664 assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2665 })
2666 .await;
2667
2668 let fixture = TestFixture::open(
2670 fixture.close().await,
2671 TestFixtureOptions { format: false, ..Default::default() },
2672 )
2673 .await;
2674 let (server, client) = zx::Channel::create();
2675 fixture.root().clone(server.into()).expect("clone failed after remount");
2676
2677 spawn_kernel_and_run(async move |locked, current_task| {
2678 let kernel = current_task.kernel();
2679 let fs = RemoteFs::new_fs(
2680 locked,
2681 &kernel,
2682 client,
2683 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2684 fio::PERM_READABLE | fio::PERM_WRITABLE,
2685 )
2686 .expect("new_fs failed after remount");
2687 let ns = Namespace::new(fs);
2688 let root = ns.root();
2689 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2690 let child = root
2691 .lookup_child(locked, ¤t_task, &mut context, "symlink".into())
2692 .expect("lookup_child failed after remount");
2693
2694 match child.readlink(locked, ¤t_task).expect("readlink failed after remount") {
2695 SymlinkTarget::Path(path) => assert_eq!(path, LINK_TARGET),
2696 SymlinkTarget::Node(_) => {
2697 panic!("readlink returned SymlinkTarget::Node after remount")
2698 }
2699 }
2700 let stat_result =
2702 child.entry.node.stat(locked, ¤t_task).expect("stat failed after remount");
2703 assert_eq!(stat_result.st_size as usize, LINK_SIZE);
2704 })
2705 .await;
2706
2707 fixture.close().await;
2708 }
2709
2710 #[::fuchsia::test]
2711 async fn test_mode_uid_gid_and_dev_persists() {
2712 const FILE_MODE: FileMode = mode!(IFREG, 0o467);
2713 const DIR_MODE: FileMode = mode!(IFDIR, 0o647);
2714 const BLK_MODE: FileMode = mode!(IFBLK, 0o746);
2715
2716 let fixture = TestFixture::new().await;
2717 let (server, client) = zx::Channel::create();
2718 fixture.root().clone(server.into()).expect("clone failed");
2719
2720 spawn_kernel_and_run(async move |locked, current_task| {
2722 let kernel = current_task.kernel();
2723 let creds = Credentials::clone(¤t_task.current_creds());
2724 current_task.set_creds(Credentials { euid: 1, fsuid: 1, egid: 2, fsgid: 2, ..creds });
2725 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2726 let fs = RemoteFs::new_fs(
2727 locked,
2728 &kernel,
2729 client,
2730 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2731 rights,
2732 )
2733 .expect("new_fs failed");
2734 let ns = Namespace::new(fs);
2735 current_task.fs().set_umask(FileMode::from_bits(0));
2736 ns.root()
2737 .create_node(locked, ¤t_task, "file".into(), FILE_MODE, DeviceId::NONE)
2738 .expect("create_node failed");
2739 ns.root()
2740 .create_node(locked, ¤t_task, "dir".into(), DIR_MODE, DeviceId::NONE)
2741 .expect("create_node failed");
2742 ns.root()
2743 .create_node(locked, ¤t_task, "dev".into(), BLK_MODE, DeviceId::RANDOM)
2744 .expect("create_node failed");
2745 })
2746 .await;
2747
2748 let fixture = TestFixture::open(
2750 fixture.close().await,
2751 TestFixtureOptions { format: false, ..Default::default() },
2752 )
2753 .await;
2754
2755 let (server, client) = zx::Channel::create();
2756 fixture.root().clone(server.into()).expect("clone failed");
2757
2758 spawn_kernel_and_run(async move |locked, current_task| {
2759 let kernel = current_task.kernel();
2760 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2761 let fs = RemoteFs::new_fs(
2762 locked,
2763 &kernel,
2764 client,
2765 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2766 rights,
2767 )
2768 .expect("new_fs failed");
2769 let ns = Namespace::new(fs);
2770 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2771 let child = ns
2772 .root()
2773 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2774 .expect("lookup_child failed");
2775 assert_matches!(
2776 &*child.entry.node.info(),
2777 FsNodeInfo { mode: FILE_MODE, uid: 1, gid: 2, rdev: DeviceId::NONE, .. }
2778 );
2779 let child = ns
2780 .root()
2781 .lookup_child(locked, ¤t_task, &mut context, "dir".into())
2782 .expect("lookup_child failed");
2783 assert_matches!(
2784 &*child.entry.node.info(),
2785 FsNodeInfo { mode: DIR_MODE, uid: 1, gid: 2, rdev: DeviceId::NONE, .. }
2786 );
2787 let child = ns
2788 .root()
2789 .lookup_child(locked, ¤t_task, &mut context, "dev".into())
2790 .expect("lookup_child failed");
2791 assert_matches!(
2792 &*child.entry.node.info(),
2793 FsNodeInfo { mode: BLK_MODE, uid: 1, gid: 2, rdev: DeviceId::RANDOM, .. }
2794 );
2795 })
2796 .await;
2797 fixture.close().await;
2798 }
2799
2800 #[::fuchsia::test]
2801 async fn test_dot_dot_inode_numbers() {
2802 let fixture = TestFixture::new().await;
2803 let (server, client) = zx::Channel::create();
2804 fixture.root().clone(server.into()).expect("clone failed");
2805
2806 const MODE: FileMode = FileMode::from_bits(FileMode::IFDIR.bits() | 0o777);
2807
2808 spawn_kernel_and_run(async |locked, current_task| {
2809 let kernel = current_task.kernel();
2810 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2811 let fs = RemoteFs::new_fs(
2812 locked,
2813 &kernel,
2814 client,
2815 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2816 rights,
2817 )
2818 .expect("new_fs failed");
2819 let ns = Namespace::new(fs);
2820 current_task.fs().set_umask(FileMode::from_bits(0));
2821 let sub_dir1 = ns
2822 .root()
2823 .create_node(locked, ¤t_task, "dir".into(), MODE, DeviceId::NONE)
2824 .expect("create_node failed");
2825 let sub_dir2 = sub_dir1
2826 .create_node(locked, ¤t_task, "dir".into(), MODE, DeviceId::NONE)
2827 .expect("create_node failed");
2828
2829 let dir_handle = ns
2830 .root()
2831 .entry
2832 .open_anonymous(locked, ¤t_task, OpenFlags::RDONLY)
2833 .expect("open failed");
2834
2835 #[derive(Default)]
2836 struct Sink {
2837 offset: off_t,
2838 dot_dot_inode_num: u64,
2839 }
2840 impl DirentSink for Sink {
2841 fn add(
2842 &mut self,
2843 inode_num: ino_t,
2844 offset: off_t,
2845 entry_type: DirectoryEntryType,
2846 name: &FsStr,
2847 ) -> Result<(), Errno> {
2848 if name == ".." {
2849 self.dot_dot_inode_num = inode_num;
2850 assert_eq!(entry_type, DirectoryEntryType::DIR);
2851 }
2852 self.offset = offset;
2853 Ok(())
2854 }
2855 fn offset(&self) -> off_t {
2856 self.offset
2857 }
2858 }
2859 let mut sink = Sink::default();
2860 dir_handle.readdir(locked, ¤t_task, &mut sink).expect("readdir failed");
2861
2862 assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2864
2865 let dir_handle = sub_dir1
2866 .entry
2867 .open_anonymous(locked, ¤t_task, OpenFlags::RDONLY)
2868 .expect("open failed");
2869 let mut sink = Sink::default();
2870 dir_handle.readdir(locked, ¤t_task, &mut sink).expect("readdir failed");
2871
2872 assert_eq!(sink.dot_dot_inode_num, ns.root().entry.node.ino);
2874
2875 let dir_handle = sub_dir2
2876 .entry
2877 .open_anonymous(locked, ¤t_task, OpenFlags::RDONLY)
2878 .expect("open failed");
2879 let mut sink = Sink::default();
2880 dir_handle.readdir(locked, ¤t_task, &mut sink).expect("readdir failed");
2881
2882 assert_eq!(sink.dot_dot_inode_num, sub_dir1.entry.node.ino);
2884 })
2885 .await;
2886 fixture.close().await;
2887 }
2888
2889 #[::fuchsia::test]
2890 async fn test_remote_special_node() {
2891 let fixture = TestFixture::new().await;
2892 let (server, client) = zx::Channel::create();
2893 fixture.root().clone(server.into()).expect("clone failed");
2894
2895 const FIFO_MODE: FileMode = FileMode::from_bits(FileMode::IFIFO.bits() | 0o777);
2896 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
2897
2898 spawn_kernel_and_run(async |locked, current_task| {
2899 let kernel = current_task.kernel();
2900 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2901 let fs = RemoteFs::new_fs(
2902 locked,
2903 &kernel,
2904 client,
2905 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2906 rights,
2907 )
2908 .expect("new_fs failed");
2909 let ns = Namespace::new(fs);
2910 current_task.fs().set_umask(FileMode::from_bits(0));
2911 let root = ns.root();
2912
2913 root.create_node(locked, ¤t_task, "fifo".into(), FIFO_MODE, DeviceId::NONE)
2915 .expect("create_node failed");
2916 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2917 let fifo_node = root
2918 .lookup_child(locked, ¤t_task, &mut context, "fifo".into())
2919 .expect("lookup_child failed");
2920
2921 match fifo_node.truncate(locked, ¤t_task, 0) {
2924 Ok(_) => {
2925 panic!("truncate passed for special node")
2926 }
2927 Err(errno) if errno == EINVAL => {}
2928 Err(e) => {
2929 panic!("truncate failed with error {:?}", e)
2930 }
2931 };
2932
2933 root.create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
2935 .expect("create_node failed");
2936 let mut context = LookupContext::new(SymlinkMode::NoFollow);
2937 let reg_node = root
2938 .lookup_child(locked, ¤t_task, &mut context, "file".into())
2939 .expect("lookup_child failed");
2940
2941 reg_node.truncate(locked, ¤t_task, 0).expect("truncate failed");
2943 })
2944 .await;
2945 fixture.close().await;
2946 }
2947
2948 #[::fuchsia::test]
2949 async fn test_hard_link() {
2950 let fixture = TestFixture::new().await;
2951 let (server, client) = zx::Channel::create();
2952 fixture.root().clone(server.into()).expect("clone failed");
2953
2954 spawn_kernel_and_run(async move |locked, current_task| {
2955 let kernel = current_task.kernel();
2956 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2957 let fs = RemoteFs::new_fs(
2958 locked,
2959 &kernel,
2960 client,
2961 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
2962 rights,
2963 )
2964 .expect("new_fs failed");
2965 let ns = Namespace::new(fs);
2966 current_task.fs().set_umask(FileMode::from_bits(0));
2967 let node = ns
2968 .root()
2969 .create_node(
2970 locked,
2971 ¤t_task,
2972 "file1".into(),
2973 mode!(IFREG, 0o666),
2974 DeviceId::NONE,
2975 )
2976 .expect("create_node failed");
2977 ns.root()
2978 .entry
2979 .node
2980 .link(locked, ¤t_task, &ns.root().mount, "file2".into(), &node.entry.node)
2981 .expect("link failed");
2982 })
2983 .await;
2984
2985 let fixture = TestFixture::open(
2986 fixture.close().await,
2987 TestFixtureOptions { format: false, ..Default::default() },
2988 )
2989 .await;
2990
2991 let (server, client) = zx::Channel::create();
2992 fixture.root().clone(server.into()).expect("clone failed");
2993
2994 spawn_kernel_and_run(async move |locked, current_task| {
2995 let kernel = current_task.kernel();
2996 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
2997 let fs = RemoteFs::new_fs(
2998 locked,
2999 &kernel,
3000 client,
3001 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3002 rights,
3003 )
3004 .expect("new_fs failed");
3005 let ns = Namespace::new(fs);
3006 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3007 let child1 = ns
3008 .root()
3009 .lookup_child(locked, ¤t_task, &mut context, "file1".into())
3010 .expect("lookup_child failed");
3011 let child2 = ns
3012 .root()
3013 .lookup_child(locked, ¤t_task, &mut context, "file2".into())
3014 .expect("lookup_child failed");
3015 assert!(Arc::ptr_eq(&child1.entry.node, &child2.entry.node));
3016 })
3017 .await;
3018 fixture.close().await;
3019 }
3020
3021 #[::fuchsia::test]
3022 async fn test_lookup_on_fsverity_enabled_file() {
3023 let fixture = TestFixture::new().await;
3024 let (server, client) = zx::Channel::create();
3025 fixture.root().clone(server.into()).expect("clone failed");
3026
3027 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3028
3029 spawn_kernel_and_run(async move |locked, current_task| {
3030 let kernel = current_task.kernel();
3031 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3032 let fs = RemoteFs::new_fs(
3033 locked,
3034 &kernel,
3035 client,
3036 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3037 rights,
3038 )
3039 .expect("new_fs failed");
3040 let ns = Namespace::new(fs);
3041 current_task.fs().set_umask(FileMode::from_bits(0));
3042 let file = ns
3043 .root()
3044 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceId::NONE)
3045 .expect("create_node failed");
3046 let desc = fsverity_descriptor {
3048 version: 1,
3049 hash_algorithm: 1,
3050 salt_size: 32,
3051 log_blocksize: 12,
3052 ..Default::default()
3053 };
3054 file.entry
3055 .node
3056 .enable_fsverity(locked, current_task, &desc)
3057 .expect("enable fsverity failed");
3058 })
3059 .await;
3060
3061 let fixture = TestFixture::open(
3064 fixture.close().await,
3065 TestFixtureOptions { format: false, ..Default::default() },
3066 )
3067 .await;
3068 let (server, client) = zx::Channel::create();
3069 fixture.root().clone(server.into()).expect("clone failed");
3070
3071 spawn_kernel_and_run(async move |locked, current_task| {
3072 let kernel = current_task.kernel();
3073 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3074 let fs = RemoteFs::new_fs(
3075 locked,
3076 &kernel,
3077 client,
3078 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3079 rights,
3080 )
3081 .expect("new_fs failed");
3082 let ns = Namespace::new(fs);
3083 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3084 let _child = ns
3085 .root()
3086 .lookup_child(locked, ¤t_task, &mut context, "file".into())
3087 .expect("lookup_child failed");
3088 })
3089 .await;
3090 fixture.close().await;
3091 }
3092
3093 #[::fuchsia::test]
3094 async fn test_update_attributes_persists() {
3095 let fixture = TestFixture::new().await;
3096 let (server, client) = zx::Channel::create();
3097 fixture.root().clone(server.into()).expect("clone failed");
3098
3099 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3100
3101 spawn_kernel_and_run(async move |locked, current_task| {
3102 let kernel = current_task.kernel();
3103 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3104 let fs = RemoteFs::new_fs(
3105 locked,
3106 &kernel,
3107 client,
3108 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3109 rights,
3110 )
3111 .expect("new_fs failed");
3112 let ns = Namespace::new(fs);
3113 current_task.fs().set_umask(FileMode::from_bits(0));
3114 let file = ns
3115 .root()
3116 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceId::NONE)
3117 .expect("create_node failed");
3118 file.entry
3120 .node
3121 .chmod(locked, ¤t_task, &file.mount, MODE | FileMode::ALLOW_ALL)
3122 .expect("chmod failed");
3123 })
3124 .await;
3125
3126 let fixture = TestFixture::open(
3128 fixture.close().await,
3129 TestFixtureOptions { format: false, ..Default::default() },
3130 )
3131 .await;
3132 let (server, client) = zx::Channel::create();
3133 fixture.root().clone(server.into()).expect("clone failed");
3134
3135 spawn_kernel_and_run(async move |locked, current_task| {
3136 let kernel = current_task.kernel();
3137 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3138 let fs = RemoteFs::new_fs(
3139 locked,
3140 &kernel,
3141 client,
3142 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3143 rights,
3144 )
3145 .expect("new_fs failed");
3146 let ns = Namespace::new(fs);
3147 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3148 let child = ns
3149 .root()
3150 .lookup_child(locked, ¤t_task, &mut context, "file".into())
3151 .expect("lookup_child failed");
3152 assert_eq!(child.entry.node.info().mode, MODE | FileMode::ALLOW_ALL);
3153 })
3154 .await;
3155 fixture.close().await;
3156 }
3157
3158 #[::fuchsia::test]
3159 async fn test_statfs() {
3160 let fixture = TestFixture::new().await;
3161 let (server, client) = zx::Channel::create();
3162 fixture.root().clone(server.into()).expect("clone failed");
3163
3164 spawn_kernel_and_run(async move |locked, current_task| {
3165 let kernel = current_task.kernel();
3166 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3167 let fs = RemoteFs::new_fs(
3168 locked,
3169 &kernel,
3170 client,
3171 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3172 rights,
3173 )
3174 .expect("new_fs failed");
3175
3176 let statfs = fs.statfs(locked, ¤t_task).expect("statfs failed");
3177 assert!(statfs.f_type != 0);
3178 assert!(statfs.f_bsize > 0);
3179 assert!(statfs.f_blocks > 0);
3180 assert!(statfs.f_bfree > 0 && statfs.f_bfree <= statfs.f_blocks);
3181 assert!(statfs.f_files > 0);
3182 assert!(statfs.f_ffree > 0 && statfs.f_ffree <= statfs.f_files);
3183 assert!(statfs.f_fsid.val[0] != 0 || statfs.f_fsid.val[1] != 0);
3184 assert!(statfs.f_namelen > 0);
3185 assert!(statfs.f_frsize > 0);
3186 })
3187 .await;
3188
3189 fixture.close().await;
3190 }
3191
3192 #[::fuchsia::test]
3193 async fn test_allocate() {
3194 let fixture = TestFixture::new().await;
3195 let (server, client) = zx::Channel::create();
3196 fixture.root().clone(server.into()).expect("clone failed");
3197
3198 spawn_kernel_and_run(async move |locked, current_task| {
3199 let kernel = current_task.kernel();
3200 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3201 let fs = RemoteFs::new_fs(
3202 locked,
3203 &kernel,
3204 client,
3205 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3206 rights,
3207 )
3208 .expect("new_fs failed");
3209 let ns = Namespace::new(fs);
3210 current_task.fs().set_umask(FileMode::from_bits(0));
3211 let root = ns.root();
3212
3213 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
3214 root.create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
3215 .expect("create_node failed");
3216 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3217 let reg_node = root
3218 .lookup_child(locked, ¤t_task, &mut context, "file".into())
3219 .expect("lookup_child failed");
3220
3221 reg_node
3222 .entry
3223 .node
3224 .fallocate(locked, ¤t_task, FallocMode::Allocate { keep_size: false }, 0, 20)
3225 .expect("truncate failed");
3226 })
3227 .await;
3228 fixture.close().await;
3229 }
3230
3231 #[::fuchsia::test]
3232 async fn test_allocate_overflow() {
3233 let fixture = TestFixture::new().await;
3234 let (server, client) = zx::Channel::create();
3235 fixture.root().clone(server.into()).expect("clone failed");
3236
3237 spawn_kernel_and_run(async move |locked, current_task| {
3238 let kernel = current_task.kernel();
3239 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3240 let fs = RemoteFs::new_fs(
3241 locked,
3242 &kernel,
3243 client,
3244 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3245 rights,
3246 )
3247 .expect("new_fs failed");
3248 let ns = Namespace::new(fs);
3249 current_task.fs().set_umask(FileMode::from_bits(0));
3250 let root = ns.root();
3251
3252 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
3253 root.create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
3254 .expect("create_node failed");
3255 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3256 let reg_node = root
3257 .lookup_child(locked, ¤t_task, &mut context, "file".into())
3258 .expect("lookup_child failed");
3259
3260 reg_node
3261 .entry
3262 .node
3263 .fallocate(
3264 locked,
3265 ¤t_task,
3266 FallocMode::Allocate { keep_size: false },
3267 1,
3268 u64::MAX,
3269 )
3270 .expect_err("truncate unexpectedly passed");
3271 })
3272 .await;
3273 fixture.close().await;
3274 }
3275
3276 #[::fuchsia::test]
3277 async fn test_time_modify_persists() {
3278 let fixture = TestFixture::new().await;
3279 let (server, client) = zx::Channel::create();
3280 fixture.root().clone(server.into()).expect("clone failed");
3281
3282 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3283
3284 let last_modified = spawn_kernel_and_run(async move |locked, current_task| {
3285 let kernel = current_task.kernel();
3286 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3287 let fs = RemoteFs::new_fs(
3288 locked,
3289 &kernel,
3290 client,
3291 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3292 rights,
3293 )
3294 .expect("new_fs failed");
3295 let ns: Arc<Namespace> = Namespace::new(fs);
3296 current_task.fs().set_umask(FileMode::from_bits(0));
3297 let child = ns
3298 .root()
3299 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceId::NONE)
3300 .expect("create_node failed");
3301 let file = child
3303 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
3304 .expect("open failed");
3305 let time_before_write = child
3308 .entry
3309 .node
3310 .fetch_and_refresh_info(locked, ¤t_task)
3311 .expect("fetch_and_refresh_info failed")
3312 .time_modify;
3313 let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3314 let written = file
3315 .write(locked, ¤t_task, &mut VecInputBuffer::new(&write_bytes))
3316 .expect("write failed");
3317 assert_eq!(written, write_bytes.len());
3318 let last_modified = child
3319 .entry
3320 .node
3321 .fetch_and_refresh_info(locked, ¤t_task)
3322 .expect("fetch_and_refresh_info failed")
3323 .time_modify;
3324 assert!(last_modified > time_before_write);
3325 last_modified
3326 })
3327 .await;
3328
3329 let fixture = TestFixture::open(
3332 fixture.close().await,
3333 TestFixtureOptions { format: false, ..Default::default() },
3334 )
3335 .await;
3336 let (server, client) = zx::Channel::create();
3337 fixture.root().clone(server.into()).expect("clone failed");
3338 let refreshed_modified_time = spawn_kernel_and_run(async move |locked, current_task| {
3339 let kernel = current_task.kernel();
3340 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3341 let fs = RemoteFs::new_fs(
3342 locked,
3343 &kernel,
3344 client,
3345 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3346 rights,
3347 )
3348 .expect("new_fs failed");
3349 let ns = Namespace::new(fs);
3350 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3351 let child = ns
3352 .root()
3353 .lookup_child(locked, ¤t_task, &mut context, "file".into())
3354 .expect("lookup_child failed");
3355 let last_modified = child
3356 .entry
3357 .node
3358 .fetch_and_refresh_info(locked, ¤t_task)
3359 .expect("fetch_and_refresh_info failed")
3360 .time_modify;
3361 last_modified
3362 })
3363 .await;
3364 assert_eq!(last_modified, refreshed_modified_time);
3365
3366 fixture.close().await;
3367 }
3368
3369 #[::fuchsia::test]
3370 async fn test_update_atime_mtime() {
3371 let fixture = TestFixture::new().await;
3372 let (server, client) = zx::Channel::create();
3373 fixture.root().clone(server.into()).expect("clone failed");
3374
3375 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3376
3377 spawn_kernel_and_run(async move |locked, current_task| {
3378 let kernel = current_task.kernel();
3379 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3380 let fs = RemoteFs::new_fs(
3381 locked,
3382 &kernel,
3383 client,
3384 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3385 rights,
3386 )
3387 .expect("new_fs failed");
3388 let ns: Arc<Namespace> = Namespace::new(fs);
3389 current_task.fs().set_umask(FileMode::from_bits(0));
3390 let child = ns
3391 .root()
3392 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceId::NONE)
3393 .expect("create_node failed");
3394
3395 let info_original = child
3396 .entry
3397 .node
3398 .fetch_and_refresh_info(locked, ¤t_task)
3399 .expect("fetch_and_refresh_info failed")
3400 .clone();
3401
3402 child
3403 .entry
3404 .node
3405 .update_atime_mtime(
3406 locked,
3407 ¤t_task,
3408 &child.mount,
3409 TimeUpdateType::Time(UtcInstant::from_nanos(30)),
3410 TimeUpdateType::Omit,
3411 )
3412 .expect("update_atime_mtime failed");
3413 let info_after_update = child
3414 .entry
3415 .node
3416 .fetch_and_refresh_info(locked, ¤t_task)
3417 .expect("fetch_and_refresh_info failed")
3418 .clone();
3419 assert_eq!(info_after_update.time_modify, info_original.time_modify);
3420 assert_eq!(info_after_update.time_access, UtcInstant::from_nanos(30));
3421
3422 child
3423 .entry
3424 .node
3425 .update_atime_mtime(
3426 locked,
3427 ¤t_task,
3428 &child.mount,
3429 TimeUpdateType::Omit,
3430 TimeUpdateType::Time(UtcInstant::from_nanos(50)),
3431 )
3432 .expect("update_atime_mtime failed");
3433 let info_after_update2 = child
3434 .entry
3435 .node
3436 .fetch_and_refresh_info(locked, ¤t_task)
3437 .expect("fetch_and_refresh_info failed")
3438 .clone();
3439 assert_eq!(info_after_update2.time_modify, UtcInstant::from_nanos(50));
3440 assert_eq!(info_after_update2.time_access, UtcInstant::from_nanos(30));
3441 })
3442 .await;
3443 fixture.close().await;
3444 }
3445
3446 #[::fuchsia::test]
3447 async fn test_write_updates_mtime_ctime() {
3448 let fixture = TestFixture::new().await;
3449 let (server, client) = zx::Channel::create();
3450 fixture.root().clone(server.into()).expect("clone failed");
3451
3452 const MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o467);
3453
3454 spawn_kernel_and_run(async move |locked, current_task| {
3455 let kernel = current_task.kernel();
3456 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3457 let fs = RemoteFs::new_fs(
3458 locked,
3459 &kernel,
3460 client,
3461 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3462 rights,
3463 )
3464 .expect("new_fs failed");
3465 let ns: Arc<Namespace> = Namespace::new(fs);
3466 current_task.fs().set_umask(FileMode::from_bits(0));
3467 let child = ns
3468 .root()
3469 .create_node(locked, ¤t_task, "file".into(), MODE, DeviceId::NONE)
3470 .expect("create_node failed");
3471 let file = child
3472 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
3473 .expect("open failed");
3474 let (ctime_before_write, mtime_before_write) = {
3477 let info = child
3478 .entry
3479 .node
3480 .fetch_and_refresh_info(locked, ¤t_task)
3481 .expect("fetch_and_refresh_info failed");
3482 (info.time_status_change, info.time_modify)
3483 };
3484
3485 let write_bytes: [u8; 5] = [1, 2, 3, 4, 5];
3487 let written = file
3488 .write(locked, ¤t_task, &mut VecInputBuffer::new(&write_bytes))
3489 .expect("write failed");
3490 assert_eq!(written, write_bytes.len());
3491
3492 let (ctime_after_write_no_refresh, mtime_after_write_no_refresh) = {
3496 let info = child.entry.node.info();
3497 (info.time_status_change, info.time_modify)
3498 };
3499 assert_eq!(ctime_after_write_no_refresh, ctime_before_write);
3500 assert_eq!(mtime_after_write_no_refresh, mtime_before_write);
3501
3502 let (ctime_after_write_refresh, mtime_after_write_refresh) = {
3506 let info = child
3507 .entry
3508 .node
3509 .fetch_and_refresh_info(locked, ¤t_task)
3510 .expect("fetch_and_refresh_info failed");
3511 (info.time_status_change, info.time_modify)
3512 };
3513 assert_eq!(ctime_after_write_refresh, mtime_after_write_refresh);
3514 assert!(ctime_after_write_refresh > ctime_after_write_no_refresh);
3515 })
3516 .await;
3517 fixture.close().await;
3518 }
3519
3520 #[::fuchsia::test]
3521 async fn test_casefold_persists() {
3522 let fixture = TestFixture::new().await;
3523 let (server, client) = zx::Channel::create();
3524 fixture.root().clone(server.into()).expect("clone failed");
3525
3526 spawn_kernel_and_run(async move |locked, current_task| {
3527 let kernel = current_task.kernel();
3528 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3529 let fs = RemoteFs::new_fs(
3530 locked,
3531 &kernel,
3532 client,
3533 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3534 rights,
3535 )
3536 .expect("new_fs failed");
3537 let ns: Arc<Namespace> = Namespace::new(fs);
3538 let child = ns
3539 .root()
3540 .create_node(
3541 locked,
3542 ¤t_task,
3543 "dir".into(),
3544 FileMode::ALLOW_ALL.with_type(FileMode::IFDIR),
3545 DeviceId::NONE,
3546 )
3547 .expect("create_node failed");
3548 child
3549 .entry
3550 .node
3551 .update_attributes(locked, ¤t_task, |info| {
3552 info.casefold = true;
3553 Ok(())
3554 })
3555 .expect("enable casefold")
3556 })
3557 .await;
3558
3559 let fixture = TestFixture::open(
3561 fixture.close().await,
3562 TestFixtureOptions { format: false, ..Default::default() },
3563 )
3564 .await;
3565 let (server, client) = zx::Channel::create();
3566 fixture.root().clone(server.into()).expect("clone failed");
3567 let casefold = spawn_kernel_and_run(async move |locked, current_task| {
3568 let kernel = current_task.kernel();
3569 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
3570 let fs = RemoteFs::new_fs(
3571 locked,
3572 &kernel,
3573 client,
3574 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
3575 rights,
3576 )
3577 .expect("new_fs failed");
3578 let ns = Namespace::new(fs);
3579 let mut context = LookupContext::new(SymlinkMode::NoFollow);
3580 let child = ns
3581 .root()
3582 .lookup_child(locked, ¤t_task, &mut context, "dir".into())
3583 .expect("lookup_child failed");
3584 let casefold = child
3585 .entry
3586 .node
3587 .fetch_and_refresh_info(locked, ¤t_task)
3588 .expect("fetch_and_refresh_info failed")
3589 .casefold;
3590 casefold
3591 })
3592 .await;
3593 assert!(casefold);
3594
3595 fixture.close().await;
3596 }
3597
3598 #[::fuchsia::test]
3599 async fn test_pending_access_time() {
3600 const TEST_FILE: &str = "test_file";
3601
3602 let fixture = TestFixture::new().await;
3603 let (server, client) = zx::Channel::create();
3604 fixture.root().clone(server.into()).expect("clone failed");
3605 let (server, client2) = zx::Channel::create();
3606 fixture.root().clone(server.into()).expect("clone failed");
3607
3608 spawn_kernel_and_run(async move |locked, current_task| {
3609 let kernel = current_task.kernel.clone();
3610
3611 let atime3 = {
3612 let fs = RemoteFs::new_fs(
3613 locked,
3614 &kernel,
3615 client,
3616 FileSystemOptions {
3617 source: FlyByteStr::new(b"/"),
3618 flags: FileSystemFlags::empty().into(),
3619 ..Default::default()
3620 },
3621 fio::PERM_READABLE | fio::PERM_WRITABLE,
3622 )
3623 .expect("new_fs failed");
3624
3625 let ns = Namespace::new_with_flags(fs, MountpointFlags::RELATIME);
3626 let child = ns
3627 .root()
3628 .open_create_node(
3629 locked,
3630 ¤t_task,
3631 TEST_FILE.into(),
3632 FileMode::ALLOW_ALL.with_type(FileMode::IFREG),
3633 DeviceId::NONE,
3634 OpenFlags::empty(),
3635 )
3636 .expect("create_node failed");
3637
3638 let atime1 = child.entry.node.info().time_access;
3639
3640 std::thread::sleep(std::time::Duration::from_micros(1));
3641
3642 let file_handle = child
3643 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
3644 .expect("open failed");
3645
3646 file_handle
3647 .read(locked, ¤t_task, &mut VecOutputBuffer::new(10))
3648 .expect("read failed");
3649
3650 let atime2 = child.entry.node.info().time_access;
3652 assert!(atime2 > atime1);
3653
3654 std::thread::sleep(std::time::Duration::from_micros(1));
3655
3656 file_handle
3657 .read(locked, ¤t_task, &mut VecOutputBuffer::new(10))
3658 .expect("read failed");
3659
3660 let atime3 = child.entry.node.info().time_access;
3662 assert!(atime3 > atime2);
3663
3664 atime3
3665 };
3666
3667 kernel.delayed_releaser.apply(locked.cast_locked(), current_task);
3668
3669 let fs = RemoteFs::new_fs(
3671 locked,
3672 &kernel,
3673 client2,
3674 FileSystemOptions {
3675 source: FlyByteStr::new(b"/"),
3676 flags: FileSystemFlags::empty().into(),
3677 ..Default::default()
3678 },
3679 fio::PERM_READABLE | fio::PERM_WRITABLE,
3680 )
3681 .expect("new_fs failed");
3682
3683 let ns = Namespace::new_with_flags(fs, MountpointFlags::RELATIME);
3684 let child = ns
3685 .root()
3686 .lookup_child(
3687 locked,
3688 ¤t_task,
3689 &mut LookupContext::new(Default::default()),
3690 TEST_FILE.into(),
3691 )
3692 .expect("lookup_child failed");
3693
3694 let atime4 = child.entry.node.info().time_access;
3695
3696 assert!(atime4 >= atime3);
3697 })
3698 .await;
3699
3700 fixture.close().await;
3701 }
3702
3703 #[::fuchsia::test]
3704 async fn test_read_chunking() {
3705 use futures::StreamExt;
3706 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3707 let content = vec![0xAB; (fio::MAX_TRANSFER_SIZE + 100) as usize];
3708 let content_clone = content.clone();
3709
3710 let _server_task = fasync::Task::spawn(async move {
3711 while let Some(Ok(request)) = stream.next().await {
3712 match request {
3713 fio::FileRequest::ReadAt { count, offset, responder } => {
3714 let start = offset as usize;
3715 let end = std::cmp::min(start + count as usize, content_clone.len());
3716 let data = if start < content_clone.len() {
3717 &content_clone[start..end]
3718 } else {
3719 &[]
3720 };
3721 responder.send(Ok(data)).unwrap();
3722 }
3723 _ => panic!("Unexpected request: {:?}", request),
3724 }
3725 }
3726 });
3727
3728 fasync::unblock(move || {
3729 let io = RemoteIo::new(client.into_channel().into());
3730 let mut buffer = VecOutputBuffer::new(content.len());
3731 assert_eq!(
3732 io.read_to_output_buffer(0, &mut buffer).expect("read_at failed"),
3733 content.len()
3734 );
3735 assert_eq!(buffer.data(), content.as_slice());
3736 })
3737 .await;
3738 }
3739
3740 #[::fuchsia::test]
3741 async fn test_write_chunking() {
3742 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3743 let content = vec![0xCD; (fio::MAX_TRANSFER_SIZE + 100) as usize];
3744 let content2 = content.clone();
3745
3746 let server_task = fasync::Task::spawn(async move {
3747 let mut written = vec![0; content2.len()];
3748 while let Some(Ok(request)) = stream.next().await {
3749 match request {
3750 fio::FileRequest::WriteAt { offset, data, responder, .. } => {
3751 let offset = offset as usize;
3752 written[offset..offset + data.len()].copy_from_slice(&data);
3753 responder.send(Ok(data.len() as u64)).unwrap();
3754 }
3755 _ => panic!("Unexpected request: {:?}", request),
3756 }
3757 }
3758 assert_eq!(written, content2);
3759 });
3760
3761 fasync::unblock(move || {
3762 let io = RemoteIo::new(client.into_channel().into());
3763 let mut buffer = VecInputBuffer::new(&content);
3764 assert_eq!(
3765 io.write_from_input_buffer(0, &mut buffer).expect("write_at failed"),
3766 content.len()
3767 );
3768 })
3769 .await;
3770
3771 server_task.await;
3772 }
3773
3774 #[::fuchsia::test]
3775 async fn test_cached_attribute_refresh_behavior() {
3776 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3777 let barrier = Arc::new(Barrier::new(2));
3778 let barrier_clone = barrier.clone();
3779 let get_attrs_count = Arc::new(AtomicU32::new(0));
3780 let get_attrs_count_clone = get_attrs_count.clone();
3781
3782 let server_task = fasync::Task::spawn(async move {
3783 while let Some(Ok(request)) = stream.next().await {
3784 match request {
3785 fio::FileRequest::GetAttributes { query: _, responder } => {
3786 get_attrs_count_clone.fetch_add(1, Ordering::SeqCst);
3787 let mutable_attrs = fio::MutableNodeAttributes { ..Default::default() };
3788 let immutable_attrs = fio::ImmutableNodeAttributes {
3789 id: Some(1),
3790 link_count: Some(1),
3791 ..Default::default()
3792 };
3793 responder.send(Ok((&mutable_attrs, &immutable_attrs))).unwrap();
3794 }
3795 fio::FileRequest::Resize { length: _, responder } => {
3796 let barrier_clone = barrier_clone.clone();
3797 fasync::Task::spawn(async move {
3798 barrier_clone.async_wait().await;
3799 barrier_clone.async_wait().await;
3800 responder.send(Ok(())).unwrap();
3801 })
3802 .detach();
3803 }
3804 fio::FileRequest::Close { responder } => {
3805 responder.send(Ok(())).unwrap();
3806 }
3807 _ => panic!("Unexpected request: {:?}", request),
3808 }
3809 }
3810 });
3811
3812 fasync::unblock(move || {
3813 let io = RemoteIo::new(client.into_channel().into());
3814 let node = BaseNode::new(io, false);
3815 let info =
3816 DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
3817
3818 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 0);
3820 {
3821 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3822 }
3823 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 0);
3824
3825 std::thread::scope(|s| {
3827 s.spawn(|| {
3828 will_dirty(&[&node], || {
3829 node.io.truncate(0).expect("truncate failed");
3830 });
3831 });
3832
3833 barrier.wait();
3835
3836 {
3838 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3839 }
3840 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
3841
3842 {
3844 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3845 }
3846 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
3847
3848 barrier.wait();
3850 });
3851
3852 {
3854 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3855 }
3856 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3857
3858 {
3860 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3861 }
3862 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3863 })
3864 .await;
3865
3866 server_task.await;
3867 }
3868
3869 #[::fuchsia::test]
3870 async fn test_attribute_refresh_during_concurrent_dirty_operation() {
3871 let (client, mut stream) = create_request_stream::<fio::FileMarker>();
3872 let get_attrs_started = Arc::new(Barrier::new(2));
3873 let get_attrs_started_clone = get_attrs_started.clone();
3874 let finish_get_attrs = Arc::new(Barrier::new(2));
3875 let finish_get_attrs_clone = finish_get_attrs.clone();
3876
3877 let resize_started = Arc::new(Barrier::new(2));
3878 let resize_started_clone = resize_started.clone();
3879 let finish_resize = Arc::new(Barrier::new(2));
3880 let finish_resize_clone = finish_resize.clone();
3881
3882 let get_attrs_count = Arc::new(AtomicU32::new(0));
3883 let get_attrs_count_clone = get_attrs_count.clone();
3884
3885 let server_task = fasync::Task::spawn(async move {
3886 while let Some(Ok(request)) = stream.next().await {
3887 match request {
3888 fio::FileRequest::GetAttributes { query: _, responder } => {
3889 let count = get_attrs_count_clone.fetch_add(1, Ordering::SeqCst);
3890 let finish_get_attrs_clone = finish_get_attrs_clone.clone();
3891 let get_attrs_started_clone = get_attrs_started_clone.clone();
3892
3893 fasync::Task::spawn(async move {
3894 if count == 0 {
3895 fasync::unblock(move || {
3896 get_attrs_started_clone.wait();
3897 finish_get_attrs_clone.wait();
3898 })
3899 .await;
3900 }
3901 let mutable_attrs = fio::MutableNodeAttributes { ..Default::default() };
3902 let immutable_attrs = fio::ImmutableNodeAttributes {
3903 id: Some(1),
3904 link_count: Some(1),
3905 ..Default::default()
3906 };
3907 responder.send(Ok((&mutable_attrs, &immutable_attrs))).unwrap();
3908 })
3909 .detach();
3910 }
3911 fio::FileRequest::Resize { length: _, responder } => {
3912 let resize_started_clone = resize_started_clone.clone();
3913 let finish_resize_clone = finish_resize_clone.clone();
3914 fasync::Task::spawn(async move {
3915 fasync::unblock(move || {
3916 resize_started_clone.wait();
3917 finish_resize_clone.wait();
3918 })
3919 .await;
3920 responder.send(Ok(())).unwrap();
3921 })
3922 .detach();
3923 }
3924 fio::FileRequest::Close { responder } => {
3925 responder.send(Ok(())).unwrap();
3926 }
3927 _ => panic!("Unexpected request: {:?}", request),
3928 }
3929 }
3930 });
3931
3932 fasync::unblock(move || {
3933 let io = RemoteIo::new(client.into_channel().into());
3934 let node = BaseNode::new(io, true);
3935 let info =
3936 DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
3937
3938 std::thread::scope(|s| {
3939 let refresh_thread = s.spawn(|| {
3941 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3942 });
3943
3944 get_attrs_started.wait();
3945
3946 let dirty_thread = s.spawn(|| {
3948 will_dirty(&[&node], || {
3949 node.io.truncate(0).expect("truncate failed");
3950 });
3951 });
3952
3953 resize_started.wait();
3954
3955 finish_get_attrs.wait();
3957 refresh_thread.join().unwrap();
3958 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
3959
3960 {
3962 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3963 }
3964 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
3965
3966 finish_resize.wait();
3968 dirty_thread.join().unwrap();
3969
3970 {
3972 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3973 }
3974 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3975
3976 {
3978 let _info = node.fetch_and_refresh_info(&info).expect("fetch failed");
3979 }
3980 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 3);
3981 });
3982 })
3983 .await;
3984
3985 server_task.await;
3986 }
3987
3988 #[::fuchsia::test]
3989 async fn test_update_attributes_invalidates_cache() {
3990 let (client, mut stream) = create_request_stream::<fio::DirectoryMarker>();
3991 let get_attrs_count = Arc::new(AtomicU32::new(0));
3992 let get_attrs_count_clone = get_attrs_count.clone();
3993
3994 let server_task = fasync::Task::spawn(async move {
3995 let mut sub_tasks = Vec::new();
3996 while let Some(Ok(request)) = stream.next().await {
3997 match request {
3998 fio::DirectoryRequest::Open { path, object, flags, .. } => {
3999 assert_eq!(path, ".", "Unexpected open() for non-self");
4000 let get_attrs_count = get_attrs_count_clone.clone();
4001 sub_tasks.push(fasync::Task::spawn(async move {
4002 let (mut stream, control_handle) =
4003 ServerEnd::<fio::DirectoryMarker>::new(object)
4004 .into_stream_and_control_handle();
4005 assert!(flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION));
4006
4007 let mutable_attributes =
4009 fio::MutableNodeAttributes { ..Default::default() };
4010 let immutable_attributes = fio::ImmutableNodeAttributes {
4011 id: Some(1),
4012 link_count: Some(1),
4013 ..Default::default()
4014 };
4015 let info = fio::DirectoryInfo {
4016 attributes: Some(fio::NodeAttributes2 {
4017 mutable_attributes,
4018 immutable_attributes,
4019 }),
4020 ..Default::default()
4021 };
4022 let _ = control_handle
4023 .send_on_representation(fio::Representation::Directory(info));
4024
4025 while let Some(Ok(request)) = stream.next().await {
4026 match request {
4027 fio::DirectoryRequest::GetAttributes {
4028 query: _,
4029 responder,
4030 } => {
4031 get_attrs_count.fetch_add(1, Ordering::SeqCst);
4032 let mutable_attrs =
4033 fio::MutableNodeAttributes { ..Default::default() };
4034 let immutable_attrs = fio::ImmutableNodeAttributes {
4035 id: Some(1),
4036 link_count: Some(1),
4037 ..Default::default()
4038 };
4039 responder
4040 .send(Ok((&mutable_attrs, &immutable_attrs)))
4041 .unwrap();
4042 }
4043 fio::DirectoryRequest::UpdateAttributes {
4044 payload: _,
4045 responder,
4046 } => {
4047 responder.send(Ok(())).unwrap();
4048 }
4049 fio::DirectoryRequest::Close { responder } => {
4050 responder.send(Ok(())).unwrap();
4051 }
4052 _ => {
4053 panic!("Unexpected request: {:?}", request)
4054 }
4055 }
4056 }
4057 }));
4058 }
4059 fio::DirectoryRequest::Close { responder } => {
4060 responder.send(Ok(())).unwrap();
4061 }
4062 fio::DirectoryRequest::QueryFilesystem { responder } => {
4063 responder.send(0i32, None).unwrap();
4064 }
4065 _ => panic!("Unexpected request: {:?}", request),
4066 }
4067 }
4068
4069 for sub_task in sub_tasks {
4070 let _ = sub_task.await;
4071 }
4072 });
4073
4074 spawn_kernel_and_run(async move |locked, current_task| {
4075 let fs = RemoteFs::new_fs(
4076 locked,
4077 ¤t_task.kernel(),
4078 client.into_channel(),
4079 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4080 fio::PERM_READABLE | fio::PERM_WRITABLE,
4081 )
4082 .expect("failed to mount test remote FS");
4083
4084 {
4086 let _info = fs
4087 .root()
4088 .node
4089 .fetch_and_refresh_info(locked, current_task)
4090 .expect("fetch failed");
4091 }
4092 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
4093
4094 {
4096 let _info = fs
4097 .root()
4098 .node
4099 .fetch_and_refresh_info(locked, current_task)
4100 .expect("fetch failed");
4101 }
4102 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 1);
4103
4104 fs.root()
4106 .node
4107 .update_attributes(locked, current_task, |attrs| {
4108 attrs.time_modify += UtcDuration::from_seconds(1);
4109 Ok(())
4110 })
4111 .expect("update_attributes failed");
4112
4113 {
4115 let _info = fs
4116 .root()
4117 .node
4118 .fetch_and_refresh_info(locked, current_task)
4119 .expect("fetch failed");
4120 }
4121 assert_eq!(get_attrs_count.load(Ordering::SeqCst), 2);
4122 })
4123 .await;
4124
4125 server_task.await;
4126 }
4127
4128 trait AsyncBarrier {
4129 async fn async_wait(&self);
4130 }
4131
4132 impl AsyncBarrier for Arc<Barrier> {
4133 async fn async_wait(&self) {
4134 let this = self.clone();
4135 fasync::unblock(move || this.wait()).await;
4136 }
4137 }
4138
4139 #[derive(Default)]
4140 struct MockRemoteFs {
4141 get_attrs_count: AtomicU32,
4142 file_size: AtomicUsize,
4143 write_offsets: Mutex<Vec<u64>>,
4144 data: Mutex<Vec<u8>>,
4145 get_attrs_hook: Mutex<Option<futures::future::BoxFuture<'static, ()>>>,
4146 write_hook: Mutex<Option<futures::future::BoxFuture<'static, ()>>>,
4147 }
4148
4149 impl MockRemoteFs {
4150 async fn handle_file_requests(
4151 self: Arc<Self>,
4152 mut stream: fio::FileRequestStream,
4153 control_handle: fio::FileControlHandle,
4154 ) {
4155 let size = self.file_size.load(Ordering::SeqCst) as u64;
4156 let info = fio::FileInfo {
4157 attributes: Some(fio::NodeAttributes2 {
4158 mutable_attributes: fio::MutableNodeAttributes { ..Default::default() },
4159 immutable_attributes: fio::ImmutableNodeAttributes {
4160 id: Some(2),
4161 link_count: Some(1),
4162 content_size: Some(size),
4163 storage_size: Some(size),
4164 ..Default::default()
4165 },
4166 }),
4167 ..Default::default()
4168 };
4169 let _ = control_handle.send_on_representation(fio::Representation::File(info));
4170 while let Some(Ok(request)) = stream.next().await {
4171 match request {
4172 fio::FileRequest::GetAttributes { responder, .. } => {
4173 let this = self.clone();
4175 fasync::Task::spawn(async move {
4176 this.get_attrs_count.fetch_add(1, Ordering::SeqCst);
4177 let size = this.file_size.load(Ordering::SeqCst) as u64;
4178 let hook = this.get_attrs_hook.lock().take();
4179 if let Some(hook) = hook {
4180 hook.await;
4181 }
4182 responder
4183 .send(Ok((
4184 &fio::MutableNodeAttributes { ..Default::default() },
4185 &fio::ImmutableNodeAttributes {
4186 id: Some(2),
4187 link_count: Some(1),
4188 content_size: Some(size),
4189 storage_size: Some(size),
4190 ..Default::default()
4191 },
4192 )))
4193 .unwrap();
4194 })
4195 .detach();
4196 }
4197 fio::FileRequest::ReadAt { count, offset, responder } => {
4198 let data = self.data.lock();
4199 let start = std::cmp::min(offset as usize, data.len());
4200 let end = std::cmp::min(start + count as usize, data.len());
4201 responder.send(Ok(&data[start..end])).unwrap();
4202 }
4203 fio::FileRequest::WriteAt { offset, data, responder, .. } => {
4204 let self_clone = Arc::clone(&self);
4206 fasync::Task::spawn(async move {
4207 self_clone.write_offsets.lock().push(offset);
4208 let end = offset as usize + data.len();
4209 {
4210 let mut mock_data = self_clone.data.lock();
4211 if end > mock_data.len() {
4212 mock_data.resize(end, 0);
4213 }
4214 mock_data[offset as usize..end].copy_from_slice(&data);
4215 }
4216 let mut current_size = self_clone.file_size.load(Ordering::SeqCst);
4217 while end > current_size {
4218 match self_clone.file_size.compare_exchange_weak(
4219 current_size,
4220 end,
4221 Ordering::SeqCst,
4222 Ordering::SeqCst,
4223 ) {
4224 Ok(_) => break,
4225 Err(actual) => current_size = actual,
4226 }
4227 }
4228 let hook = self_clone.write_hook.lock().take();
4229 if let Some(hook) = hook {
4230 hook.await;
4231 }
4232 responder.send(Ok(data.len() as u64)).unwrap();
4233 })
4234 .detach();
4235 }
4236 fio::FileRequest::Resize { length, responder, .. } => {
4237 self.file_size.store(length as usize, Ordering::SeqCst);
4238 responder.send(Ok(())).unwrap();
4239 }
4240 fio::FileRequest::Seek { origin, offset, responder } => {
4241 let new_offset = match origin {
4242 fio::SeekOrigin::Start => offset as u64,
4243 fio::SeekOrigin::Current => 0,
4244 fio::SeekOrigin::End => {
4245 (self.file_size.load(Ordering::SeqCst) as i64 + offset) as u64
4246 }
4247 };
4248 responder.send(Ok(new_offset)).unwrap();
4249 }
4250 fio::FileRequest::Close { responder } => {
4251 responder.send(Ok(())).unwrap();
4252 }
4253 _ => {}
4254 }
4255 }
4256 }
4257
4258 async fn handle_directory_requests(
4259 self: Arc<Self>,
4260 mut stream: fio::DirectoryRequestStream,
4261 control_handle: fio::DirectoryControlHandle,
4262 ) {
4263 let info = fio::DirectoryInfo {
4264 attributes: Some(fio::NodeAttributes2 {
4265 mutable_attributes: fio::MutableNodeAttributes { ..Default::default() },
4266 immutable_attributes: fio::ImmutableNodeAttributes {
4267 id: Some(1),
4268 link_count: Some(1),
4269 ..Default::default()
4270 },
4271 }),
4272 ..Default::default()
4273 };
4274 let _ = control_handle.send_on_representation(fio::Representation::Directory(info));
4275 let mut file_tasks = Vec::new();
4276 while let Some(Ok(request)) = stream.next().await {
4277 match request {
4278 fio::DirectoryRequest::Open { path, object, .. } => {
4279 if path == "file" {
4280 let self_clone = Arc::clone(&self);
4281 file_tasks.push(fasync::Task::spawn(async move {
4282 let (stream, control_handle) =
4283 ServerEnd::<fio::FileMarker>::new(object)
4284 .into_stream_and_control_handle();
4285 self_clone.handle_file_requests(stream, control_handle).await;
4286 }));
4287 }
4288 }
4289 fio::DirectoryRequest::Close { responder } => {
4290 responder.send(Ok(())).unwrap();
4291 }
4292 _ => {}
4293 }
4294 }
4295 for task in file_tasks {
4296 let _ = task.await;
4297 }
4298 }
4299
4300 async fn run(self: Arc<Self>, mut stream: fio::DirectoryRequestStream) {
4301 let mut sub_tasks = Vec::new();
4302 while let Some(Ok(request)) = stream.next().await {
4303 match request {
4304 fio::DirectoryRequest::Open { path, object, .. } => {
4305 if path == "." {
4306 let self_clone = Arc::clone(&self);
4307 sub_tasks.push(fasync::Task::spawn(async move {
4308 let (stream, control_handle) =
4309 ServerEnd::<fio::DirectoryMarker>::new(object)
4310 .into_stream_and_control_handle();
4311 self_clone.handle_directory_requests(stream, control_handle).await;
4312 }));
4313 }
4314 }
4315 fio::DirectoryRequest::Close { responder } => {
4316 responder.send(Ok(())).unwrap();
4317 }
4318 fio::DirectoryRequest::QueryFilesystem { responder } => {
4319 responder.send(0i32, None).unwrap();
4320 }
4321 _ => {}
4322 }
4323 }
4324 for sub_task in sub_tasks {
4325 let _ = sub_task.await;
4326 }
4327 }
4328 }
4329
4330 #[::fuchsia::test]
4331 async fn test_get_size_uses_cache_unless_truncated() {
4332 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4333 let state = Arc::new(MockRemoteFs::default());
4334
4335 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4336
4337 spawn_kernel_and_run(async move |locked, current_task| {
4338 let fs = RemoteFs::new_fs(
4339 locked,
4340 ¤t_task.kernel(),
4341 client.into_channel(),
4342 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4343 fio::PERM_READABLE | fio::PERM_WRITABLE,
4344 )
4345 .expect("failed to mount test remote FS");
4346
4347 let ns = Namespace::new(fs);
4348 let root = ns.root();
4349
4350 let mut context = LookupContext::default();
4351 let file_node = root
4352 .lookup_child(locked, current_task, &mut context, "file".into())
4353 .expect("lookup failed");
4354
4355 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4357 {
4358 let _size =
4359 file_node.entry.node.get_size(locked, current_task).expect("get_size failed");
4360 }
4361 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4362
4363 let file_handle = file_node
4365 .open(
4366 locked,
4367 current_task,
4368 OpenFlags::RDWR | OpenFlags::APPEND,
4369 AccessCheck::default(),
4370 )
4371 .expect("open failed");
4372
4373 {
4374 let mut data = VecInputBuffer::new(b"foo");
4375 let written =
4376 file_handle.write(locked, current_task, &mut data).expect("write failed");
4377 assert_eq!(written, 3);
4378 }
4379 assert_eq!(
4380 file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4381 3
4382 );
4383 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 0);
4384 assert_eq!(*state.write_offsets.lock(), vec![0]);
4385
4386 file_node
4388 .entry
4389 .node
4390 .truncate(locked, current_task, &file_node.mount, 0)
4391 .expect("truncate failed");
4392 assert_eq!(state.file_size.load(Ordering::SeqCst), 0);
4393
4394 {
4396 let size =
4397 file_node.entry.node.get_size(locked, current_task).expect("get_size failed");
4398 assert_eq!(size, 0);
4399 }
4400 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 1);
4401
4402 {
4404 let mut data = VecInputBuffer::new(b"bar");
4405 let written =
4406 file_handle.write(locked, current_task, &mut data).expect("write failed");
4407 assert_eq!(written, 3);
4408 }
4409 assert_eq!(
4410 file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4411 3
4412 );
4413 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 1);
4416 assert_eq!(*state.write_offsets.lock(), vec![0, 0]);
4417
4418 file_node
4420 .entry
4421 .node
4422 .truncate(locked, current_task, &file_node.mount, 10)
4423 .expect("truncate failed");
4424 {
4425 let mut data = VecInputBuffer::new(b"baz");
4426 let written =
4427 file_handle.write(locked, current_task, &mut data).expect("write failed");
4428 assert_eq!(written, 3);
4429 }
4430 assert_eq!(state.get_attrs_count.load(Ordering::SeqCst), 2);
4432 assert_eq!(
4433 file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4434 13
4435 );
4436 assert_eq!(*state.write_offsets.lock(), vec![0, 0, 10]);
4437 })
4438 .await;
4439
4440 server_task.await;
4441 }
4442
4443 #[::fuchsia::test]
4444 async fn test_get_size_during_refresh_after_truncate() {
4445 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4446 let state = Arc::new(MockRemoteFs::default());
4447
4448 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4449
4450 spawn_kernel_and_run(async move |locked, current_task| {
4451 let fs = RemoteFs::new_fs(
4452 locked,
4453 ¤t_task.kernel(),
4454 client.into_channel(),
4455 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4456 fio::PERM_READABLE | fio::PERM_WRITABLE,
4457 )
4458 .expect("failed to mount test remote FS");
4459
4460 let ns = Namespace::new(fs);
4461 let root = ns.root();
4462
4463 let mut context = LookupContext::default();
4464 let file_node = root
4465 .lookup_child(locked, current_task, &mut context, "file".into())
4466 .expect("lookup failed");
4467
4468 assert_eq!(
4470 file_node.entry.node.get_size(locked, current_task).expect("get_size failed"),
4471 0
4472 );
4473
4474 file_node
4476 .entry
4477 .node
4478 .truncate(locked, current_task, &file_node.mount, 10)
4479 .expect("truncate failed");
4480
4481 let barrier = Arc::new(Barrier::new(2));
4483 {
4484 let barrier = barrier.clone();
4485 *state.get_attrs_hook.lock() = Some(Box::pin(async move {
4486 barrier.async_wait().await;
4487 barrier.async_wait().await;
4488 }));
4489 }
4490
4491 let file_node_clone = file_node.clone();
4493 let (result1, request) = SpawnRequestBuilder::new()
4494 .with_sync_closure(move |locked, current_task| {
4495 let size = file_node_clone
4496 .entry
4497 .node
4498 .get_size(locked, current_task)
4499 .expect("get_size failed");
4500 assert_eq!(size, 10);
4501 })
4502 .build_with_async_result();
4503 current_task.kernel().kthreads.spawner().spawn_from_request(request);
4504
4505 barrier.async_wait().await;
4507
4508 *state.get_attrs_hook.lock() =
4510 Some(Box::pin(async move { barrier.async_wait().await }));
4511
4512 let (result2, request) = SpawnRequestBuilder::new()
4514 .with_sync_closure(move |locked, current_task| {
4515 let size = file_node
4516 .entry
4517 .node
4518 .get_size(locked, current_task)
4519 .expect("get_size failed");
4520 assert_eq!(size, 10);
4521 })
4522 .build_with_async_result();
4523 current_task.kernel().kthreads.spawner().spawn_from_request(request);
4524
4525 result1.await.unwrap();
4526 result2.await.unwrap();
4527 })
4528 .await;
4529
4530 server_task.await;
4531 }
4532
4533 #[::fuchsia::test]
4534 async fn test_get_size_during_outstanding_write() {
4535 let (client, stream) = create_request_stream::<fio::DirectoryMarker>();
4536 let state = Arc::new(MockRemoteFs::default());
4537
4538 let server_task = fasync::Task::spawn(Arc::clone(&state).run(stream));
4539
4540 spawn_kernel_and_run(async move |locked, current_task| {
4541 let fs = RemoteFs::new_fs(
4542 locked,
4543 ¤t_task.kernel(),
4544 client.into_channel(),
4545 FileSystemOptions { source: FlyByteStr::new(b"."), ..Default::default() },
4546 fio::PERM_READABLE | fio::PERM_WRITABLE,
4547 )
4548 .expect("failed to mount test remote FS");
4549
4550 let ns = Namespace::new(fs);
4551 let root = ns.root();
4552
4553 let mut context = LookupContext::default();
4554 let file_node = root
4555 .lookup_child(locked, current_task, &mut context, "file".into())
4556 .expect("lookup failed");
4557
4558 let file_handle = file_node
4560 .open(locked, current_task, OpenFlags::RDWR, AccessCheck::default())
4561 .expect("open failed");
4562
4563 let barrier = Arc::new(Barrier::new(2));
4565 {
4566 let barrier = barrier.clone();
4567 *state.write_hook.lock() = Some(Box::pin(async move {
4568 barrier.async_wait().await;
4569 barrier.async_wait().await;
4570 }));
4571 }
4572
4573 let file_handle_clone = file_handle.clone();
4575 current_task.kernel().kthreads.spawner().spawn_from_request(
4576 SpawnRequestBuilder::new()
4577 .with_sync_closure(move |locked, current_task| {
4578 let mut data = VecInputBuffer::new(b"hello");
4579 file_handle_clone
4580 .write(locked, current_task, &mut data)
4581 .expect("write failed");
4582 })
4583 .build(),
4584 );
4585
4586 barrier.async_wait().await;
4588
4589 {
4591 let mut data = VecOutputBuffer::new(5);
4592 let read =
4593 file_handle.read_at(locked, current_task, 0, &mut data).expect("read failed");
4594 assert_eq!(read, 5);
4595 assert_eq!(data.data(), b"hello");
4596 }
4597
4598 let size =
4600 file_node.entry.node.get_size(locked, current_task).expect("get_size failed");
4601 assert_eq!(
4602 size, 5,
4603 "get_size should return the updated size even if a write is outstanding"
4604 );
4605
4606 barrier.async_wait().await;
4608 })
4609 .await;
4610
4611 server_task.await;
4612 }
4613
4614 #[test]
4615 fn test_info_state_initial_state() {
4616 let state = InfoState::new(true); assert_eq!(state.0.load(Ordering::Relaxed), 0);
4618
4619 let state = InfoState::new(false); assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4621 }
4622
4623 #[test]
4624 fn test_info_state_dirty_op_guard() {
4625 let state = InfoState::new(false);
4626 {
4627 let _guard = state.dirty_op_guard(false);
4628 assert_eq!(state.0.load(Ordering::Relaxed), 1); assert!(!state.is_size_accurate());
4630 }
4631 assert_eq!(state.0.load(Ordering::Relaxed), 0);
4632 assert!(state.is_size_accurate());
4633
4634 {
4635 let _guard = state.dirty_op_guard(true);
4636 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED | 1);
4637 assert!(!state.is_size_accurate());
4638 }
4639 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4640 assert!(!state.is_size_accurate());
4641
4642 {
4643 let _guard1 = state.dirty_op_guard(true);
4644 let _guard2 = state.dirty_op_guard(true);
4645 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED | 2);
4646 assert!(!state.is_size_accurate());
4647 }
4648 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4649 assert!(!state.is_size_accurate());
4650 }
4651
4652 #[test]
4653 fn test_info_state_refresh_clears_truncated() {
4654 let state = InfoState::new(true);
4655 {
4657 let _guard = state.dirty_op_guard(true);
4658 }
4659 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::TRUNCATED);
4660
4661 let info =
4662 DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4663 state.maybe_refresh(&info, |_| Ok(()), |_| unreachable!()).unwrap();
4664
4665 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4666 assert!(state.is_size_accurate());
4667 }
4668
4669 #[test]
4670 fn test_info_state_maybe_refresh_success() {
4671 let state = InfoState::new(true);
4672 let info =
4673 DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4674
4675 let res = state.maybe_refresh(&info, |_| Ok(42), |_| unreachable!());
4676 assert_eq!(res.unwrap(), 42);
4677 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::IN_SYNC);
4678 }
4679
4680 #[test]
4681 fn test_info_state_maybe_refresh_error() {
4682 let state = InfoState::new(true);
4683 let info =
4684 DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4685
4686 let res: Result<u32, Errno> =
4687 state.maybe_refresh(&info, |_| error!(EIO), |_| unreachable!());
4688 assert!(res.is_err());
4689 assert_eq!(state.0.load(Ordering::Relaxed), 0); }
4691
4692 #[test]
4693 fn test_info_state_maybe_refresh_not_needed() {
4694 let state = InfoState::new(false); let info =
4696 DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4697 let res = state.maybe_refresh(&info, |_| unreachable!(), |_| Ok(123));
4698 assert_eq!(res.unwrap(), 123);
4699 }
4700
4701 #[test]
4702 fn test_info_state_concurrent_dirty_op_during_refresh() {
4703 let state = InfoState::new(true);
4704 let info =
4705 DynamicLockDepRwLock::new::<starnix_sync::FsNodeInfoLevel>(FsNodeInfo::default());
4706
4707 state
4708 .maybe_refresh(
4709 &info,
4710 |_| {
4711 let _guard = state.dirty_op_guard(false);
4713 assert_eq!(state.0.load(Ordering::Relaxed), InfoState::PENDING_REFRESH | 1);
4714 Ok(())
4715 },
4716 |_| unreachable!(),
4717 )
4718 .unwrap();
4719
4720 assert_eq!(state.0.load(Ordering::Relaxed), 0);
4721 }
4722
4723 #[::fuchsia::test]
4724 async fn test_sync() {
4725 let fixture = TestFixture::new().await;
4726 let (server, client) = zx::Channel::create();
4727 fixture.root().clone(server.into()).expect("clone failed");
4728
4729 spawn_kernel_and_run(async move |locked, current_task| {
4730 let kernel = current_task.kernel();
4731 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4732 let fs = RemoteFs::new_fs(
4733 locked,
4734 &kernel,
4735 client,
4736 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
4737 rights,
4738 )
4739 .expect("new_fs failed");
4740 let ns = Namespace::new(fs);
4741 current_task.fs().set_umask(FileMode::from_bits(0));
4742 let root = ns.root();
4743
4744 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits());
4745 root.create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
4746 .expect("create_node failed");
4747 let mut context = LookupContext::new(SymlinkMode::NoFollow);
4748 let reg_node = root
4749 .lookup_child(locked, ¤t_task, &mut context, "file".into())
4750 .expect("lookup_child failed");
4751
4752 reg_node
4754 .entry
4755 .node
4756 .ops()
4757 .sync(®_node.entry.node, ¤t_task)
4758 .expect("sync failed");
4759 })
4760 .await;
4761 fixture.close().await;
4762 }
4763
4764 #[::fuchsia::test]
4765 async fn test_msync_propagates_to_fxfs() {
4766 use crate::mm::MemoryAccessor;
4767 use crate::mm::syscalls::{sys_mmap, sys_msync};
4768 use crate::vfs::FdFlags;
4769 use starnix_uapi::user_address::UserAddress;
4770 use starnix_uapi::{MAP_SHARED, MS_SYNC, PROT_READ, PROT_WRITE};
4771
4772 let commit_count = Arc::new(AtomicUsize::new(0));
4774 let commit_count_clone = commit_count.clone();
4775
4776 let fixture = TestFixture::open(
4778 DeviceHolder::new(FakeDevice::new(1024 * 1024, 512)),
4779 TestFixtureOptions {
4780 format: true,
4781 as_blob: false,
4782 encrypted: true,
4783 pre_commit_hook: Some(Box::new(move |_transaction| {
4784 commit_count_clone.fetch_add(1, Ordering::SeqCst);
4785 Ok(())
4786 })),
4787 },
4788 )
4789 .await;
4790
4791 let (server, client) = zx::Channel::create();
4792 fixture.root().clone(server.into()).expect("clone channel");
4793
4794 spawn_kernel_and_run(async move |locked, current_task| {
4795 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4797 let fs = RemoteFs::new_fs(
4798 locked,
4799 current_task.kernel(),
4800 client,
4801 FileSystemOptions { source: FlyByteStr::new(b"/test"), ..Default::default() },
4802 rights,
4803 )
4804 .expect("new_fs");
4805 let ns = Namespace::new(fs);
4806 let root = ns.root();
4807
4808 let node = root
4810 .create_node(
4811 locked,
4812 ¤t_task,
4813 "test_file".into(),
4814 mode!(IFREG, 0o666),
4815 DeviceId::NONE,
4816 )
4817 .expect("create_node");
4818 let file_handle = node
4819 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
4820 .expect("open");
4821 let fd =
4822 current_task.add_file(locked, file_handle, FdFlags::empty()).expect("add file");
4823
4824 let len = *PAGE_SIZE as usize * 4;
4826 let mmap_addr = sys_mmap(
4827 locked,
4828 current_task,
4829 UserAddress::default(),
4830 len,
4831 PROT_READ | PROT_WRITE,
4832 MAP_SHARED,
4833 fd,
4834 0,
4835 )
4836 .expect("mmap");
4837
4838 for i in 0..4 {
4840 let data = [0xAAu8; 1];
4841 current_task
4842 .write_memory((mmap_addr + (i * *PAGE_SIZE as usize)).unwrap(), &data)
4843 .expect("write memory");
4844 }
4845
4846 let commits_before_msync = commit_count.load(Ordering::SeqCst);
4848
4849 sys_msync(locked, current_task, mmap_addr, len, MS_SYNC).expect("msync");
4851
4852 let final_commits = commit_count.load(Ordering::SeqCst);
4854 assert!(
4855 final_commits > commits_before_msync,
4856 "msync should trigger Fxfs transaction. commits: {} -> {}",
4857 commits_before_msync,
4858 final_commits
4859 );
4860 })
4861 .await;
4862
4863 fixture.close().await;
4864 }
4865
4866 #[::fuchsia::test]
4867 async fn test_get_size() {
4868 let fixture = TestFixture::new().await;
4869 let (server, client) = zx::Channel::create();
4870 fixture.root().clone(server.into()).expect("clone failed");
4871
4872 spawn_kernel_and_run(async move |locked, current_task| {
4873 let kernel = current_task.kernel();
4874 let rights = fio::PERM_READABLE | fio::PERM_WRITABLE;
4875 let fs = RemoteFs::new_fs(
4876 locked,
4877 &kernel,
4878 client,
4879 FileSystemOptions { source: FlyByteStr::new(b"/"), ..Default::default() },
4880 rights,
4881 )
4882 .expect("new_fs failed");
4883 let ns = Namespace::new(fs);
4884 let root = ns.root();
4885
4886 const REG_MODE: FileMode = FileMode::from_bits(FileMode::IFREG.bits() | 0o666);
4887 let node = root
4888 .create_node(locked, ¤t_task, "file".into(), REG_MODE, DeviceId::NONE)
4889 .expect("create_node failed");
4890 let file = node
4891 .open(locked, ¤t_task, OpenFlags::RDWR, AccessCheck::default())
4892 .expect("open failed");
4893
4894 assert_eq!(
4896 node.entry.node.get_size(locked, ¤t_task).expect("get_size failed"),
4897 0
4898 );
4899
4900 let mut data = VecInputBuffer::new(b"hello");
4902 file.write(locked, ¤t_task, &mut data).expect("write failed");
4903
4904 assert_eq!(
4906 node.entry.node.get_size(locked, ¤t_task).expect("get_size failed"),
4907 5
4908 );
4909
4910 node.truncate(locked, ¤t_task, 10).expect("truncate failed");
4912
4913 assert_eq!(
4915 node.entry.node.get_size(locked, ¤t_task).expect("get_size failed"),
4916 10
4917 );
4918
4919 node.truncate(locked, ¤t_task, 3).expect("truncate failed");
4921
4922 assert_eq!(
4924 node.entry.node.get_size(locked, ¤t_task).expect("get_size failed"),
4925 3
4926 );
4927 })
4928 .await;
4929 fixture.close().await;
4930 }
4931}