1use crate::common::{
6 decode_extended_attribute_value, encode_extended_attribute_value, extended_attributes_sender,
7 inherit_rights_for_clone, io1_to_io2_attrs,
8};
9use crate::execution_scope::ExecutionScope;
10use crate::file::common::new_connection_validate_options;
11use crate::file::{File, FileIo, FileOptions, RawFileIoConnection, SyncMode};
12use crate::name::parse_name;
13use crate::node::OpenNode;
14use crate::object_request::{
15 run_synchronous_future_or_spawn, ConnectionCreator, ObjectRequest, Representation,
16};
17use crate::protocols::ToFileOptions;
18use crate::request_handler::{RequestHandler, RequestListener};
19use crate::{ObjectRequestRef, ProtocolsExt, ToObjectRequest};
20use anyhow::Error;
21use fidl::endpoints::ServerEnd;
22use fidl_fuchsia_io as fio;
23use static_assertions::assert_eq_size;
24use std::convert::TryInto as _;
25use std::future::Future;
26use std::ops::{ControlFlow, Deref, DerefMut};
27use std::pin::Pin;
28use std::sync::Arc;
29use storage_trace::{self as trace, TraceFutureExt};
30use zx_status::Status;
31
32#[cfg(target_os = "fuchsia")]
33use {
34 crate::file::common::get_backing_memory_validate_flags,
35 crate::temp_clone::{unblock, TempClonable},
36 std::io::SeekFrom,
37 zx::{self as zx, HandleBased},
38};
39
40async fn create_connection<
42 T: 'static + File,
43 U: Deref<Target = OpenNode<T>> + DerefMut + IoOpHandler + Unpin,
44>(
45 scope: ExecutionScope,
46 file: U,
47 options: FileOptions,
48 object_request: ObjectRequestRef<'_>,
49) -> Result<(), Status> {
50 new_connection_validate_options(&options, file.readable(), file.writable(), file.executable())?;
51
52 file.open_file(&options).await?;
53 if object_request.truncate {
54 file.truncate(0).await?;
55 }
56
57 let connection = FileConnection { scope: scope.clone(), file, options };
58 if let Ok(requests) = object_request.take().into_request_stream(&connection).await {
59 scope.spawn(RequestListener::new(requests, Some(connection)));
60 }
61 Ok(())
62}
63
64trait IoOpHandler: Send + Sync + Sized + 'static {
66 fn read(&mut self, count: u64) -> impl Future<Output = Result<Vec<u8>, Status>> + Send;
69
70 fn read_at(
72 &self,
73 offset: u64,
74 count: u64,
75 ) -> impl Future<Output = Result<Vec<u8>, Status>> + Send;
76
77 fn write(&mut self, data: Vec<u8>) -> impl Future<Output = Result<u64, Status>> + Send;
81
82 fn write_at(
84 &self,
85 offset: u64,
86 data: Vec<u8>,
87 ) -> impl Future<Output = Result<u64, Status>> + Send;
88
89 fn seek(
91 &mut self,
92 offset: i64,
93 origin: fio::SeekOrigin,
94 ) -> impl Future<Output = Result<u64, Status>> + Send;
95
96 fn set_flags(&mut self, flags: fio::Flags) -> Result<(), Status>;
98
99 #[cfg(target_os = "fuchsia")]
102 fn duplicate_stream(&self) -> Result<Option<zx::Stream>, Status>;
103
104 fn clone_connection(&self, options: FileOptions) -> Result<Self, Status>;
106}
107
108pub struct FidlIoConnection<T: 'static + File> {
111 file: OpenNode<T>,
113
114 seek: u64,
126
127 is_append: bool,
129}
130
131impl<T: 'static + File> Deref for FidlIoConnection<T> {
132 type Target = OpenNode<T>;
133
134 fn deref(&self) -> &Self::Target {
135 &self.file
136 }
137}
138
139impl<T: 'static + File> DerefMut for FidlIoConnection<T> {
140 fn deref_mut(&mut self) -> &mut Self::Target {
141 &mut self.file
142 }
143}
144
145impl<T: 'static + File + FileIo> FidlIoConnection<T> {
146 pub async fn create(
152 scope: ExecutionScope,
153 file: Arc<T>,
154 options: impl ToFileOptions,
155 object_request: ObjectRequestRef<'_>,
156 ) -> Result<(), Status> {
157 let file = OpenNode::new(file);
158 let options = options.to_file_options()?;
159 create_connection(
160 scope,
161 FidlIoConnection { file, seek: 0, is_append: options.is_append },
162 options,
163 object_request,
164 )
165 .await
166 }
167
168 pub fn create_sync(
171 scope: ExecutionScope,
172 file: Arc<T>,
173 options: impl ToFileOptions,
174 object_request: ObjectRequest,
175 ) {
176 run_synchronous_future_or_spawn(
177 scope.clone(),
178 object_request.handle_async(async |object_request| {
179 Self::create(scope, file, options, object_request).await
180 }),
181 )
182 }
183}
184
185impl<T: 'static + File + FileIo> ConnectionCreator<T> for FidlIoConnection<T> {
186 async fn create<'a>(
187 scope: ExecutionScope,
188 node: Arc<T>,
189 protocols: impl ProtocolsExt,
190 object_request: ObjectRequestRef<'a>,
191 ) -> Result<(), Status> {
192 Self::create(scope, node, protocols, object_request).await
193 }
194}
195
196impl<T: 'static + File + FileIo> IoOpHandler for FidlIoConnection<T> {
197 async fn read(&mut self, count: u64) -> Result<Vec<u8>, Status> {
198 let buffer = self.read_at(self.seek, count).await?;
199 let count: u64 = buffer.len().try_into().unwrap();
200 self.seek += count;
201 Ok(buffer)
202 }
203
204 async fn read_at(&self, offset: u64, count: u64) -> Result<Vec<u8>, Status> {
205 let mut buffer = vec![0u8; count as usize];
206 let count = self.file.read_at(offset, &mut buffer[..]).await?;
207 buffer.truncate(count.try_into().unwrap());
208 Ok(buffer)
209 }
210
211 async fn write(&mut self, data: Vec<u8>) -> Result<u64, Status> {
212 if self.is_append {
213 let (bytes, offset) = self.file.append(&data).await?;
214 self.seek = offset;
215 Ok(bytes)
216 } else {
217 let actual = self.write_at(self.seek, data).await?;
218 self.seek += actual;
219 Ok(actual)
220 }
221 }
222
223 async fn write_at(&self, offset: u64, data: Vec<u8>) -> Result<u64, Status> {
224 self.file.write_at(offset, &data).await
225 }
226
227 async fn seek(&mut self, offset: i64, origin: fio::SeekOrigin) -> Result<u64, Status> {
228 let new_seek = match origin {
230 fio::SeekOrigin::Start => offset as i128,
231 fio::SeekOrigin::Current => {
232 assert_eq_size!(usize, i64);
233 self.seek as i128 + offset as i128
234 }
235 fio::SeekOrigin::End => {
236 let size = self.file.get_size().await?;
237 assert_eq_size!(usize, i64, u64);
238 size as i128 + offset as i128
239 }
240 };
241
242 if let Ok(new_seek) = u64::try_from(new_seek) {
246 self.seek = new_seek;
247 Ok(self.seek)
248 } else {
249 Err(Status::OUT_OF_RANGE)
250 }
251 }
252
253 fn set_flags(&mut self, flags: fio::Flags) -> Result<(), Status> {
254 self.is_append = flags.intersects(fio::Flags::FILE_APPEND);
255 Ok(())
256 }
257
258 #[cfg(target_os = "fuchsia")]
259 fn duplicate_stream(&self) -> Result<Option<zx::Stream>, Status> {
260 Ok(None)
261 }
262
263 fn clone_connection(&self, options: FileOptions) -> Result<Self, Status> {
264 self.file.will_clone();
265 Ok(Self { file: OpenNode::new(self.file.clone()), seek: 0, is_append: options.is_append })
266 }
267}
268
269pub struct RawIoConnection<T: 'static + File> {
270 file: OpenNode<T>,
271}
272
273impl<T: 'static + File + RawFileIoConnection> RawIoConnection<T> {
274 pub async fn create(
275 scope: ExecutionScope,
276 file: Arc<T>,
277 options: impl ToFileOptions,
278 object_request: ObjectRequestRef<'_>,
279 ) -> Result<(), Status> {
280 let file = OpenNode::new(file);
281 create_connection(
282 scope,
283 RawIoConnection { file },
284 options.to_file_options()?,
285 object_request,
286 )
287 .await
288 }
289}
290
291impl<T: 'static + File + RawFileIoConnection> ConnectionCreator<T> for RawIoConnection<T> {
292 async fn create<'a>(
293 scope: ExecutionScope,
294 node: Arc<T>,
295 protocols: impl crate::ProtocolsExt,
296 object_request: ObjectRequestRef<'a>,
297 ) -> Result<(), Status> {
298 Self::create(scope, node, protocols, object_request).await
299 }
300}
301
302impl<T: 'static + File> Deref for RawIoConnection<T> {
303 type Target = OpenNode<T>;
304
305 fn deref(&self) -> &Self::Target {
306 &self.file
307 }
308}
309
310impl<T: 'static + File> DerefMut for RawIoConnection<T> {
311 fn deref_mut(&mut self) -> &mut Self::Target {
312 &mut self.file
313 }
314}
315
316impl<T: 'static + File + RawFileIoConnection> IoOpHandler for RawIoConnection<T> {
317 async fn read(&mut self, count: u64) -> Result<Vec<u8>, Status> {
318 self.file.read(count).await
319 }
320
321 async fn read_at(&self, offset: u64, count: u64) -> Result<Vec<u8>, Status> {
322 self.file.read_at(offset, count).await
323 }
324
325 async fn write(&mut self, data: Vec<u8>) -> Result<u64, Status> {
326 self.file.write(&data).await
327 }
328
329 async fn write_at(&self, offset: u64, data: Vec<u8>) -> Result<u64, Status> {
330 self.file.write_at(offset, &data).await
331 }
332
333 async fn seek(&mut self, offset: i64, origin: fio::SeekOrigin) -> Result<u64, Status> {
334 self.file.seek(offset, origin).await
335 }
336
337 fn set_flags(&mut self, flags: fio::Flags) -> Result<(), Status> {
338 self.file.set_flags(flags)
339 }
340
341 #[cfg(target_os = "fuchsia")]
342 fn duplicate_stream(&self) -> Result<Option<zx::Stream>, Status> {
343 Ok(None)
344 }
345
346 fn clone_connection(&self, _options: FileOptions) -> Result<Self, Status> {
347 self.file.will_clone();
348 Ok(Self { file: OpenNode::new(self.file.clone()) })
349 }
350}
351
352#[cfg(target_os = "fuchsia")]
353mod stream_io {
354 use super::*;
355 pub trait GetVmo {
356 const PAGER_ON_FIDL_EXECUTOR: bool = false;
362
363 fn get_vmo(&self) -> &zx::Vmo;
365 }
366
367 pub struct StreamIoConnection<T: 'static + File + GetVmo> {
370 file: OpenNode<T>,
372
373 stream: TempClonable<zx::Stream>,
375 }
376
377 impl<T: 'static + File + GetVmo> Deref for StreamIoConnection<T> {
378 type Target = OpenNode<T>;
379
380 fn deref(&self) -> &Self::Target {
381 &self.file
382 }
383 }
384
385 impl<T: 'static + File + GetVmo> DerefMut for StreamIoConnection<T> {
386 fn deref_mut(&mut self) -> &mut Self::Target {
387 &mut self.file
388 }
389 }
390
391 impl<T: 'static + File + GetVmo> StreamIoConnection<T> {
392 pub async fn create(
397 scope: ExecutionScope,
398 file: Arc<T>,
399 options: impl ToFileOptions,
400 object_request: ObjectRequestRef<'_>,
401 ) -> Result<(), Status> {
402 let file = OpenNode::new(file);
403 let options = options.to_file_options()?;
404 let stream = TempClonable::new(zx::Stream::create(
405 options.to_stream_options(),
406 file.get_vmo(),
407 0,
408 )?);
409 create_connection(scope, StreamIoConnection { file, stream }, options, object_request)
410 .await
411 }
412
413 pub fn create_sync(
416 scope: ExecutionScope,
417 file: Arc<T>,
418 options: impl ToFileOptions,
419 object_request: ObjectRequest,
420 ) {
421 run_synchronous_future_or_spawn(
422 scope.clone(),
423 object_request.handle_async(async |object_request| {
424 Self::create(scope, file, options, object_request).await
425 }),
426 )
427 }
428
429 async fn maybe_unblock<F, R>(&self, f: F) -> R
430 where
431 F: FnOnce(&zx::Stream) -> R + Send + 'static,
432 R: Send + 'static,
433 {
434 if T::PAGER_ON_FIDL_EXECUTOR {
435 let stream = self.stream.temp_clone();
436 unblock(move || f(&*stream)).await
437 } else {
438 f(&*self.stream)
439 }
440 }
441 }
442
443 impl<T: 'static + File + GetVmo> ConnectionCreator<T> for StreamIoConnection<T> {
444 async fn create<'a>(
445 scope: ExecutionScope,
446 node: Arc<T>,
447 protocols: impl crate::ProtocolsExt,
448 object_request: ObjectRequestRef<'a>,
449 ) -> Result<(), Status> {
450 Self::create(scope, node, protocols, object_request).await
451 }
452 }
453
454 impl<T: 'static + File + GetVmo> IoOpHandler for StreamIoConnection<T> {
455 async fn read(&mut self, count: u64) -> Result<Vec<u8>, Status> {
456 self.maybe_unblock(move |stream| {
457 stream.read_to_vec(zx::StreamReadOptions::empty(), count as usize)
458 })
459 .await
460 }
461
462 async fn read_at(&self, offset: u64, count: u64) -> Result<Vec<u8>, Status> {
463 self.maybe_unblock(move |stream| {
464 stream.read_at_to_vec(zx::StreamReadOptions::empty(), offset, count as usize)
465 })
466 .await
467 }
468
469 async fn write(&mut self, data: Vec<u8>) -> Result<u64, Status> {
470 self.maybe_unblock(move |stream| {
471 let actual = stream.write(zx::StreamWriteOptions::empty(), &data)?;
472 Ok(actual as u64)
473 })
474 .await
475 }
476
477 async fn write_at(&self, offset: u64, data: Vec<u8>) -> Result<u64, Status> {
478 self.maybe_unblock(move |stream| {
479 let actual = stream.write_at(zx::StreamWriteOptions::empty(), offset, &data)?;
480 Ok(actual as u64)
481 })
482 .await
483 }
484
485 async fn seek(&mut self, offset: i64, origin: fio::SeekOrigin) -> Result<u64, Status> {
486 let position = match origin {
487 fio::SeekOrigin::Start => {
488 if offset < 0 {
489 return Err(Status::INVALID_ARGS);
490 }
491 SeekFrom::Start(offset as u64)
492 }
493 fio::SeekOrigin::Current => SeekFrom::Current(offset),
494 fio::SeekOrigin::End => SeekFrom::End(offset),
495 };
496 self.stream.seek(position)
497 }
498
499 fn set_flags(&mut self, flags: fio::Flags) -> Result<(), Status> {
500 let append_mode = if flags.contains(fio::Flags::FILE_APPEND) { 1 } else { 0 };
501 self.stream.set_mode_append(&append_mode)
502 }
503
504 fn duplicate_stream(&self) -> Result<Option<zx::Stream>, Status> {
505 self.stream.duplicate_handle(zx::Rights::SAME_RIGHTS).map(|s| Some(s))
506 }
507
508 fn clone_connection(&self, options: FileOptions) -> Result<Self, Status> {
509 let stream = TempClonable::new(zx::Stream::create(
510 options.to_stream_options(),
511 self.file.get_vmo(),
512 0,
513 )?);
514 self.file.will_clone();
515 Ok(Self { file: OpenNode::new(self.file.clone()), stream })
516 }
517 }
518}
519
520#[cfg(target_os = "fuchsia")]
521pub use stream_io::*;
522
523enum ConnectionState {
525 Alive,
527 Closed(fio::FileCloseResponder),
530 Dropped,
533}
534
535struct FileConnection<U> {
537 scope: ExecutionScope,
540
541 file: U,
543
544 options: FileOptions,
546}
547
548impl<T: 'static + File, U: Deref<Target = OpenNode<T>> + DerefMut + IoOpHandler + Unpin>
549 FileConnection<U>
550{
551 async fn handle_request(&mut self, req: fio::FileRequest) -> Result<ConnectionState, Error> {
554 match req {
555 #[cfg(fuchsia_api_level_at_least = "26")]
556 fio::FileRequest::DeprecatedClone { flags, object, control_handle: _ } => {
557 trace::duration!(c"storage", c"File::DeprecatedClone");
558 self.handle_deprecated_clone(flags, object).await;
559 }
560 #[cfg(not(fuchsia_api_level_at_least = "26"))]
561 fio::FileRequest::Clone { flags, object, control_handle: _ } => {
562 trace::duration!(c"storage", c"File::Clone");
563 self.handle_deprecated_clone(flags, object).await;
564 }
565 #[cfg(fuchsia_api_level_at_least = "26")]
566 fio::FileRequest::Clone { request, control_handle: _ } => {
567 trace::duration!(c"storage", c"File::Clone");
568 self.handle_clone(ServerEnd::new(request.into_channel()));
569 }
570 #[cfg(not(fuchsia_api_level_at_least = "26"))]
571 fio::FileRequest::Clone2 { request, control_handle: _ } => {
572 trace::duration!(c"storage", c"File::Clone2");
573 self.handle_clone(ServerEnd::new(request.into_channel()));
574 }
575 fio::FileRequest::Close { responder } => {
576 return Ok(ConnectionState::Closed(responder));
577 }
578 #[cfg(not(target_os = "fuchsia"))]
579 fio::FileRequest::Describe { responder } => {
580 responder.send(fio::FileInfo {
581 stream: None,
582 observer: self.file.event()?,
583 ..Default::default()
584 })?;
585 }
586 #[cfg(target_os = "fuchsia")]
587 fio::FileRequest::Describe { responder } => {
588 trace::duration!(c"storage", c"File::Describe");
589 let stream = self.file.duplicate_stream()?;
590 responder.send(fio::FileInfo {
591 stream,
592 observer: self.file.event()?,
593 ..Default::default()
594 })?;
595 }
596 fio::FileRequest::LinkInto { dst_parent_token, dst, responder } => {
597 async move {
598 responder.send(
599 self.handle_link_into(dst_parent_token, dst)
600 .await
601 .map_err(Status::into_raw),
602 )
603 }
604 .trace(trace::trace_future_args!(c"storage", c"File::LinkInto"))
605 .await?;
606 }
607 fio::FileRequest::GetConnectionInfo { responder } => {
608 trace::duration!(c"storage", c"File::GetConnectionInfo");
609 responder.send(fio::ConnectionInfo {
611 rights: Some(self.options.rights),
612 ..Default::default()
613 })?;
614 }
615 fio::FileRequest::Sync { responder } => {
616 async move {
617 responder.send(self.file.sync(SyncMode::Normal).await.map_err(Status::into_raw))
618 }
619 .trace(trace::trace_future_args!(c"storage", c"File::Sync"))
620 .await?;
621 }
622 fio::FileRequest::GetAttr { responder } => {
623 async move {
624 let (status, attrs) =
625 crate::common::io2_to_io1_attrs(self.file.as_ref(), self.options.rights)
626 .await;
627 responder.send(status.into_raw(), &attrs)
628 }
629 .trace(trace::trace_future_args!(c"storage", c"File::GetAttr"))
630 .await?;
631 }
632 fio::FileRequest::SetAttr { flags, attributes, responder } => {
633 async move {
634 let result =
635 self.handle_update_attributes(io1_to_io2_attrs(flags, attributes)).await;
636 responder.send(Status::from_result(result).into_raw())
637 }
638 .trace(trace::trace_future_args!(c"storage", c"File::SetAttr"))
639 .await?;
640 }
641 fio::FileRequest::GetAttributes { query, responder } => {
642 async move {
643 let attrs = self.file.get_attributes(query).await;
645 responder.send(
646 attrs
647 .as_ref()
648 .map(|attrs| (&attrs.mutable_attributes, &attrs.immutable_attributes))
649 .map_err(|status| status.into_raw()),
650 )
651 }
652 .trace(trace::trace_future_args!(c"storage", c"File::GetAttributes"))
653 .await?;
654 }
655 fio::FileRequest::UpdateAttributes { payload, responder } => {
656 async move {
657 let result =
658 self.handle_update_attributes(payload).await.map_err(Status::into_raw);
659 responder.send(result)
660 }
661 .trace(trace::trace_future_args!(c"storage", c"File::UpdateAttributes"))
662 .await?;
663 }
664 fio::FileRequest::ListExtendedAttributes { iterator, control_handle: _ } => {
665 self.handle_list_extended_attribute(iterator)
666 .trace(trace::trace_future_args!(c"storage", c"File::ListExtendedAttributes"))
667 .await;
668 }
669 fio::FileRequest::GetExtendedAttribute { name, responder } => {
670 async move {
671 let res =
672 self.handle_get_extended_attribute(name).await.map_err(Status::into_raw);
673 responder.send(res)
674 }
675 .trace(trace::trace_future_args!(c"storage", c"File::GetExtendedAttribute"))
676 .await?;
677 }
678 fio::FileRequest::SetExtendedAttribute { name, value, mode, responder } => {
679 async move {
680 let res = self
681 .handle_set_extended_attribute(name, value, mode)
682 .await
683 .map_err(Status::into_raw);
684 responder.send(res)
685 }
686 .trace(trace::trace_future_args!(c"storage", c"File::SetExtendedAttribute"))
687 .await?;
688 }
689 fio::FileRequest::RemoveExtendedAttribute { name, responder } => {
690 async move {
691 let res =
692 self.handle_remove_extended_attribute(name).await.map_err(Status::into_raw);
693 responder.send(res)
694 }
695 .trace(trace::trace_future_args!(c"storage", c"File::RemoveExtendedAttribute"))
696 .await?;
697 }
698 #[cfg(fuchsia_api_level_at_least = "HEAD")]
699 fio::FileRequest::EnableVerity { options, responder } => {
700 async move {
701 let res = self.handle_enable_verity(options).await.map_err(Status::into_raw);
702 responder.send(res)
703 }
704 .trace(trace::trace_future_args!(c"storage", c"File::EnableVerity"))
705 .await?;
706 }
707 fio::FileRequest::Read { count, responder } => {
708 let trace_args =
709 trace::trace_future_args!(c"storage", c"File::Read", "bytes" => count);
710 async move {
711 let result = self.handle_read(count).await;
712 responder.send(result.as_deref().map_err(|s| s.into_raw()))
713 }
714 .trace(trace_args)
715 .await?;
716 }
717 fio::FileRequest::ReadAt { offset, count, responder } => {
718 let trace_args = trace::trace_future_args!(
719 c"storage",
720 c"File::ReadAt",
721 "offset" => offset,
722 "bytes" => count
723 );
724 async move {
725 let result = self.handle_read_at(offset, count).await;
726 responder.send(result.as_deref().map_err(|s| s.into_raw()))
727 }
728 .trace(trace_args)
729 .await?;
730 }
731 fio::FileRequest::Write { data, responder } => {
732 let trace_args =
733 trace::trace_future_args!(c"storage", c"File::Write", "bytes" => data.len());
734 async move {
735 let result = self.handle_write(data).await;
736 responder.send(result.map_err(Status::into_raw))
737 }
738 .trace(trace_args)
739 .await?;
740 }
741 fio::FileRequest::WriteAt { offset, data, responder } => {
742 let trace_args = trace::trace_future_args!(
743 c"storage",
744 c"File::WriteAt",
745 "offset" => offset,
746 "bytes" => data.len()
747 );
748 async move {
749 let result = self.handle_write_at(offset, data).await;
750 responder.send(result.map_err(Status::into_raw))
751 }
752 .trace(trace_args)
753 .await?;
754 }
755 fio::FileRequest::Seek { origin, offset, responder } => {
756 async move {
757 let result = self.handle_seek(offset, origin).await;
758 responder.send(result.map_err(Status::into_raw))
759 }
760 .trace(trace::trace_future_args!(c"storage", c"File::Seek"))
761 .await?;
762 }
763 fio::FileRequest::Resize { length, responder } => {
764 async move {
765 let result = self.handle_truncate(length).await;
766 responder.send(result.map_err(Status::into_raw))
767 }
768 .trace(trace::trace_future_args!(c"storage", c"File::Resize"))
769 .await?;
770 }
771 #[cfg(fuchsia_api_level_at_least = "NEXT")]
772 fio::FileRequest::GetFlags { responder } => {
773 trace::duration!(c"storage", c"File::GetFlags");
774 responder.send(Ok(fio::Flags::from(&self.options)))?;
775 }
776 #[cfg(fuchsia_api_level_at_least = "NEXT")]
777 fio::FileRequest::SetFlags { flags, responder } => {
778 trace::duration!(c"storage", c"File::SetFlags");
779 if flags.is_empty() || flags == fio::Flags::FILE_APPEND {
781 self.options.is_append = flags.contains(fio::Flags::FILE_APPEND);
782 responder.send(self.file.set_flags(flags).map_err(Status::into_raw))?;
783 } else {
784 responder.send(Err(Status::INVALID_ARGS.into_raw()))?;
785 }
786 }
787 #[cfg(fuchsia_api_level_at_least = "NEXT")]
788 fio::FileRequest::DeprecatedGetFlags { responder } => {
789 trace::duration!(c"storage", c"File::DeprecatedGetFlags");
790 responder.send(Status::OK.into_raw(), self.options.to_io1())?;
791 }
792 #[cfg(fuchsia_api_level_at_least = "NEXT")]
793 fio::FileRequest::DeprecatedSetFlags { flags, responder } => {
794 trace::duration!(c"storage", c"File::DeprecatedSetFlags");
795 let is_append = flags.contains(fio::OpenFlags::APPEND);
797 self.options.is_append = is_append;
798 let flags = if is_append { fio::Flags::FILE_APPEND } else { fio::Flags::empty() };
799 responder.send(Status::from_result(self.file.set_flags(flags)).into_raw())?;
800 }
801 #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
802 fio::FileRequest::GetFlags { responder } => {
803 trace::duration!(c"storage", c"File::GetFlags");
804 responder.send(Status::OK.into_raw(), self.options.to_io1())?;
805 }
806 #[cfg(not(fuchsia_api_level_at_least = "NEXT"))]
807 fio::FileRequest::SetFlags { flags, responder } => {
808 trace::duration!(c"storage", c"File::SetFlags");
809 let is_append = flags.contains(fio::OpenFlags::APPEND);
811 self.options.is_append = is_append;
812 let flags = if is_append { fio::Flags::FILE_APPEND } else { fio::Flags::empty() };
813 responder.send(Status::from_result(self.file.set_flags(flags)).into_raw())?;
814 }
815 #[cfg(target_os = "fuchsia")]
816 fio::FileRequest::GetBackingMemory { flags, responder } => {
817 async move {
818 let result = self.handle_get_backing_memory(flags).await;
819 responder.send(result.map_err(Status::into_raw))
820 }
821 .trace(trace::trace_future_args!(c"storage", c"File::GetBackingMemory"))
822 .await?;
823 }
824
825 #[cfg(not(target_os = "fuchsia"))]
826 fio::FileRequest::GetBackingMemory { flags: _, responder } => {
827 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
828 }
829 fio::FileRequest::AdvisoryLock { request: _, responder } => {
830 trace::duration!(c"storage", c"File::AdvisoryLock");
831 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
832 }
833 fio::FileRequest::Query { responder } => {
834 trace::duration!(c"storage", c"File::Query");
835 responder.send(fio::FILE_PROTOCOL_NAME.as_bytes())?;
836 }
837 fio::FileRequest::QueryFilesystem { responder } => {
838 trace::duration!(c"storage", c"File::QueryFilesystem");
839 match self.file.query_filesystem() {
840 Err(status) => responder.send(status.into_raw(), None)?,
841 Ok(info) => responder.send(0, Some(&info))?,
842 }
843 }
844 #[cfg(fuchsia_api_level_at_least = "HEAD")]
845 fio::FileRequest::Allocate { offset, length, mode, responder } => {
846 async move {
847 let result = self.handle_allocate(offset, length, mode).await;
848 responder.send(result.map_err(Status::into_raw))
849 }
850 .trace(trace::trace_future_args!(c"storage", c"File::Allocate"))
851 .await?;
852 }
853 fio::FileRequest::_UnknownMethod { .. } => (),
854 }
855 Ok(ConnectionState::Alive)
856 }
857
858 async fn handle_deprecated_clone(
859 &mut self,
860 flags: fio::OpenFlags,
861 server_end: ServerEnd<fio::NodeMarker>,
862 ) {
863 flags
864 .to_object_request(server_end)
865 .handle_async(async |object_request| {
866 let options =
867 inherit_rights_for_clone(self.options.to_io1(), flags)?.to_file_options()?;
868
869 let connection = Self {
870 scope: self.scope.clone(),
871 file: self.file.clone_connection(options)?,
872 options,
873 };
874
875 let requests = object_request.take().into_request_stream(&connection).await?;
876 self.scope.spawn(RequestListener::new(requests, Some(connection)));
877 Ok(())
878 })
879 .await;
880 }
881
882 fn handle_clone(&mut self, server_end: ServerEnd<fio::FileMarker>) {
883 let connection = match self.file.clone_connection(self.options) {
884 Ok(file) => Self { scope: self.scope.clone(), file, options: self.options },
885 Err(status) => {
886 let _ = server_end.close_with_epitaph(status);
887 return;
888 }
889 };
890 self.scope.spawn(RequestListener::new(server_end.into_stream(), Some(connection)));
891 }
892
893 async fn handle_read(&mut self, count: u64) -> Result<Vec<u8>, Status> {
894 if !self.options.rights.intersects(fio::Operations::READ_BYTES) {
895 return Err(Status::BAD_HANDLE);
896 }
897
898 if count > fio::MAX_TRANSFER_SIZE {
899 return Err(Status::OUT_OF_RANGE);
900 }
901 self.file.read(count).await
902 }
903
904 async fn handle_read_at(&self, offset: u64, count: u64) -> Result<Vec<u8>, Status> {
905 if !self.options.rights.intersects(fio::Operations::READ_BYTES) {
906 return Err(Status::BAD_HANDLE);
907 }
908 if count > fio::MAX_TRANSFER_SIZE {
909 return Err(Status::OUT_OF_RANGE);
910 }
911 self.file.read_at(offset, count).await
912 }
913
914 async fn handle_write(&mut self, content: Vec<u8>) -> Result<u64, Status> {
915 if !self.options.rights.intersects(fio::Operations::WRITE_BYTES) {
916 return Err(Status::BAD_HANDLE);
917 }
918 self.file.write(content).await
919 }
920
921 async fn handle_write_at(&self, offset: u64, content: Vec<u8>) -> Result<u64, Status> {
922 if !self.options.rights.intersects(fio::Operations::WRITE_BYTES) {
923 return Err(Status::BAD_HANDLE);
924 }
925
926 self.file.write_at(offset, content).await
927 }
928
929 async fn handle_seek(&mut self, offset: i64, origin: fio::SeekOrigin) -> Result<u64, Status> {
931 self.file.seek(offset, origin).await
932 }
933
934 async fn handle_update_attributes(
935 &mut self,
936 attributes: fio::MutableNodeAttributes,
937 ) -> Result<(), Status> {
938 if !self.options.rights.intersects(fio::Operations::UPDATE_ATTRIBUTES) {
939 return Err(Status::BAD_HANDLE);
940 }
941
942 self.file.update_attributes(attributes).await
943 }
944
945 #[cfg(fuchsia_api_level_at_least = "HEAD")]
946 async fn handle_enable_verity(
947 &mut self,
948 options: fio::VerificationOptions,
949 ) -> Result<(), Status> {
950 if !self.options.rights.intersects(fio::Operations::UPDATE_ATTRIBUTES) {
951 return Err(Status::BAD_HANDLE);
952 }
953 self.file.enable_verity(options).await
954 }
955
956 async fn handle_truncate(&mut self, length: u64) -> Result<(), Status> {
957 if !self.options.rights.intersects(fio::Operations::WRITE_BYTES) {
958 return Err(Status::BAD_HANDLE);
959 }
960
961 self.file.truncate(length).await
962 }
963
964 #[cfg(target_os = "fuchsia")]
965 async fn handle_get_backing_memory(&mut self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
966 get_backing_memory_validate_flags(flags, self.options.to_io1())?;
967 self.file.get_backing_memory(flags).await
968 }
969
970 async fn handle_list_extended_attribute(
971 &mut self,
972 iterator: ServerEnd<fio::ExtendedAttributeIteratorMarker>,
973 ) {
974 let attributes = match self.file.list_extended_attributes().await {
975 Ok(attributes) => attributes,
976 Err(status) => {
977 log::error!(status:?; "list extended attributes failed");
978 iterator
979 .close_with_epitaph(status)
980 .unwrap_or_else(|error| log::error!(error:?; "failed to send epitaph"));
981 return;
982 }
983 };
984 self.scope.spawn(extended_attributes_sender(iterator, attributes));
985 }
986
987 async fn handle_get_extended_attribute(
988 &mut self,
989 name: Vec<u8>,
990 ) -> Result<fio::ExtendedAttributeValue, Status> {
991 let value = self.file.get_extended_attribute(name).await?;
992 encode_extended_attribute_value(value)
993 }
994
995 async fn handle_set_extended_attribute(
996 &mut self,
997 name: Vec<u8>,
998 value: fio::ExtendedAttributeValue,
999 mode: fio::SetExtendedAttributeMode,
1000 ) -> Result<(), Status> {
1001 if name.contains(&0) {
1002 return Err(Status::INVALID_ARGS);
1003 }
1004 let val = decode_extended_attribute_value(value)?;
1005 self.file.set_extended_attribute(name, val, mode).await
1006 }
1007
1008 async fn handle_remove_extended_attribute(&mut self, name: Vec<u8>) -> Result<(), Status> {
1009 self.file.remove_extended_attribute(name).await
1010 }
1011
1012 async fn handle_link_into(
1013 &mut self,
1014 target_parent_token: fidl::Event,
1015 target_name: String,
1016 ) -> Result<(), Status> {
1017 let target_name = parse_name(target_name).map_err(|_| Status::INVALID_ARGS)?;
1018
1019 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1020 if !self.options.is_linkable {
1021 return Err(Status::NOT_FOUND);
1022 }
1023
1024 if !self.options.rights.contains(
1025 fio::Operations::READ_BYTES
1026 | fio::Operations::WRITE_BYTES
1027 | fio::Operations::GET_ATTRIBUTES
1028 | fio::Operations::UPDATE_ATTRIBUTES,
1029 ) {
1030 return Err(Status::ACCESS_DENIED);
1031 }
1032
1033 let target_parent = self
1034 .scope
1035 .token_registry()
1036 .get_owner(target_parent_token.into())?
1037 .ok_or(Err(Status::NOT_FOUND))?;
1038
1039 self.file.clone().link_into(target_parent, target_name).await
1040 }
1041
1042 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1043 async fn handle_allocate(
1044 &mut self,
1045 offset: u64,
1046 length: u64,
1047 mode: fio::AllocateMode,
1048 ) -> Result<(), Status> {
1049 self.file.allocate(offset, length, mode).await
1050 }
1051
1052 fn should_sync_before_close(&self) -> bool {
1053 self.options
1054 .rights
1055 .intersects(fio::Operations::WRITE_BYTES | fio::Operations::UPDATE_ATTRIBUTES)
1056 }
1057}
1058
1059impl<T: 'static + File, U: Deref<Target = OpenNode<T>> + DerefMut + IoOpHandler + Unpin>
1062 RequestHandler for Option<FileConnection<U>>
1063{
1064 type Request = Result<fio::FileRequest, fidl::Error>;
1065
1066 async fn handle_request(self: Pin<&mut Self>, request: Self::Request) -> ControlFlow<()> {
1067 let option_this = self.get_mut();
1068 let this = option_this.as_mut().unwrap();
1069 let _guard = this.scope.active_guard();
1070 let state = match request {
1071 Ok(request) => {
1072 this.handle_request(request)
1073 .await
1074 .unwrap_or(ConnectionState::Dropped)
1077 }
1078 Err(_) => {
1079 ConnectionState::Dropped
1083 }
1084 };
1085 match state {
1086 ConnectionState::Alive => ControlFlow::Continue(()),
1087 ConnectionState::Dropped => {
1088 if this.should_sync_before_close() {
1089 let _ = this.file.sync(SyncMode::PreClose).await;
1090 }
1091 ControlFlow::Break(())
1092 }
1093 ConnectionState::Closed(responder) => {
1094 async move {
1095 let this = option_this.as_mut().unwrap();
1096 let _ = responder.send({
1097 let result = if this.should_sync_before_close() {
1098 this.file.sync(SyncMode::PreClose).await.map_err(Status::into_raw)
1099 } else {
1100 Ok(())
1101 };
1102 std::mem::drop(option_this.take());
1105 result
1106 });
1107 }
1108 .trace(trace::trace_future_args!(c"storage", c"File::Close"))
1109 .await;
1110 ControlFlow::Break(())
1111 }
1112 }
1113 }
1114
1115 async fn stream_closed(self: Pin<&mut Self>) {
1116 let this = self.get_mut().as_mut().unwrap();
1117 if this.should_sync_before_close() {
1118 let _guard = this.scope.active_guard();
1119 let _ = this.file.sync(SyncMode::PreClose).await;
1120 }
1121 }
1122}
1123
1124impl<T: 'static + File, U: Deref<Target = OpenNode<T>> + IoOpHandler> Representation
1125 for FileConnection<U>
1126{
1127 type Protocol = fio::FileMarker;
1128
1129 async fn get_representation(
1130 &self,
1131 requested_attributes: fio::NodeAttributesQuery,
1132 ) -> Result<fio::Representation, Status> {
1133 Ok(fio::Representation::File(fio::FileInfo {
1135 is_append: Some(self.options.is_append),
1136 observer: self.file.event()?,
1137 #[cfg(target_os = "fuchsia")]
1138 stream: self.file.duplicate_stream()?,
1139 #[cfg(not(target_os = "fuchsia"))]
1140 stream: None,
1141 attributes: if requested_attributes.is_empty() {
1142 None
1143 } else {
1144 Some(self.file.get_attributes(requested_attributes).await?)
1145 },
1146 ..Default::default()
1147 }))
1148 }
1149
1150 async fn node_info(&self) -> Result<fio::NodeInfoDeprecated, Status> {
1151 #[cfg(target_os = "fuchsia")]
1152 let stream = self.file.duplicate_stream()?;
1153 #[cfg(not(target_os = "fuchsia"))]
1154 let stream = None;
1155 Ok(fio::NodeInfoDeprecated::File(fio::FileObject { event: self.file.event()?, stream }))
1156 }
1157}
1158
1159#[cfg(test)]
1160mod tests {
1161 use super::*;
1162 use crate::directory::entry::{EntryInfo, GetEntryInfo};
1163 use crate::node::Node;
1164 use assert_matches::assert_matches;
1165 use futures::prelude::*;
1166 use std::sync::Mutex;
1167
1168 const RIGHTS_R: fio::Operations =
1169 fio::Operations::READ_BYTES.union(fio::Operations::GET_ATTRIBUTES);
1170 const RIGHTS_W: fio::Operations = fio::Operations::WRITE_BYTES
1171 .union(fio::Operations::GET_ATTRIBUTES)
1172 .union(fio::Operations::UPDATE_ATTRIBUTES);
1173 const RIGHTS_RW: fio::Operations = fio::Operations::READ_BYTES
1174 .union(fio::Operations::WRITE_BYTES)
1175 .union(fio::Operations::GET_ATTRIBUTES)
1176 .union(fio::Operations::UPDATE_ATTRIBUTES);
1177
1178 #[derive(Debug, PartialEq)]
1179 enum FileOperation {
1180 Init {
1181 options: FileOptions,
1182 },
1183 ReadAt {
1184 offset: u64,
1185 count: u64,
1186 },
1187 WriteAt {
1188 offset: u64,
1189 content: Vec<u8>,
1190 },
1191 Append {
1192 content: Vec<u8>,
1193 },
1194 Truncate {
1195 length: u64,
1196 },
1197 #[cfg(target_os = "fuchsia")]
1198 GetBackingMemory {
1199 flags: fio::VmoFlags,
1200 },
1201 GetSize,
1202 GetAttributes {
1203 query: fio::NodeAttributesQuery,
1204 },
1205 UpdateAttributes {
1206 attrs: fio::MutableNodeAttributes,
1207 },
1208 Close,
1209 Sync,
1210 }
1211
1212 type MockCallbackType = Box<dyn Fn(&FileOperation) -> Status + Sync + Send>;
1213 struct MockFile {
1215 operations: Mutex<Vec<FileOperation>>,
1217 callback: MockCallbackType,
1219 file_size: u64,
1221 #[cfg(target_os = "fuchsia")]
1222 vmo: zx::Vmo,
1224 }
1225
1226 const MOCK_FILE_SIZE: u64 = 256;
1227 const MOCK_FILE_ID: u64 = 10;
1228 const MOCK_FILE_LINKS: u64 = 2;
1229 const MOCK_FILE_CREATION_TIME: u64 = 10;
1230 const MOCK_FILE_MODIFICATION_TIME: u64 = 100;
1231 impl MockFile {
1232 fn new(callback: MockCallbackType) -> Arc<Self> {
1233 Arc::new(MockFile {
1234 operations: Mutex::new(Vec::new()),
1235 callback,
1236 file_size: MOCK_FILE_SIZE,
1237 #[cfg(target_os = "fuchsia")]
1238 vmo: zx::Handle::invalid().into(),
1239 })
1240 }
1241
1242 #[cfg(target_os = "fuchsia")]
1243 fn new_with_vmo(callback: MockCallbackType, vmo: zx::Vmo) -> Arc<Self> {
1244 Arc::new(MockFile {
1245 operations: Mutex::new(Vec::new()),
1246 callback,
1247 file_size: MOCK_FILE_SIZE,
1248 vmo,
1249 })
1250 }
1251
1252 fn handle_operation(&self, operation: FileOperation) -> Result<(), Status> {
1253 let result = (self.callback)(&operation);
1254 self.operations.lock().unwrap().push(operation);
1255 match result {
1256 Status::OK => Ok(()),
1257 err => Err(err),
1258 }
1259 }
1260 }
1261
1262 impl GetEntryInfo for MockFile {
1263 fn entry_info(&self) -> EntryInfo {
1264 EntryInfo::new(MOCK_FILE_ID, fio::DirentType::File)
1265 }
1266 }
1267
1268 impl Node for MockFile {
1269 async fn get_attributes(
1270 &self,
1271 query: fio::NodeAttributesQuery,
1272 ) -> Result<fio::NodeAttributes2, Status> {
1273 self.handle_operation(FileOperation::GetAttributes { query })?;
1274 Ok(attributes!(
1275 query,
1276 Mutable {
1277 creation_time: MOCK_FILE_CREATION_TIME,
1278 modification_time: MOCK_FILE_MODIFICATION_TIME,
1279 },
1280 Immutable {
1281 protocols: fio::NodeProtocolKinds::FILE,
1282 abilities: fio::Operations::GET_ATTRIBUTES
1283 | fio::Operations::UPDATE_ATTRIBUTES
1284 | fio::Operations::READ_BYTES
1285 | fio::Operations::WRITE_BYTES,
1286 content_size: self.file_size,
1287 storage_size: 2 * self.file_size,
1288 link_count: MOCK_FILE_LINKS,
1289 id: MOCK_FILE_ID,
1290 }
1291 ))
1292 }
1293
1294 fn close(self: Arc<Self>) {
1295 let _ = self.handle_operation(FileOperation::Close);
1296 }
1297 }
1298
1299 impl File for MockFile {
1300 fn writable(&self) -> bool {
1301 true
1302 }
1303
1304 async fn open_file(&self, options: &FileOptions) -> Result<(), Status> {
1305 self.handle_operation(FileOperation::Init { options: *options })?;
1306 Ok(())
1307 }
1308
1309 async fn truncate(&self, length: u64) -> Result<(), Status> {
1310 self.handle_operation(FileOperation::Truncate { length })
1311 }
1312
1313 #[cfg(target_os = "fuchsia")]
1314 async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
1315 self.handle_operation(FileOperation::GetBackingMemory { flags })?;
1316 Err(Status::NOT_SUPPORTED)
1317 }
1318
1319 async fn get_size(&self) -> Result<u64, Status> {
1320 self.handle_operation(FileOperation::GetSize)?;
1321 Ok(self.file_size)
1322 }
1323
1324 async fn update_attributes(&self, attrs: fio::MutableNodeAttributes) -> Result<(), Status> {
1325 self.handle_operation(FileOperation::UpdateAttributes { attrs })?;
1326 Ok(())
1327 }
1328
1329 async fn sync(&self, _mode: SyncMode) -> Result<(), Status> {
1330 self.handle_operation(FileOperation::Sync)
1331 }
1332 }
1333
1334 impl FileIo for MockFile {
1335 async fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<u64, Status> {
1336 let count = buffer.len() as u64;
1337 self.handle_operation(FileOperation::ReadAt { offset, count })?;
1338
1339 let mut i = offset;
1341 buffer.fill_with(|| {
1342 let v = (i % 256) as u8;
1343 i += 1;
1344 v
1345 });
1346 Ok(count)
1347 }
1348
1349 async fn write_at(&self, offset: u64, content: &[u8]) -> Result<u64, Status> {
1350 self.handle_operation(FileOperation::WriteAt { offset, content: content.to_vec() })?;
1351 Ok(content.len() as u64)
1352 }
1353
1354 async fn append(&self, content: &[u8]) -> Result<(u64, u64), Status> {
1355 self.handle_operation(FileOperation::Append { content: content.to_vec() })?;
1356 Ok((content.len() as u64, self.file_size + content.len() as u64))
1357 }
1358 }
1359
1360 #[cfg(target_os = "fuchsia")]
1361 impl GetVmo for MockFile {
1362 fn get_vmo(&self) -> &zx::Vmo {
1363 &self.vmo
1364 }
1365 }
1366
1367 fn only_allow_init(op: &FileOperation) -> Status {
1369 match op {
1370 FileOperation::Init { .. } => Status::OK,
1371 _ => Status::IO,
1372 }
1373 }
1374
1375 fn always_succeed_callback(_op: &FileOperation) -> Status {
1377 Status::OK
1378 }
1379
1380 struct TestEnv {
1381 pub file: Arc<MockFile>,
1382 pub proxy: fio::FileProxy,
1383 pub scope: ExecutionScope,
1384 }
1385
1386 fn init_mock_file(callback: MockCallbackType, flags: fio::OpenFlags) -> TestEnv {
1387 let file = MockFile::new(callback);
1388 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::FileMarker>();
1389
1390 let scope = ExecutionScope::new();
1391
1392 flags.to_object_request(server_end).create_connection_sync::<FidlIoConnection<_>, _>(
1393 scope.clone(),
1394 file.clone(),
1395 flags,
1396 );
1397
1398 TestEnv { file, proxy, scope }
1399 }
1400
1401 #[fuchsia::test]
1402 async fn test_open_flag_truncate() {
1403 let env = init_mock_file(
1404 Box::new(always_succeed_callback),
1405 fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::TRUNCATE,
1406 );
1407 let () = env.proxy.sync().await.unwrap().map_err(Status::from_raw).unwrap();
1409 let events = env.file.operations.lock().unwrap();
1410 assert_eq!(
1411 *events,
1412 vec![
1413 FileOperation::Init {
1414 options: FileOptions { rights: RIGHTS_W, is_append: false, is_linkable: true }
1415 },
1416 FileOperation::Truncate { length: 0 },
1417 FileOperation::Sync,
1418 ]
1419 );
1420 }
1421
1422 #[fuchsia::test]
1423 async fn test_clone_same_rights() {
1424 let env = init_mock_file(
1425 Box::new(always_succeed_callback),
1426 fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
1427 );
1428 let _: Vec<u8> = env.proxy.read(6).await.unwrap().map_err(Status::from_raw).unwrap();
1430 let (clone_proxy, remote) = fidl::endpoints::create_proxy::<fio::FileMarker>();
1431 env.proxy
1432 .deprecated_clone(fio::OpenFlags::CLONE_SAME_RIGHTS, remote.into_channel().into())
1433 .unwrap();
1434 let _: u64 = clone_proxy
1436 .seek(fio::SeekOrigin::Start, 100)
1437 .await
1438 .unwrap()
1439 .map_err(Status::from_raw)
1440 .unwrap();
1441 let _: Vec<u8> = clone_proxy.read(5).await.unwrap().map_err(Status::from_raw).unwrap();
1442
1443 let _: Vec<u8> = env.proxy.read(5).await.unwrap().map_err(Status::from_raw).unwrap();
1445
1446 let events = env.file.operations.lock().unwrap();
1447 assert_eq!(
1449 *events,
1450 vec![
1451 FileOperation::Init {
1452 options: FileOptions { rights: RIGHTS_RW, is_append: false, is_linkable: true }
1453 },
1454 FileOperation::ReadAt { offset: 0, count: 6 },
1455 FileOperation::ReadAt { offset: 100, count: 5 },
1456 FileOperation::ReadAt { offset: 6, count: 5 },
1457 ]
1458 );
1459 }
1460
1461 #[fuchsia::test]
1462 async fn test_close_succeeds() {
1463 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1464 let () = env.proxy.close().await.unwrap().map_err(Status::from_raw).unwrap();
1465
1466 let events = env.file.operations.lock().unwrap();
1467 assert_eq!(
1468 *events,
1469 vec![
1470 FileOperation::Init {
1471 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1472 },
1473 FileOperation::Close {},
1474 ]
1475 );
1476 }
1477
1478 #[fuchsia::test]
1479 async fn test_close_fails() {
1480 let env = init_mock_file(
1481 Box::new(only_allow_init),
1482 fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
1483 );
1484 let status = env.proxy.close().await.unwrap().map_err(Status::from_raw);
1485 assert_eq!(status, Err(Status::IO));
1486
1487 let events = env.file.operations.lock().unwrap();
1488 assert_eq!(
1489 *events,
1490 vec![
1491 FileOperation::Init {
1492 options: FileOptions { rights: RIGHTS_RW, is_append: false, is_linkable: true }
1493 },
1494 FileOperation::Sync,
1495 FileOperation::Close,
1496 ]
1497 );
1498 }
1499
1500 #[fuchsia::test]
1501 async fn test_close_called_when_dropped() {
1502 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1503 let _ = env.proxy.sync().await;
1504 std::mem::drop(env.proxy);
1505 env.scope.shutdown();
1506 env.scope.wait().await;
1507 let events = env.file.operations.lock().unwrap();
1508 assert_eq!(
1509 *events,
1510 vec![
1511 FileOperation::Init {
1512 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1513 },
1514 FileOperation::Sync,
1515 FileOperation::Close,
1516 ]
1517 );
1518 }
1519
1520 #[fuchsia::test]
1521 async fn test_describe() {
1522 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1523 let protocol = env.proxy.query().await.unwrap();
1524 assert_eq!(protocol, fio::FILE_PROTOCOL_NAME.as_bytes());
1525 }
1526
1527 #[fuchsia::test]
1528 async fn test_get_attributes() {
1529 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::empty());
1530 let (mutable_attributes, immutable_attributes) = env
1531 .proxy
1532 .get_attributes(fio::NodeAttributesQuery::all())
1533 .await
1534 .unwrap()
1535 .map_err(Status::from_raw)
1536 .unwrap();
1537 let expected = attributes!(
1538 fio::NodeAttributesQuery::all(),
1539 Mutable {
1540 creation_time: MOCK_FILE_CREATION_TIME,
1541 modification_time: MOCK_FILE_MODIFICATION_TIME,
1542 },
1543 Immutable {
1544 protocols: fio::NodeProtocolKinds::FILE,
1545 abilities: fio::Operations::GET_ATTRIBUTES
1546 | fio::Operations::UPDATE_ATTRIBUTES
1547 | fio::Operations::READ_BYTES
1548 | fio::Operations::WRITE_BYTES,
1549 content_size: MOCK_FILE_SIZE,
1550 storage_size: 2 * MOCK_FILE_SIZE,
1551 link_count: MOCK_FILE_LINKS,
1552 id: MOCK_FILE_ID,
1553 }
1554 );
1555 assert_eq!(mutable_attributes, expected.mutable_attributes);
1556 assert_eq!(immutable_attributes, expected.immutable_attributes);
1557
1558 let events = env.file.operations.lock().unwrap();
1559 assert_eq!(
1560 *events,
1561 vec![
1562 FileOperation::Init {
1563 options: FileOptions {
1564 rights: fio::Operations::GET_ATTRIBUTES,
1565 is_append: false,
1566 is_linkable: true
1567 }
1568 },
1569 FileOperation::GetAttributes { query: fio::NodeAttributesQuery::all() }
1570 ]
1571 );
1572 }
1573
1574 #[fuchsia::test]
1575 async fn test_getbuffer() {
1576 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1577 let result = env
1578 .proxy
1579 .get_backing_memory(fio::VmoFlags::READ)
1580 .await
1581 .unwrap()
1582 .map_err(Status::from_raw);
1583 assert_eq!(result, Err(Status::NOT_SUPPORTED));
1584 let events = env.file.operations.lock().unwrap();
1585 assert_eq!(
1586 *events,
1587 vec![
1588 FileOperation::Init {
1589 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1590 },
1591 #[cfg(target_os = "fuchsia")]
1592 FileOperation::GetBackingMemory { flags: fio::VmoFlags::READ },
1593 ]
1594 );
1595 }
1596
1597 #[fuchsia::test]
1598 async fn test_getbuffer_no_perms() {
1599 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::empty());
1600 let result = env
1601 .proxy
1602 .get_backing_memory(fio::VmoFlags::READ)
1603 .await
1604 .unwrap()
1605 .map_err(Status::from_raw);
1606 #[cfg(target_os = "fuchsia")]
1608 assert_eq!(result, Err(Status::ACCESS_DENIED));
1609 #[cfg(not(target_os = "fuchsia"))]
1610 assert_eq!(result, Err(Status::NOT_SUPPORTED));
1611 let events = env.file.operations.lock().unwrap();
1612 assert_eq!(
1613 *events,
1614 vec![FileOperation::Init {
1615 options: FileOptions {
1616 rights: fio::Operations::GET_ATTRIBUTES,
1617 is_append: false,
1618 is_linkable: true
1619 }
1620 },]
1621 );
1622 }
1623
1624 #[fuchsia::test]
1625 async fn test_getbuffer_vmo_exec_requires_right_executable() {
1626 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1627 let result = env
1628 .proxy
1629 .get_backing_memory(fio::VmoFlags::EXECUTE)
1630 .await
1631 .unwrap()
1632 .map_err(Status::from_raw);
1633 #[cfg(target_os = "fuchsia")]
1635 assert_eq!(result, Err(Status::ACCESS_DENIED));
1636 #[cfg(not(target_os = "fuchsia"))]
1637 assert_eq!(result, Err(Status::NOT_SUPPORTED));
1638 let events = env.file.operations.lock().unwrap();
1639 assert_eq!(
1640 *events,
1641 vec![FileOperation::Init {
1642 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1643 },]
1644 );
1645 }
1646
1647 #[fuchsia::test]
1648 async fn test_deprecated_get_flags() {
1649 let env = init_mock_file(
1650 Box::new(always_succeed_callback),
1651 fio::OpenFlags::RIGHT_READABLE
1652 | fio::OpenFlags::RIGHT_WRITABLE
1653 | fio::OpenFlags::TRUNCATE,
1654 );
1655 let (status, flags) = env.proxy.deprecated_get_flags().await.unwrap();
1656 assert_eq!(Status::from_raw(status), Status::OK);
1657 assert_eq!(flags, fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
1659 let events = env.file.operations.lock().unwrap();
1660 assert_eq!(
1661 *events,
1662 vec![
1663 FileOperation::Init {
1664 options: FileOptions { rights: RIGHTS_RW, is_append: false, is_linkable: true }
1665 },
1666 FileOperation::Truncate { length: 0 }
1667 ]
1668 );
1669 }
1670
1671 #[fuchsia::test]
1672 async fn test_open_flag_describe() {
1673 let env = init_mock_file(
1674 Box::new(always_succeed_callback),
1675 fio::OpenFlags::RIGHT_READABLE
1676 | fio::OpenFlags::RIGHT_WRITABLE
1677 | fio::OpenFlags::DESCRIBE,
1678 );
1679 let event = env.proxy.take_event_stream().try_next().await.unwrap();
1680 match event {
1681 Some(fio::FileEvent::OnOpen_ { s, info: Some(boxed) }) => {
1682 assert_eq!(Status::from_raw(s), Status::OK);
1683 assert_eq!(
1684 *boxed,
1685 fio::NodeInfoDeprecated::File(fio::FileObject { event: None, stream: None })
1686 );
1687 }
1688 Some(fio::FileEvent::OnRepresentation { payload }) => {
1689 assert_eq!(payload, fio::Representation::File(fio::FileInfo::default()));
1690 }
1691 e => panic!("Expected OnOpen event with fio::NodeInfoDeprecated::File, got {:?}", e),
1692 }
1693 let events = env.file.operations.lock().unwrap();
1694 assert_eq!(
1695 *events,
1696 vec![FileOperation::Init {
1697 options: FileOptions { rights: RIGHTS_RW, is_append: false, is_linkable: true },
1698 }]
1699 );
1700 }
1701
1702 #[fuchsia::test]
1703 async fn test_read_succeeds() {
1704 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1705 let data = env.proxy.read(10).await.unwrap().map_err(Status::from_raw).unwrap();
1706 assert_eq!(data, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1707
1708 let events = env.file.operations.lock().unwrap();
1709 assert_eq!(
1710 *events,
1711 vec![
1712 FileOperation::Init {
1713 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1714 },
1715 FileOperation::ReadAt { offset: 0, count: 10 },
1716 ]
1717 );
1718 }
1719
1720 #[fuchsia::test]
1721 async fn test_read_not_readable() {
1722 let env = init_mock_file(Box::new(only_allow_init), fio::OpenFlags::RIGHT_WRITABLE);
1723 let result = env.proxy.read(10).await.unwrap().map_err(Status::from_raw);
1724 assert_eq!(result, Err(Status::BAD_HANDLE));
1725 }
1726
1727 #[fuchsia::test]
1728 async fn test_read_validates_count() {
1729 let env = init_mock_file(Box::new(only_allow_init), fio::OpenFlags::RIGHT_READABLE);
1730 let result =
1731 env.proxy.read(fio::MAX_TRANSFER_SIZE + 1).await.unwrap().map_err(Status::from_raw);
1732 assert_eq!(result, Err(Status::OUT_OF_RANGE));
1733 }
1734
1735 #[fuchsia::test]
1736 async fn test_read_at_succeeds() {
1737 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1738 let data = env.proxy.read_at(5, 10).await.unwrap().map_err(Status::from_raw).unwrap();
1739 assert_eq!(data, vec![10, 11, 12, 13, 14]);
1740
1741 let events = env.file.operations.lock().unwrap();
1742 assert_eq!(
1743 *events,
1744 vec![
1745 FileOperation::Init {
1746 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1747 },
1748 FileOperation::ReadAt { offset: 10, count: 5 },
1749 ]
1750 );
1751 }
1752
1753 #[fuchsia::test]
1754 async fn test_read_at_validates_count() {
1755 let env = init_mock_file(Box::new(only_allow_init), fio::OpenFlags::RIGHT_READABLE);
1756 let result = env
1757 .proxy
1758 .read_at(fio::MAX_TRANSFER_SIZE + 1, 0)
1759 .await
1760 .unwrap()
1761 .map_err(Status::from_raw);
1762 assert_eq!(result, Err(Status::OUT_OF_RANGE));
1763 }
1764
1765 #[fuchsia::test]
1766 async fn test_seek_start() {
1767 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1768 let offset = env
1769 .proxy
1770 .seek(fio::SeekOrigin::Start, 10)
1771 .await
1772 .unwrap()
1773 .map_err(Status::from_raw)
1774 .unwrap();
1775 assert_eq!(offset, 10);
1776
1777 let data = env.proxy.read(1).await.unwrap().map_err(Status::from_raw).unwrap();
1778 assert_eq!(data, vec![10]);
1779 let events = env.file.operations.lock().unwrap();
1780 assert_eq!(
1781 *events,
1782 vec![
1783 FileOperation::Init {
1784 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1785 },
1786 FileOperation::ReadAt { offset: 10, count: 1 },
1787 ]
1788 );
1789 }
1790
1791 #[fuchsia::test]
1792 async fn test_seek_cur() {
1793 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1794 let offset = env
1795 .proxy
1796 .seek(fio::SeekOrigin::Start, 10)
1797 .await
1798 .unwrap()
1799 .map_err(Status::from_raw)
1800 .unwrap();
1801 assert_eq!(offset, 10);
1802
1803 let offset = env
1804 .proxy
1805 .seek(fio::SeekOrigin::Current, -2)
1806 .await
1807 .unwrap()
1808 .map_err(Status::from_raw)
1809 .unwrap();
1810 assert_eq!(offset, 8);
1811
1812 let data = env.proxy.read(1).await.unwrap().map_err(Status::from_raw).unwrap();
1813 assert_eq!(data, vec![8]);
1814 let events = env.file.operations.lock().unwrap();
1815 assert_eq!(
1816 *events,
1817 vec![
1818 FileOperation::Init {
1819 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1820 },
1821 FileOperation::ReadAt { offset: 8, count: 1 },
1822 ]
1823 );
1824 }
1825
1826 #[fuchsia::test]
1827 async fn test_seek_before_start() {
1828 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1829 let result =
1830 env.proxy.seek(fio::SeekOrigin::Current, -4).await.unwrap().map_err(Status::from_raw);
1831 assert_eq!(result, Err(Status::OUT_OF_RANGE));
1832 }
1833
1834 #[fuchsia::test]
1835 async fn test_seek_end() {
1836 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1837 let offset = env
1838 .proxy
1839 .seek(fio::SeekOrigin::End, -4)
1840 .await
1841 .unwrap()
1842 .map_err(Status::from_raw)
1843 .unwrap();
1844 assert_eq!(offset, MOCK_FILE_SIZE - 4);
1845
1846 let data = env.proxy.read(1).await.unwrap().map_err(Status::from_raw).unwrap();
1847 assert_eq!(data, vec![(offset % 256) as u8]);
1848 let events = env.file.operations.lock().unwrap();
1849 assert_eq!(
1850 *events,
1851 vec![
1852 FileOperation::Init {
1853 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1854 },
1855 FileOperation::GetSize, FileOperation::ReadAt { offset, count: 1 },
1857 ]
1858 );
1859 }
1860
1861 #[fuchsia::test]
1862 async fn test_update_attributes() {
1863 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_WRITABLE);
1864 let attributes = fio::MutableNodeAttributes {
1865 creation_time: Some(40000),
1866 modification_time: Some(100000),
1867 mode: Some(1),
1868 ..Default::default()
1869 };
1870 let () = env
1871 .proxy
1872 .update_attributes(&attributes)
1873 .await
1874 .unwrap()
1875 .map_err(Status::from_raw)
1876 .unwrap();
1877
1878 let events = env.file.operations.lock().unwrap();
1879 assert_eq!(
1880 *events,
1881 vec![
1882 FileOperation::Init {
1883 options: FileOptions { rights: RIGHTS_W, is_append: false, is_linkable: true }
1884 },
1885 FileOperation::UpdateAttributes { attrs: attributes },
1886 ]
1887 );
1888 }
1889
1890 #[fuchsia::test]
1891 async fn test_deprecated_set_flags() {
1892 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_WRITABLE);
1893 let status = env.proxy.deprecated_set_flags(fio::OpenFlags::APPEND).await.unwrap();
1894 assert_eq!(Status::from_raw(status), Status::OK);
1895 let (status, flags) = env.proxy.deprecated_get_flags().await.unwrap();
1896 assert_eq!(Status::from_raw(status), Status::OK);
1897 assert_eq!(flags, fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::APPEND);
1898 }
1899
1900 #[fuchsia::test]
1901 async fn test_sync() {
1902 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::empty());
1903 let () = env.proxy.sync().await.unwrap().map_err(Status::from_raw).unwrap();
1904 let events = env.file.operations.lock().unwrap();
1905 assert_eq!(
1906 *events,
1907 vec![
1908 FileOperation::Init {
1909 options: FileOptions {
1910 rights: fio::Operations::GET_ATTRIBUTES,
1911 is_append: false,
1912 is_linkable: true
1913 }
1914 },
1915 FileOperation::Sync
1916 ]
1917 );
1918 }
1919
1920 #[fuchsia::test]
1921 async fn test_resize() {
1922 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_WRITABLE);
1923 let () = env.proxy.resize(10).await.unwrap().map_err(Status::from_raw).unwrap();
1924 let events = env.file.operations.lock().unwrap();
1925 assert_matches!(
1926 &events[..],
1927 [
1928 FileOperation::Init {
1929 options: FileOptions { rights: RIGHTS_W, is_append: false, is_linkable: true }
1930 },
1931 FileOperation::Truncate { length: 10 },
1932 ]
1933 );
1934 }
1935
1936 #[fuchsia::test]
1937 async fn test_resize_no_perms() {
1938 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1939 let result = env.proxy.resize(10).await.unwrap().map_err(Status::from_raw);
1940 assert_eq!(result, Err(Status::BAD_HANDLE));
1941 let events = env.file.operations.lock().unwrap();
1942 assert_eq!(
1943 *events,
1944 vec![FileOperation::Init {
1945 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1946 },]
1947 );
1948 }
1949
1950 #[fuchsia::test]
1951 async fn test_write() {
1952 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_WRITABLE);
1953 let data = "Hello, world!".as_bytes();
1954 let count = env.proxy.write(data).await.unwrap().map_err(Status::from_raw).unwrap();
1955 assert_eq!(count, data.len() as u64);
1956 let events = env.file.operations.lock().unwrap();
1957 assert_matches!(
1958 &events[..],
1959 [
1960 FileOperation::Init {
1961 options: FileOptions { rights: RIGHTS_W, is_append: false, is_linkable: true }
1962 },
1963 FileOperation::WriteAt { offset: 0, .. },
1964 ]
1965 );
1966 if let FileOperation::WriteAt { content, .. } = &events[1] {
1967 assert_eq!(content.as_slice(), data);
1968 } else {
1969 unreachable!();
1970 }
1971 }
1972
1973 #[fuchsia::test]
1974 async fn test_write_no_perms() {
1975 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_READABLE);
1976 let data = "Hello, world!".as_bytes();
1977 let result = env.proxy.write(data).await.unwrap().map_err(Status::from_raw);
1978 assert_eq!(result, Err(Status::BAD_HANDLE));
1979 let events = env.file.operations.lock().unwrap();
1980 assert_eq!(
1981 *events,
1982 vec![FileOperation::Init {
1983 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
1984 },]
1985 );
1986 }
1987
1988 #[fuchsia::test]
1989 async fn test_write_at() {
1990 let env = init_mock_file(Box::new(always_succeed_callback), fio::OpenFlags::RIGHT_WRITABLE);
1991 let data = "Hello, world!".as_bytes();
1992 let count = env.proxy.write_at(data, 10).await.unwrap().map_err(Status::from_raw).unwrap();
1993 assert_eq!(count, data.len() as u64);
1994 let events = env.file.operations.lock().unwrap();
1995 assert_matches!(
1996 &events[..],
1997 [
1998 FileOperation::Init {
1999 options: FileOptions { rights: RIGHTS_W, is_append: false, is_linkable: true }
2000 },
2001 FileOperation::WriteAt { offset: 10, .. },
2002 ]
2003 );
2004 if let FileOperation::WriteAt { content, .. } = &events[1] {
2005 assert_eq!(content.as_slice(), data);
2006 } else {
2007 unreachable!();
2008 }
2009 }
2010
2011 #[fuchsia::test]
2012 async fn test_append() {
2013 let env = init_mock_file(
2014 Box::new(always_succeed_callback),
2015 fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::APPEND,
2016 );
2017 let data = "Hello, world!".as_bytes();
2018 let count = env.proxy.write(data).await.unwrap().map_err(Status::from_raw).unwrap();
2019 assert_eq!(count, data.len() as u64);
2020 let offset = env
2021 .proxy
2022 .seek(fio::SeekOrigin::Current, 0)
2023 .await
2024 .unwrap()
2025 .map_err(Status::from_raw)
2026 .unwrap();
2027 assert_eq!(offset, MOCK_FILE_SIZE + data.len() as u64);
2028 let events = env.file.operations.lock().unwrap();
2029 assert_matches!(
2030 &events[..],
2031 [
2032 FileOperation::Init {
2033 options: FileOptions { rights: RIGHTS_W, is_append: true, .. }
2034 },
2035 FileOperation::Append { .. }
2036 ]
2037 );
2038 if let FileOperation::Append { content } = &events[1] {
2039 assert_eq!(content.as_slice(), data);
2040 } else {
2041 unreachable!();
2042 }
2043 }
2044
2045 #[cfg(target_os = "fuchsia")]
2046 mod stream_tests {
2047 use super::*;
2048
2049 fn init_mock_stream_file(vmo: zx::Vmo, flags: fio::OpenFlags) -> TestEnv {
2050 let file = MockFile::new_with_vmo(Box::new(always_succeed_callback), vmo);
2051 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::FileMarker>();
2052
2053 let scope = ExecutionScope::new();
2054
2055 let cloned_file = file.clone();
2056 let cloned_scope = scope.clone();
2057
2058 flags.to_object_request(server_end).create_connection_sync::<StreamIoConnection<_>, _>(
2059 cloned_scope,
2060 cloned_file,
2061 flags,
2062 );
2063
2064 TestEnv { file, proxy, scope }
2065 }
2066
2067 #[fuchsia::test]
2068 async fn test_stream_describe() {
2069 const VMO_CONTENTS: &[u8] = b"hello there";
2070 let vmo = zx::Vmo::create(VMO_CONTENTS.len() as u64).unwrap();
2071 vmo.write(VMO_CONTENTS, 0).unwrap();
2072 let flags = fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE;
2073 let env = init_mock_stream_file(vmo, flags);
2074
2075 let fio::FileInfo { stream: Some(stream), .. } = env.proxy.describe().await.unwrap()
2076 else {
2077 panic!("Missing stream")
2078 };
2079 let contents =
2080 stream.read_to_vec(zx::StreamReadOptions::empty(), 20).expect("read failed");
2081 assert_eq!(contents, VMO_CONTENTS);
2082 }
2083
2084 #[fuchsia::test]
2085 async fn test_stream_read() {
2086 let vmo_contents = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
2087 let vmo = zx::Vmo::create(vmo_contents.len() as u64).unwrap();
2088 vmo.write(&vmo_contents, 0).unwrap();
2089 let flags = fio::OpenFlags::RIGHT_READABLE;
2090 let env = init_mock_stream_file(vmo, flags);
2091
2092 let data = env
2093 .proxy
2094 .read(vmo_contents.len() as u64)
2095 .await
2096 .unwrap()
2097 .map_err(Status::from_raw)
2098 .unwrap();
2099 assert_eq!(data, vmo_contents);
2100
2101 let events = env.file.operations.lock().unwrap();
2102 assert_eq!(
2103 *events,
2104 [FileOperation::Init {
2105 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
2106 },]
2107 );
2108 }
2109
2110 #[fuchsia::test]
2111 async fn test_stream_read_at() {
2112 let vmo_contents = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
2113 let vmo = zx::Vmo::create(vmo_contents.len() as u64).unwrap();
2114 vmo.write(&vmo_contents, 0).unwrap();
2115 let flags = fio::OpenFlags::RIGHT_READABLE;
2116 let env = init_mock_stream_file(vmo, flags);
2117
2118 const OFFSET: u64 = 4;
2119 let data = env
2120 .proxy
2121 .read_at((vmo_contents.len() as u64) - OFFSET, OFFSET)
2122 .await
2123 .unwrap()
2124 .map_err(Status::from_raw)
2125 .unwrap();
2126 assert_eq!(data, vmo_contents[OFFSET as usize..]);
2127
2128 let events = env.file.operations.lock().unwrap();
2129 assert_eq!(
2130 *events,
2131 [FileOperation::Init {
2132 options: FileOptions { rights: RIGHTS_R, is_append: false, is_linkable: true }
2133 },]
2134 );
2135 }
2136
2137 #[fuchsia::test]
2138 async fn test_stream_write() {
2139 const DATA_SIZE: u64 = 10;
2140 let vmo = zx::Vmo::create(DATA_SIZE).unwrap();
2141 let flags = fio::OpenFlags::RIGHT_WRITABLE;
2142 let env = init_mock_stream_file(
2143 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2144 flags,
2145 );
2146
2147 let data: [u8; DATA_SIZE as usize] = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
2148 let written = env.proxy.write(&data).await.unwrap().map_err(Status::from_raw).unwrap();
2149 assert_eq!(written, DATA_SIZE);
2150 let mut vmo_contents = [0; DATA_SIZE as usize];
2151 vmo.read(&mut vmo_contents, 0).unwrap();
2152 assert_eq!(vmo_contents, data);
2153
2154 let events = env.file.operations.lock().unwrap();
2155 assert_eq!(
2156 *events,
2157 [FileOperation::Init {
2158 options: FileOptions { rights: RIGHTS_W, is_append: false, is_linkable: true }
2159 },]
2160 );
2161 }
2162
2163 #[fuchsia::test]
2164 async fn test_stream_write_at() {
2165 const OFFSET: u64 = 4;
2166 const DATA_SIZE: u64 = 10;
2167 let vmo = zx::Vmo::create(DATA_SIZE + OFFSET).unwrap();
2168 let flags = fio::OpenFlags::RIGHT_WRITABLE;
2169 let env = init_mock_stream_file(
2170 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2171 flags,
2172 );
2173
2174 let data: [u8; DATA_SIZE as usize] = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
2175 let written =
2176 env.proxy.write_at(&data, OFFSET).await.unwrap().map_err(Status::from_raw).unwrap();
2177 assert_eq!(written, DATA_SIZE);
2178 let mut vmo_contents = [0; DATA_SIZE as usize];
2179 vmo.read(&mut vmo_contents, OFFSET).unwrap();
2180 assert_eq!(vmo_contents, data);
2181
2182 let events = env.file.operations.lock().unwrap();
2183 assert_eq!(
2184 *events,
2185 [FileOperation::Init {
2186 options: FileOptions { rights: RIGHTS_W, is_append: false, is_linkable: true }
2187 }]
2188 );
2189 }
2190
2191 #[fuchsia::test]
2192 async fn test_stream_seek() {
2193 let vmo_contents = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
2194 let vmo = zx::Vmo::create(vmo_contents.len() as u64).unwrap();
2195 vmo.write(&vmo_contents, 0).unwrap();
2196 let flags = fio::OpenFlags::RIGHT_READABLE;
2197 let env = init_mock_stream_file(vmo, flags);
2198
2199 let position = env
2200 .proxy
2201 .seek(fio::SeekOrigin::Start, 8)
2202 .await
2203 .unwrap()
2204 .map_err(Status::from_raw)
2205 .unwrap();
2206 assert_eq!(position, 8);
2207 let data = env.proxy.read(2).await.unwrap().map_err(Status::from_raw).unwrap();
2208 assert_eq!(data, [1, 0]);
2209
2210 let position = env
2211 .proxy
2212 .seek(fio::SeekOrigin::Current, -4)
2213 .await
2214 .unwrap()
2215 .map_err(Status::from_raw)
2216 .unwrap();
2217 assert_eq!(position, 6);
2219 let data = env.proxy.read(2).await.unwrap().map_err(Status::from_raw).unwrap();
2220 assert_eq!(data, [3, 2]);
2221
2222 let position = env
2223 .proxy
2224 .seek(fio::SeekOrigin::End, -6)
2225 .await
2226 .unwrap()
2227 .map_err(Status::from_raw)
2228 .unwrap();
2229 assert_eq!(position, 4);
2230 let data = env.proxy.read(2).await.unwrap().map_err(Status::from_raw).unwrap();
2231 assert_eq!(data, [5, 4]);
2232
2233 let e = env
2234 .proxy
2235 .seek(fio::SeekOrigin::Start, -1)
2236 .await
2237 .unwrap()
2238 .map_err(Status::from_raw)
2239 .expect_err("Seeking before the start of a file should be an error");
2240 assert_eq!(e, Status::INVALID_ARGS);
2241 }
2242
2243 #[fuchsia::test]
2244 async fn test_stream_deprecated_set_flags() {
2245 let data = [0, 1, 2, 3, 4];
2246 let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 100).unwrap();
2247 let flags = fio::OpenFlags::RIGHT_WRITABLE;
2248 let env = init_mock_stream_file(
2249 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
2250 flags,
2251 );
2252
2253 let written = env.proxy.write(&data).await.unwrap().map_err(Status::from_raw).unwrap();
2254 assert_eq!(written, data.len() as u64);
2255 assert_eq!(vmo.get_content_size().unwrap(), 100);
2257
2258 zx::ok(env.proxy.deprecated_set_flags(fio::OpenFlags::APPEND).await.unwrap()).unwrap();
2260 env.proxy
2261 .seek(fio::SeekOrigin::Start, 0)
2262 .await
2263 .unwrap()
2264 .map_err(Status::from_raw)
2265 .unwrap();
2266 let written = env.proxy.write(&data).await.unwrap().map_err(Status::from_raw).unwrap();
2267 assert_eq!(written, data.len() as u64);
2268 assert_eq!(vmo.get_content_size().unwrap(), 105);
2270
2271 zx::ok(env.proxy.deprecated_set_flags(fio::OpenFlags::empty()).await.unwrap()).unwrap();
2273 env.proxy
2274 .seek(fio::SeekOrigin::Start, 0)
2275 .await
2276 .unwrap()
2277 .map_err(Status::from_raw)
2278 .unwrap();
2279 let written = env.proxy.write(&data).await.unwrap().map_err(Status::from_raw).unwrap();
2280 assert_eq!(written, data.len() as u64);
2281 assert_eq!(vmo.get_content_size().unwrap(), 105);
2283 }
2284
2285 #[fuchsia::test]
2286 async fn test_stream_read_validates_count() {
2287 let vmo = zx::Vmo::create(10).unwrap();
2288 let flags = fio::OpenFlags::RIGHT_READABLE;
2289 let env = init_mock_stream_file(vmo, flags);
2290 let result =
2291 env.proxy.read(fio::MAX_TRANSFER_SIZE + 1).await.unwrap().map_err(Status::from_raw);
2292 assert_eq!(result, Err(Status::OUT_OF_RANGE));
2293 }
2294
2295 #[fuchsia::test]
2296 async fn test_stream_read_at_validates_count() {
2297 let vmo = zx::Vmo::create(10).unwrap();
2298 let flags = fio::OpenFlags::RIGHT_READABLE;
2299 let env = init_mock_stream_file(vmo, flags);
2300 let result = env
2301 .proxy
2302 .read_at(fio::MAX_TRANSFER_SIZE + 1, 0)
2303 .await
2304 .unwrap()
2305 .map_err(Status::from_raw);
2306 assert_eq!(result, Err(Status::OUT_OF_RANGE));
2307 }
2308 }
2309}