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