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