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