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