1use crate::node::{CloseError, OpenError};
8use fidl::{Persistable, persist, unpersist};
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::{Kind, take_on_open_event};
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 #[cfg(fuchsia_api_level_at_least = "HEAD")]
250 pub(super) fn write_to_stream(stream: zx::Stream, contents: &[u8]) -> Result<(), WriteError> {
251 let mut offset: usize = 0;
252 while offset < contents.len() {
253 let actual = stream
254 .write(zx::StreamWriteOptions::empty(), &contents[offset..])
255 .map_err(WriteError::WriteError)?;
256 debug_assert!(actual > 0);
259 offset += actual;
260 }
261 Ok(())
262 }
263
264 pub(crate) async fn read_file_with_on_open_event(
267 file: fio::FileProxy,
268 ) -> Result<Vec<u8>, ReadError> {
269 let event = take_on_open_event(&file).await.map_err(ReadError::Open)?;
270 let stream = extract_stream_from_on_open_event(event).map_err(ReadError::Open)?;
271
272 if let Some(stream) = stream {
273 read_contents_of_stream(stream)
274 } else {
275 read(&file).await
277 }
278 }
279
280 #[cfg(fuchsia_api_level_at_least = "HEAD")]
283 pub(crate) async fn write_file_with_on_open_event(
284 file: &fio::FileProxy,
285 contents: &[u8],
286 ) -> Result<(), WriteError> {
287 let event = take_on_open_event(file).await.map_err(WriteError::Open)?;
288 let stream = extract_stream_from_on_open_event(event).map_err(WriteError::Open)?;
289
290 if let Some(stream) = stream {
291 write_to_stream(stream, contents)
292 } else {
293 write(file, contents).await
295 }
296 }
297}
298
299#[derive(Debug, Error)]
301#[allow(missing_docs)]
302pub enum ReadError {
303 #[error("while opening the file: {0:?}")]
304 Open(#[from] OpenError),
305
306 #[error("read call failed: {0:?}")]
307 Fidl(#[from] fidl::Error),
308
309 #[error("read failed with status: {0}")]
310 ReadError(#[source] zx_status::Status),
311
312 #[error("file was not a utf-8 encoded string: {0}")]
313 InvalidUtf8(#[from] std::string::FromUtf8Error),
314}
315
316impl ReadError {
317 pub fn is_not_found_error(&self) -> bool {
319 matches!(self, ReadError::Open(e) if e.is_not_found_error())
320 }
321}
322
323#[derive(Debug, Error)]
325#[allow(missing_docs)]
326pub enum WriteError {
327 #[error("while opening the file: {0}")]
328 Open(#[from] OpenError),
329
330 #[error("while linking the file: {0}")]
331 Link(#[from] zx_status::Status),
332
333 #[error("while renaming the file: {0}")]
334 #[cfg(fuchsia_api_level_at_least = "HEAD")]
335 Rename(#[from] crate::node::RenameError),
336
337 #[error("write call failed: {0}")]
338 Fidl(#[from] fidl::Error),
339
340 #[error("write failed with status: {0}")]
341 WriteError(#[source] zx_status::Status),
342
343 #[error("file endpoint reported more bytes written than were provided")]
344 Overwrite,
345}
346
347pub async fn close(file: fio::FileProxy) -> Result<(), CloseError> {
349 let result = file.close().await.map_err(CloseError::SendCloseRequest)?;
350 result.map_err(|s| CloseError::CloseError(zx_status::Status::from_raw(s)))
351}
352
353pub async fn write<D>(file: &fio::FileProxy, data: D) -> Result<(), WriteError>
355where
356 D: AsRef<[u8]>,
357{
358 let mut data = data.as_ref();
359
360 while !data.is_empty() {
361 let bytes_written = file
362 .write(&data[..std::cmp::min(fio::MAX_BUF as usize, data.len())])
363 .await?
364 .map_err(|s| WriteError::WriteError(zx_status::Status::from_raw(s)))?;
365
366 if bytes_written > data.len() as u64 {
367 return Err(WriteError::Overwrite);
368 }
369
370 data = &data[bytes_written as usize..];
371 }
372 Ok(())
373}
374
375pub async fn write_fidl<T: Persistable>(
377 file: &fio::FileProxy,
378 data: &mut T,
379) -> Result<(), WriteError> {
380 write(file, persist(data)?).await?;
381 Ok(())
382}
383
384pub async fn read(file: &fio::FileProxy) -> Result<Vec<u8>, ReadError> {
386 let mut out = Vec::new();
387
388 loop {
389 let mut bytes = file
390 .read(fio::MAX_BUF)
391 .await?
392 .map_err(|s| ReadError::ReadError(zx_status::Status::from_raw(s)))?;
393 if bytes.is_empty() {
394 break;
395 }
396 out.append(&mut bytes);
397 }
398 Ok(out)
399}
400
401pub async fn read_num_bytes(file: &fio::FileProxy, num_bytes: u64) -> Result<Vec<u8>, ReadError> {
404 let mut data = vec![];
405
406 let mut bytes_left = num_bytes;
409 while bytes_left > 0 {
410 let bytes_to_read = std::cmp::min(bytes_left, fio::MAX_BUF);
411 let mut bytes = file
412 .read(bytes_to_read)
413 .await?
414 .map_err(|s| ReadError::ReadError(zx_status::Status::from_raw(s)))?;
415
416 if bytes.is_empty() {
417 break;
418 }
419
420 bytes_left -= bytes.len() as u64;
421 data.append(&mut bytes);
422 }
423
424 let num_bytes = num_bytes as usize;
426 if data.len() > num_bytes {
427 data.drain(num_bytes..data.len());
428 }
429
430 Ok(data)
431}
432
433pub async fn read_to_string(file: &fio::FileProxy) -> Result<String, ReadError> {
435 let bytes = read(file).await?;
436 let string = String::from_utf8(bytes)?;
437 Ok(string)
438}
439
440pub async fn read_fidl<T: Persistable>(file: &fio::FileProxy) -> Result<T, ReadError> {
445 let bytes = read(file).await?;
446 Ok(unpersist(&bytes)?)
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452 use crate::directory;
453 use crate::node::{Kind, take_on_open_event};
454 use assert_matches::assert_matches;
455 use fidl_fidl_test_schema::{DataTable1, DataTable2};
456 use fuchsia_async as fasync;
457 use std::path::Path;
458 use std::sync::Arc;
459 use tempfile::TempDir;
460 use vfs::ToObjectRequest;
461 use vfs::execution_scope::ExecutionScope;
462 use vfs::file::vmo::{VmoFile, read_only};
463
464 const DATA_FILE_CONTENTS: &str = "Hello World!\n";
465
466 #[fasync::run_singlethreaded(test)]
469 async fn open_in_namespace_opens_real_file() {
470 let exists = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
471 assert_matches!(close(exists).await, Ok(()));
472 }
473
474 #[fasync::run_singlethreaded(test)]
475 async fn open_in_namespace_opens_fake_file_under_of_root_namespace_entry() {
476 let notfound = open_in_namespace("/pkg/fake", fio::PERM_READABLE).unwrap();
477 assert_matches!(close(notfound).await, Err(_));
479 }
480
481 #[fasync::run_singlethreaded(test)]
482 async fn open_in_namespace_rejects_fake_root_namespace_entry() {
483 assert_matches!(
484 open_in_namespace("/fake", fio::PERM_READABLE),
485 Err(OpenError::Namespace(zx_status::Status::NOT_FOUND))
486 );
487 }
488
489 #[fasync::run_singlethreaded(test)]
492 async fn write_in_namespace_creates_file() {
493 let tempdir = TempDir::new().unwrap();
494 let path = tempdir.path().join(Path::new("new-file")).to_str().unwrap().to_owned();
495
496 let data = b"\x80"; write_in_namespace(&path, data).await.unwrap();
499
500 let contents = std::fs::read(&path).unwrap();
502 assert_eq!(&contents, &data);
503 }
504
505 #[fasync::run_singlethreaded(test)]
506 async fn write_in_namespace_overwrites_existing_file() {
507 let tempdir = TempDir::new().unwrap();
508 let path = tempdir.path().join(Path::new("existing-file")).to_str().unwrap().to_owned();
509
510 let original_data = b"\x80\x81"; write_in_namespace(&path, original_data).await.unwrap();
513
514 let new_data = b"\x82"; write_in_namespace(&path, new_data).await.unwrap();
517
518 let contents = std::fs::read(&path).unwrap();
520 assert_eq!(&contents, &new_data);
521 }
522
523 #[fasync::run_singlethreaded(test)]
524 async fn write_in_namespace_fails_on_invalid_namespace_entry() {
525 assert_matches!(
526 write_in_namespace("/fake", b"").await,
527 Err(WriteNamedError { path, source: WriteError::Open(_) }) if path == "/fake"
528 );
529 let err = write_in_namespace("/fake", b"").await.unwrap_err();
530 assert_eq!(err.path(), "/fake");
531 assert_matches!(err.into_inner(), WriteError::Open(_));
532 }
533
534 #[fasync::run_singlethreaded(test)]
537 async fn write_writes_to_file() {
538 let tempdir = TempDir::new().unwrap();
539 let dir = directory::open_in_namespace(
540 tempdir.path().to_str().unwrap(),
541 fio::PERM_READABLE | fio::PERM_WRITABLE,
542 )
543 .unwrap();
544
545 let file =
547 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
548 .await
549 .unwrap();
550 let data = b"\x80"; write(&file, data).await.unwrap();
552
553 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
555 assert_eq!(&contents, &data);
556 }
557
558 #[fasync::run_singlethreaded(test)]
559 async fn write_writes_to_file_in_chunks_if_needed() {
560 let tempdir = TempDir::new().unwrap();
561 let dir = directory::open_in_namespace(
562 tempdir.path().to_str().unwrap(),
563 fio::PERM_READABLE | fio::PERM_WRITABLE,
564 )
565 .unwrap();
566
567 let file =
569 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
570 .await
571 .unwrap();
572 let data = "abc".repeat(10000);
573 write(&file, &data).await.unwrap();
574
575 let contents = std::fs::read_to_string(tempdir.path().join(Path::new("file"))).unwrap();
577 assert_eq!(&contents, &data);
578 }
579
580 #[fasync::run_singlethreaded(test)]
581 async fn write_appends_to_file() {
582 let tempdir = TempDir::new().unwrap();
583 let dir = directory::open_in_namespace(
584 tempdir.path().to_str().unwrap(),
585 fio::PERM_READABLE | fio::PERM_WRITABLE,
586 )
587 .unwrap();
588
589 let file =
591 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
592 .await
593 .unwrap();
594 write(&file, "Hello ").await.unwrap();
595 write(&file, "World!\n").await.unwrap();
596 close(file).await.unwrap();
597
598 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
600 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
601 }
602
603 #[fasync::run_singlethreaded(test)]
604 async fn write_stream() {
605 let tempdir = TempDir::new().unwrap();
606 let dir = directory::open_in_namespace(
607 tempdir.path().to_str().unwrap(),
608 fio::PERM_READABLE | fio::PERM_WRITABLE,
609 )
610 .unwrap();
611
612 let file =
614 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
615 .await
616 .unwrap();
617 write(&file, "Hello ").await.unwrap();
618 write(&file, "World!\n").await.unwrap();
619 close(file).await.unwrap();
620
621 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
623 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
624 }
625
626 #[fasync::run_singlethreaded(test)]
629 async fn read_reads_to_end_of_file() {
630 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
631
632 let contents = read(&file).await.unwrap();
633 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
634 }
635
636 #[fasync::run_singlethreaded(test)]
637 async fn read_reads_from_current_position() {
638 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
639
640 let _: Vec<u8> = file.read(1).await.unwrap().unwrap();
642
643 let contents = read(&file).await.unwrap();
645 assert_eq!(&contents[..], "ello World!\n".as_bytes());
646 }
647
648 #[fasync::run_singlethreaded(test)]
651 async fn read_in_namespace_reads_contents() {
652 let contents = read_in_namespace("/pkg/data/file").await.unwrap();
653 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
654 }
655
656 #[fasync::run_singlethreaded(test)]
657 async fn read_in_namespace_fails_on_invalid_namespace_entry() {
658 assert_matches!(
659 read_in_namespace("/fake").await,
660 Err(ReadNamedError { path, source: ReadError::Open(_) }) if path == "/fake"
661 );
662 let err = read_in_namespace("/fake").await.unwrap_err();
663 assert_eq!(err.path(), "/fake");
664 assert_matches!(err.into_inner(), ReadError::Open(_));
665 }
666
667 #[fasync::run_singlethreaded(test)]
670 async fn read_to_string_reads_data_file() {
671 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
672 assert_eq!(read_to_string(&file).await.unwrap(), DATA_FILE_CONTENTS);
673 }
674
675 #[fasync::run_singlethreaded(test)]
678 async fn read_in_namespace_to_string_reads_data_file() {
679 assert_eq!(
680 read_in_namespace_to_string("/pkg/data/file").await.unwrap(),
681 DATA_FILE_CONTENTS
682 );
683 }
684
685 #[fasync::run_singlethreaded(test)]
688 async fn write_fidl_writes_to_file() {
689 let tempdir = TempDir::new().unwrap();
690 let dir = directory::open_in_namespace(
691 tempdir.path().to_str().unwrap(),
692 fio::PERM_READABLE | fio::PERM_WRITABLE,
693 )
694 .unwrap();
695
696 let file =
698 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
699 .await
700 .unwrap();
701
702 let mut data = DataTable1 {
703 num: Some(42),
704 string: Some(DATA_FILE_CONTENTS.to_string()),
705 ..Default::default()
706 };
707
708 let fidl_bytes = persist(&data).unwrap();
710
711 write_fidl(&file, &mut data).await.unwrap();
712
713 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
715 assert_eq!(&contents, &fidl_bytes);
716 }
717
718 #[fasync::run_singlethreaded(test)]
719 async fn read_fidl_reads_from_file() {
720 let file = open_in_namespace("/pkg/data/fidl_file", fio::PERM_READABLE).unwrap();
721
722 let contents = read_fidl::<DataTable2>(&file).await.unwrap();
723
724 let data = DataTable2 {
725 num: Some(42),
726 string: Some(DATA_FILE_CONTENTS.to_string()),
727 new_field: None,
728 ..Default::default()
729 };
730 assert_eq!(&contents, &data);
731 }
732
733 #[test]
734 fn extract_stream_from_on_open_event_with_stream() {
735 let vmo = zx::Vmo::create(0).unwrap();
736 let stream = zx::Stream::create(zx::StreamOptions::empty(), &vmo, 0).unwrap();
737 let event = fio::FileEvent::OnOpen_ {
738 s: 0,
739 info: Some(Box::new(fio::NodeInfoDeprecated::File(fio::FileObject {
740 stream: Some(stream),
741 event: None,
742 }))),
743 };
744 let stream = extract_stream_from_on_open_event(event)
745 .expect("Not a file")
746 .expect("Stream not present");
747 assert!(!stream.is_invalid());
748 }
749
750 #[test]
751 fn extract_stream_from_on_open_event_without_stream() {
752 let event = fio::FileEvent::OnOpen_ {
753 s: 0,
754 info: Some(Box::new(fio::NodeInfoDeprecated::File(fio::FileObject {
755 stream: None,
756 event: None,
757 }))),
758 };
759 let stream = extract_stream_from_on_open_event(event).expect("Not a file");
760 assert!(stream.is_none());
761 }
762
763 #[test]
764 fn extract_stream_from_on_open_event_with_open_error() {
765 let event = fio::FileEvent::OnOpen_ { s: zx::Status::NOT_FOUND.into_raw(), info: None };
766 let result = extract_stream_from_on_open_event(event);
767 assert_matches!(result, Err(OpenError::OpenError(zx::Status::NOT_FOUND)));
768 }
769
770 #[test]
771 fn extract_stream_from_on_open_event_not_a_file() {
772 let event = fio::FileEvent::OnOpen_ {
773 s: 0,
774 info: Some(Box::new(fio::NodeInfoDeprecated::Service(fio::Service))),
775 };
776 let result = extract_stream_from_on_open_event(event);
777 assert_matches!(
778 result,
779 Err(OpenError::UnexpectedNodeKind { expected: Kind::File, actual: Kind::Service })
780 );
781 }
782
783 #[test]
784 fn extract_stream_from_on_representation_event_with_stream() {
785 let vmo = zx::Vmo::create(0).unwrap();
786 let stream = zx::Stream::create(zx::StreamOptions::empty(), &vmo, 0).unwrap();
787 let event = fio::FileEvent::OnRepresentation {
788 payload: fio::Representation::File(fio::FileInfo {
789 stream: Some(stream),
790 ..Default::default()
791 }),
792 };
793 let stream = extract_stream_from_on_open_event(event)
794 .expect("Not a file")
795 .expect("Stream not present");
796 assert!(!stream.is_invalid());
797 }
798
799 #[test]
800 fn extract_stream_from_on_representation_event_without_stream() {
801 let event = fio::FileEvent::OnRepresentation {
802 payload: fio::Representation::File(fio::FileInfo::default()),
803 };
804 let stream = extract_stream_from_on_open_event(event).expect("Not a file");
805 assert!(stream.is_none());
806 }
807
808 #[test]
809 fn extract_stream_from_on_representation_event_not_a_file() {
810 let event = fio::FileEvent::OnRepresentation {
811 payload: fio::Representation::Directory(Default::default()),
812 };
813 let result = extract_stream_from_on_open_event(event);
814 assert_matches!(
815 result,
816 Err(OpenError::UnexpectedNodeKind { expected: Kind::File, actual: Kind::Directory })
817 );
818 }
819
820 #[test]
821 fn read_contents_of_stream_with_contents() {
822 let data = b"file-contents".repeat(1000);
823 let vmo = zx::Vmo::create(data.len() as u64).unwrap();
824 vmo.write(&data, 0).unwrap();
825 let stream = zx::Stream::create(zx::StreamOptions::MODE_READ, &vmo, 0).unwrap();
826 let contents = read_contents_of_stream(stream).unwrap();
827 assert_eq!(contents, data);
828 }
829
830 #[test]
831 fn read_contents_of_stream_with_empty_stream() {
832 let vmo = zx::Vmo::create(0).unwrap();
833 let stream = zx::Stream::create(zx::StreamOptions::MODE_READ, &vmo, 0).unwrap();
834 let contents = read_contents_of_stream(stream).unwrap();
835 assert!(contents.is_empty());
836 }
837
838 fn serve_file(file: Arc<VmoFile>, flags: fio::Flags) -> fio::FileProxy {
839 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::FileMarker>();
840 flags.to_object_request(server_end).handle(|object_request| {
841 vfs::file::serve(file, ExecutionScope::new(), &flags, object_request)
842 });
843 proxy
844 }
845
846 #[fasync::run_singlethreaded(test)]
847 async fn read_file_with_on_open_event_with_stream() {
848 let data = b"file-contents".repeat(1000);
849 let vmo_file = read_only(&data);
850 const FLAGS: fio::Flags = fio::PERM_READABLE.union(fio::Flags::FLAG_SEND_REPRESENTATION);
851
852 {
853 let file = serve_file(vmo_file.clone(), FLAGS);
855 let event = take_on_open_event(&file).await.unwrap();
856 extract_stream_from_on_open_event(event).unwrap().expect("Stream not present");
857 }
858
859 let file = serve_file(vmo_file.clone(), FLAGS);
860 let contents = read_file_with_on_open_event(file).await.unwrap();
861 assert_eq!(contents, data);
862 }
863
864 #[fasync::run_singlethreaded(test)]
865 async fn read_missing_file_in_namespace() {
866 assert_matches!(
867 read_in_namespace("/pkg/data/missing").await,
868 Err(e) if e.is_not_found_error()
869 );
870 }
871
872 #[test]
873 fn write_to_stream_with_contents() {
874 let data = b"file-contents".repeat(1000);
875 let vmo = zx::Vmo::create(data.len() as u64).unwrap();
876 let stream = zx::Stream::create(zx::StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
877 write_to_stream(stream, &data).unwrap();
878
879 let mut read_back = vec![0; data.len()];
880 vmo.read(&mut read_back, 0).unwrap();
881 assert_eq!(read_back, data);
882 }
883
884 #[test]
885 fn write_to_stream_empty() {
886 let data = b"";
887 let vmo = zx::Vmo::create(0).unwrap();
888 let stream = zx::Stream::create(zx::StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
889 write_to_stream(stream, data).unwrap();
890 assert_eq!(vmo.get_size().unwrap(), 0);
891 }
892}