1use crate::node::{CloseError, OpenError};
8use fidl::{persist, unpersist, Persistable};
9use flex_fuchsia_io as fio;
10use thiserror::Error;
11
12mod async_reader;
13pub use async_reader::AsyncReader;
14
15mod async_read_at;
16pub use async_read_at::{Adapter, AsyncFile, AsyncGetSize, AsyncGetSizeExt, AsyncReadAt};
17mod async_read_at_ext;
18pub use async_read_at_ext::AsyncReadAtExt;
19mod buffered_async_read_at;
20pub use buffered_async_read_at::BufferedAsyncReadAt;
21
22#[cfg(target_os = "fuchsia")]
23#[cfg(not(feature = "fdomain"))]
24pub use fuchsia::*;
25
26#[cfg(target_os = "fuchsia")]
27#[cfg(not(feature = "fdomain"))]
28mod fuchsia {
29 use super::*;
30 use crate::node::{take_on_open_event, Kind};
31
32 #[derive(Debug, Error)]
34 #[error("error reading '{path}': {source}")]
35 pub struct ReadNamedError {
36 pub(super) path: String,
37
38 #[source]
39 pub(super) source: ReadError,
40 }
41
42 impl ReadNamedError {
43 pub fn path(&self) -> &str {
45 &self.path
46 }
47
48 pub fn into_inner(self) -> ReadError {
50 self.source
51 }
52
53 pub fn is_not_found_error(&self) -> bool {
55 self.source.is_not_found_error()
56 }
57 }
58
59 #[derive(Debug, Error)]
61 #[error("error writing '{path}': {source}")]
62 pub struct WriteNamedError {
63 pub(super) path: String,
64
65 #[source]
66 pub(super) source: WriteError,
67 }
68
69 impl WriteNamedError {
70 pub fn path(&self) -> &str {
72 &self.path
73 }
74
75 pub fn into_inner(self) -> WriteError {
77 self.source
78 }
79 }
80
81 pub fn open_in_namespace(path: &str, flags: fio::Flags) -> Result<fio::FileProxy, OpenError> {
91 let (node, request) = fidl::endpoints::create_proxy();
92 open_channel_in_namespace(path, flags, request)?;
93 Ok(node)
94 }
95
96 pub fn open_channel_in_namespace(
106 path: &str,
107 flags: fio::Flags,
108 request: fidl::endpoints::ServerEnd<fio::FileMarker>,
109 ) -> Result<(), OpenError> {
110 let flags = flags | fio::Flags::PROTOCOL_FILE;
111 let namespace = fdio::Namespace::installed().map_err(OpenError::Namespace)?;
112 namespace.open(path, flags, request.into_channel()).map_err(OpenError::Namespace)
113 }
114
115 pub async fn write_in_namespace<D>(path: &str, data: D) -> Result<(), WriteNamedError>
120 where
121 D: AsRef<[u8]>,
122 {
123 async {
124 let flags =
125 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::FILE_TRUNCATE | fio::PERM_WRITABLE;
126 let file = open_in_namespace(path, flags)?;
127
128 write(&file, data).await?;
129
130 let _ = close(file).await;
131 Ok(())
132 }
133 .await
134 .map_err(|source| WriteNamedError { path: path.to_owned(), source })
135 }
136
137 pub async fn write_fidl_in_namespace<T: Persistable>(
142 path: &str,
143 data: &mut T,
144 ) -> Result<(), WriteNamedError> {
145 let data = persist(data)
146 .map_err(|source| WriteNamedError { path: path.to_owned(), source: source.into() })?;
147 write_in_namespace(path, data).await?;
148 Ok(())
149 }
150
151 pub async fn read_in_namespace(path: &str) -> Result<Vec<u8>, ReadNamedError> {
154 async {
155 let file = open_in_namespace(
156 path,
157 fio::Flags::FLAG_SEND_REPRESENTATION
158 | fio::PERM_READABLE
159 | fio::Flags::PROTOCOL_FILE,
160 )?;
161 read_file_with_on_open_event(file).await
162 }
163 .await
164 .map_err(|source| ReadNamedError { path: path.to_owned(), source })
165 }
166
167 pub async fn read_in_namespace_to_string(path: &str) -> Result<String, ReadNamedError> {
170 let bytes = read_in_namespace(path).await?;
171 let string = String::from_utf8(bytes)
172 .map_err(|source| ReadNamedError { path: path.to_owned(), source: source.into() })?;
173 Ok(string)
174 }
175
176 pub async fn read_in_namespace_to_fidl<T: Persistable>(
182 path: &str,
183 ) -> Result<T, ReadNamedError> {
184 let bytes = read_in_namespace(path).await?;
185 unpersist(&bytes)
186 .map_err(|source| ReadNamedError { path: path.to_owned(), source: source.into() })
187 }
188
189 pub(super) fn extract_stream_from_on_open_event(
191 event: fio::FileEvent,
192 ) -> Result<Option<zx::Stream>, OpenError> {
193 match event {
194 fio::FileEvent::OnOpen_ { s: status, info } => {
195 zx::Status::ok(status).map_err(OpenError::OpenError)?;
196 let node_info = info.ok_or(OpenError::MissingOnOpenInfo)?;
197 match *node_info {
198 fio::NodeInfoDeprecated::File(file_info) => Ok(file_info.stream),
199 node_info @ _ => Err(OpenError::UnexpectedNodeKind {
200 expected: Kind::File,
201 actual: Kind::kind_of(&node_info),
202 }),
203 }
204 }
205 fio::FileEvent::OnRepresentation { payload } => match payload {
206 fio::Representation::File(file_info) => Ok(file_info.stream),
207 representation @ _ => Err(OpenError::UnexpectedNodeKind {
208 expected: Kind::File,
209 actual: Kind::kind_of2(&representation),
210 }),
211 },
212 fio::FileEvent::_UnknownEvent { ordinal, .. } => {
213 Err(OpenError::UnknownEvent { ordinal })
214 }
215 }
216 }
217
218 pub(super) fn read_contents_of_stream(stream: zx::Stream) -> Result<Vec<u8>, ReadError> {
220 let file_size =
222 stream.seek(std::io::SeekFrom::End(0)).map_err(ReadError::ReadError)? as usize;
223 let mut data = Vec::with_capacity(file_size);
224 let mut remaining = file_size;
225 while remaining > 0 {
226 let actual = stream
230 .read_at_uninit(
231 zx::StreamReadOptions::empty(),
232 data.len() as u64,
233 &mut data.spare_capacity_mut()[0..remaining],
234 )
235 .map_err(ReadError::ReadError)?;
236 if actual == 0 {
239 break;
240 }
241 unsafe { data.set_len(data.len() + actual) };
243 remaining -= actual;
244 }
245 Ok(data)
246 }
247
248 pub(crate) async fn read_file_with_on_open_event(
251 file: fio::FileProxy,
252 ) -> Result<Vec<u8>, ReadError> {
253 let event = take_on_open_event(&file).await.map_err(ReadError::Open)?;
254 let stream = extract_stream_from_on_open_event(event).map_err(ReadError::Open)?;
255
256 if let Some(stream) = stream {
257 read_contents_of_stream(stream)
258 } else {
259 read(&file).await
261 }
262 }
263}
264
265#[derive(Debug, Error)]
267#[allow(missing_docs)]
268pub enum ReadError {
269 #[error("while opening the file: {0:?}")]
270 Open(#[from] OpenError),
271
272 #[error("read call failed: {0:?}")]
273 Fidl(#[from] fidl::Error),
274
275 #[error("read failed with status: {0}")]
276 ReadError(#[source] zx_status::Status),
277
278 #[error("file was not a utf-8 encoded string: {0}")]
279 InvalidUtf8(#[from] std::string::FromUtf8Error),
280}
281
282impl ReadError {
283 pub fn is_not_found_error(&self) -> bool {
285 matches!(self, ReadError::Open(e) if e.is_not_found_error())
286 }
287}
288
289#[derive(Debug, Error)]
291#[allow(missing_docs)]
292pub enum WriteError {
293 #[error("while creating the file: {0}")]
294 Create(#[from] OpenError),
295
296 #[error("write call failed: {0}")]
297 Fidl(#[from] fidl::Error),
298
299 #[error("write failed with status: {0}")]
300 WriteError(#[source] zx_status::Status),
301
302 #[error("file endpoint reported more bytes written than were provided")]
303 Overwrite,
304}
305
306pub async fn close(file: fio::FileProxy) -> Result<(), CloseError> {
308 let result = file.close().await.map_err(CloseError::SendCloseRequest)?;
309 result.map_err(|s| CloseError::CloseError(zx_status::Status::from_raw(s)))
310}
311
312pub async fn write<D>(file: &fio::FileProxy, data: D) -> Result<(), WriteError>
314where
315 D: AsRef<[u8]>,
316{
317 let mut data = data.as_ref();
318
319 while !data.is_empty() {
320 let bytes_written = file
321 .write(&data[..std::cmp::min(fio::MAX_BUF as usize, data.len())])
322 .await?
323 .map_err(|s| WriteError::WriteError(zx_status::Status::from_raw(s)))?;
324
325 if bytes_written > data.len() as u64 {
326 return Err(WriteError::Overwrite);
327 }
328
329 data = &data[bytes_written as usize..];
330 }
331 Ok(())
332}
333
334pub async fn write_fidl<T: Persistable>(
336 file: &fio::FileProxy,
337 data: &mut T,
338) -> Result<(), WriteError> {
339 write(file, persist(data)?).await?;
340 Ok(())
341}
342
343pub async fn read(file: &fio::FileProxy) -> Result<Vec<u8>, ReadError> {
345 let mut out = Vec::new();
346
347 loop {
348 let mut bytes = file
349 .read(fio::MAX_BUF)
350 .await?
351 .map_err(|s| ReadError::ReadError(zx_status::Status::from_raw(s)))?;
352 if bytes.is_empty() {
353 break;
354 }
355 out.append(&mut bytes);
356 }
357 Ok(out)
358}
359
360pub async fn read_num_bytes(file: &fio::FileProxy, num_bytes: u64) -> Result<Vec<u8>, ReadError> {
363 let mut data = vec![];
364
365 let mut bytes_left = num_bytes;
368 while bytes_left > 0 {
369 let bytes_to_read = std::cmp::min(bytes_left, fio::MAX_BUF);
370 let mut bytes = file
371 .read(bytes_to_read)
372 .await?
373 .map_err(|s| ReadError::ReadError(zx_status::Status::from_raw(s)))?;
374
375 if bytes.is_empty() {
376 break;
377 }
378
379 bytes_left -= bytes.len() as u64;
380 data.append(&mut bytes);
381 }
382
383 let num_bytes = num_bytes as usize;
385 if data.len() > num_bytes {
386 data.drain(num_bytes..data.len());
387 }
388
389 Ok(data)
390}
391
392pub async fn read_to_string(file: &fio::FileProxy) -> Result<String, ReadError> {
394 let bytes = read(file).await?;
395 let string = String::from_utf8(bytes)?;
396 Ok(string)
397}
398
399pub async fn read_fidl<T: Persistable>(file: &fio::FileProxy) -> Result<T, ReadError> {
404 let bytes = read(file).await?;
405 Ok(unpersist(&bytes)?)
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411 use crate::directory;
412 use crate::node::{take_on_open_event, Kind};
413 use assert_matches::assert_matches;
414 use fidl_fidl_test_schema::{DataTable1, DataTable2};
415 use fuchsia_async as fasync;
416 use std::path::Path;
417 use std::sync::Arc;
418 use tempfile::TempDir;
419 use vfs::execution_scope::ExecutionScope;
420 use vfs::file::vmo::{read_only, VmoFile};
421 use vfs::ToObjectRequest;
422 use zx::{self as zx, HandleBased as _};
423
424 const DATA_FILE_CONTENTS: &str = "Hello World!\n";
425
426 #[fasync::run_singlethreaded(test)]
429 async fn open_in_namespace_opens_real_file() {
430 let exists = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
431 assert_matches!(close(exists).await, Ok(()));
432 }
433
434 #[fasync::run_singlethreaded(test)]
435 async fn open_in_namespace_opens_fake_file_under_of_root_namespace_entry() {
436 let notfound = open_in_namespace("/pkg/fake", fio::PERM_READABLE).unwrap();
437 assert_matches!(close(notfound).await, Err(_));
439 }
440
441 #[fasync::run_singlethreaded(test)]
442 async fn open_in_namespace_rejects_fake_root_namespace_entry() {
443 assert_matches!(
444 open_in_namespace("/fake", fio::PERM_READABLE),
445 Err(OpenError::Namespace(zx_status::Status::NOT_FOUND))
446 );
447 }
448
449 #[fasync::run_singlethreaded(test)]
452 async fn write_in_namespace_creates_file() {
453 let tempdir = TempDir::new().unwrap();
454 let path = tempdir.path().join(Path::new("new-file")).to_str().unwrap().to_owned();
455
456 let data = b"\x80"; write_in_namespace(&path, data).await.unwrap();
459
460 let contents = std::fs::read(&path).unwrap();
462 assert_eq!(&contents, &data);
463 }
464
465 #[fasync::run_singlethreaded(test)]
466 async fn write_in_namespace_overwrites_existing_file() {
467 let tempdir = TempDir::new().unwrap();
468 let path = tempdir.path().join(Path::new("existing-file")).to_str().unwrap().to_owned();
469
470 let original_data = b"\x80\x81"; write_in_namespace(&path, original_data).await.unwrap();
473
474 let new_data = b"\x82"; write_in_namespace(&path, new_data).await.unwrap();
477
478 let contents = std::fs::read(&path).unwrap();
480 assert_eq!(&contents, &new_data);
481 }
482
483 #[fasync::run_singlethreaded(test)]
484 async fn write_in_namespace_fails_on_invalid_namespace_entry() {
485 assert_matches!(
486 write_in_namespace("/fake", b"").await,
487 Err(WriteNamedError { path, source: WriteError::Create(_) }) if path == "/fake"
488 );
489 let err = write_in_namespace("/fake", b"").await.unwrap_err();
490 assert_eq!(err.path(), "/fake");
491 assert_matches!(err.into_inner(), WriteError::Create(_));
492 }
493
494 #[fasync::run_singlethreaded(test)]
497 async fn write_writes_to_file() {
498 let tempdir = TempDir::new().unwrap();
499 let dir = directory::open_in_namespace(
500 tempdir.path().to_str().unwrap(),
501 fio::PERM_READABLE | fio::PERM_WRITABLE,
502 )
503 .unwrap();
504
505 let file =
507 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
508 .await
509 .unwrap();
510 let data = b"\x80"; write(&file, data).await.unwrap();
512
513 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
515 assert_eq!(&contents, &data);
516 }
517
518 #[fasync::run_singlethreaded(test)]
519 async fn write_writes_to_file_in_chunks_if_needed() {
520 let tempdir = TempDir::new().unwrap();
521 let dir = directory::open_in_namespace(
522 tempdir.path().to_str().unwrap(),
523 fio::PERM_READABLE | fio::PERM_WRITABLE,
524 )
525 .unwrap();
526
527 let file =
529 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
530 .await
531 .unwrap();
532 let data = "abc".repeat(10000);
533 write(&file, &data).await.unwrap();
534
535 let contents = std::fs::read_to_string(tempdir.path().join(Path::new("file"))).unwrap();
537 assert_eq!(&contents, &data);
538 }
539
540 #[fasync::run_singlethreaded(test)]
541 async fn write_appends_to_file() {
542 let tempdir = TempDir::new().unwrap();
543 let dir = directory::open_in_namespace(
544 tempdir.path().to_str().unwrap(),
545 fio::PERM_READABLE | fio::PERM_WRITABLE,
546 )
547 .unwrap();
548
549 let file =
551 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
552 .await
553 .unwrap();
554 write(&file, "Hello ").await.unwrap();
555 write(&file, "World!\n").await.unwrap();
556 close(file).await.unwrap();
557
558 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
560 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
561 }
562
563 #[fasync::run_singlethreaded(test)]
566 async fn read_reads_to_end_of_file() {
567 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
568
569 let contents = read(&file).await.unwrap();
570 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
571 }
572
573 #[fasync::run_singlethreaded(test)]
574 async fn read_reads_from_current_position() {
575 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
576
577 let _: Vec<u8> = file.read(1).await.unwrap().unwrap();
579
580 let contents = read(&file).await.unwrap();
582 assert_eq!(&contents[..], "ello World!\n".as_bytes());
583 }
584
585 #[fasync::run_singlethreaded(test)]
588 async fn read_in_namespace_reads_contents() {
589 let contents = read_in_namespace("/pkg/data/file").await.unwrap();
590 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
591 }
592
593 #[fasync::run_singlethreaded(test)]
594 async fn read_in_namespace_fails_on_invalid_namespace_entry() {
595 assert_matches!(
596 read_in_namespace("/fake").await,
597 Err(ReadNamedError { path, source: ReadError::Open(_) }) if path == "/fake"
598 );
599 let err = read_in_namespace("/fake").await.unwrap_err();
600 assert_eq!(err.path(), "/fake");
601 assert_matches!(err.into_inner(), ReadError::Open(_));
602 }
603
604 #[fasync::run_singlethreaded(test)]
607 async fn read_to_string_reads_data_file() {
608 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
609 assert_eq!(read_to_string(&file).await.unwrap(), DATA_FILE_CONTENTS);
610 }
611
612 #[fasync::run_singlethreaded(test)]
615 async fn read_in_namespace_to_string_reads_data_file() {
616 assert_eq!(
617 read_in_namespace_to_string("/pkg/data/file").await.unwrap(),
618 DATA_FILE_CONTENTS
619 );
620 }
621
622 #[fasync::run_singlethreaded(test)]
625 async fn write_fidl_writes_to_file() {
626 let tempdir = TempDir::new().unwrap();
627 let dir = directory::open_in_namespace(
628 tempdir.path().to_str().unwrap(),
629 fio::PERM_READABLE | fio::PERM_WRITABLE,
630 )
631 .unwrap();
632
633 let file =
635 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
636 .await
637 .unwrap();
638
639 let mut data = DataTable1 {
640 num: Some(42),
641 string: Some(DATA_FILE_CONTENTS.to_string()),
642 ..Default::default()
643 };
644
645 let fidl_bytes = persist(&data).unwrap();
647
648 write_fidl(&file, &mut data).await.unwrap();
649
650 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
652 assert_eq!(&contents, &fidl_bytes);
653 }
654
655 #[fasync::run_singlethreaded(test)]
656 async fn read_fidl_reads_from_file() {
657 let file = open_in_namespace("/pkg/data/fidl_file", fio::PERM_READABLE).unwrap();
658
659 let contents = read_fidl::<DataTable2>(&file).await.unwrap();
660
661 let data = DataTable2 {
662 num: Some(42),
663 string: Some(DATA_FILE_CONTENTS.to_string()),
664 new_field: None,
665 ..Default::default()
666 };
667 assert_eq!(&contents, &data);
668 }
669
670 #[test]
671 fn extract_stream_from_on_open_event_with_stream() {
672 let vmo = zx::Vmo::create(0).unwrap();
673 let stream = zx::Stream::create(zx::StreamOptions::empty(), &vmo, 0).unwrap();
674 let event = fio::FileEvent::OnOpen_ {
675 s: 0,
676 info: Some(Box::new(fio::NodeInfoDeprecated::File(fio::FileObject {
677 stream: Some(stream),
678 event: None,
679 }))),
680 };
681 let stream = extract_stream_from_on_open_event(event)
682 .expect("Not a file")
683 .expect("Stream not present");
684 assert!(!stream.is_invalid_handle());
685 }
686
687 #[test]
688 fn extract_stream_from_on_open_event_without_stream() {
689 let event = fio::FileEvent::OnOpen_ {
690 s: 0,
691 info: Some(Box::new(fio::NodeInfoDeprecated::File(fio::FileObject {
692 stream: None,
693 event: None,
694 }))),
695 };
696 let stream = extract_stream_from_on_open_event(event).expect("Not a file");
697 assert!(stream.is_none());
698 }
699
700 #[test]
701 fn extract_stream_from_on_open_event_with_open_error() {
702 let event = fio::FileEvent::OnOpen_ { s: zx::Status::NOT_FOUND.into_raw(), info: None };
703 let result = extract_stream_from_on_open_event(event);
704 assert_matches!(result, Err(OpenError::OpenError(zx::Status::NOT_FOUND)));
705 }
706
707 #[test]
708 fn extract_stream_from_on_open_event_not_a_file() {
709 let event = fio::FileEvent::OnOpen_ {
710 s: 0,
711 info: Some(Box::new(fio::NodeInfoDeprecated::Service(fio::Service))),
712 };
713 let result = extract_stream_from_on_open_event(event);
714 assert_matches!(
715 result,
716 Err(OpenError::UnexpectedNodeKind { expected: Kind::File, actual: Kind::Service })
717 );
718 }
719
720 #[test]
721 fn extract_stream_from_on_representation_event_with_stream() {
722 let vmo = zx::Vmo::create(0).unwrap();
723 let stream = zx::Stream::create(zx::StreamOptions::empty(), &vmo, 0).unwrap();
724 let event = fio::FileEvent::OnRepresentation {
725 payload: fio::Representation::File(fio::FileInfo {
726 stream: Some(stream),
727 ..Default::default()
728 }),
729 };
730 let stream = extract_stream_from_on_open_event(event)
731 .expect("Not a file")
732 .expect("Stream not present");
733 assert!(!stream.is_invalid_handle());
734 }
735
736 #[test]
737 fn extract_stream_from_on_representation_event_without_stream() {
738 let event = fio::FileEvent::OnRepresentation {
739 payload: fio::Representation::File(fio::FileInfo::default()),
740 };
741 let stream = extract_stream_from_on_open_event(event).expect("Not a file");
742 assert!(stream.is_none());
743 }
744
745 #[test]
746 fn extract_stream_from_on_representation_event_not_a_file() {
747 let event = fio::FileEvent::OnRepresentation {
748 payload: fio::Representation::Directory(Default::default()),
749 };
750 let result = extract_stream_from_on_open_event(event);
751 assert_matches!(
752 result,
753 Err(OpenError::UnexpectedNodeKind { expected: Kind::File, actual: Kind::Directory })
754 );
755 }
756
757 #[test]
758 fn read_contents_of_stream_with_contents() {
759 let data = b"file-contents".repeat(1000);
760 let vmo = zx::Vmo::create(data.len() as u64).unwrap();
761 vmo.write(&data, 0).unwrap();
762 let stream = zx::Stream::create(zx::StreamOptions::MODE_READ, &vmo, 0).unwrap();
763 let contents = read_contents_of_stream(stream).unwrap();
764 assert_eq!(contents, data);
765 }
766
767 #[test]
768 fn read_contents_of_stream_with_empty_stream() {
769 let vmo = zx::Vmo::create(0).unwrap();
770 let stream = zx::Stream::create(zx::StreamOptions::MODE_READ, &vmo, 0).unwrap();
771 let contents = read_contents_of_stream(stream).unwrap();
772 assert!(contents.is_empty());
773 }
774
775 fn serve_file(file: Arc<VmoFile>, flags: fio::Flags) -> fio::FileProxy {
776 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::FileMarker>();
777 flags.to_object_request(server_end).handle(|object_request| {
778 vfs::file::serve(file, ExecutionScope::new(), &flags, object_request)
779 });
780 proxy
781 }
782
783 #[fasync::run_singlethreaded(test)]
784 async fn read_file_with_on_open_event_with_stream() {
785 let data = b"file-contents".repeat(1000);
786 let vmo_file = read_only(&data);
787 const FLAGS: fio::Flags = fio::PERM_READABLE.union(fio::Flags::FLAG_SEND_REPRESENTATION);
788
789 {
790 let file = serve_file(vmo_file.clone(), FLAGS);
792 let event = take_on_open_event(&file).await.unwrap();
793 extract_stream_from_on_open_event(event).unwrap().expect("Stream not present");
794 }
795
796 let file = serve_file(vmo_file.clone(), FLAGS);
797 let contents = read_file_with_on_open_event(file).await.unwrap();
798 assert_eq!(contents, data);
799 }
800
801 #[fasync::run_singlethreaded(test)]
802 async fn read_missing_file_in_namespace() {
803 assert_matches!(
804 read_in_namespace("/pkg/data/missing").await,
805 Err(e) if e.is_not_found_error()
806 );
807 }
808}