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