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