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 use zx::{self as zx, HandleBased as _};
464
465 const DATA_FILE_CONTENTS: &str = "Hello World!\n";
466
467 #[fasync::run_singlethreaded(test)]
470 async fn open_in_namespace_opens_real_file() {
471 let exists = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
472 assert_matches!(close(exists).await, Ok(()));
473 }
474
475 #[fasync::run_singlethreaded(test)]
476 async fn open_in_namespace_opens_fake_file_under_of_root_namespace_entry() {
477 let notfound = open_in_namespace("/pkg/fake", fio::PERM_READABLE).unwrap();
478 assert_matches!(close(notfound).await, Err(_));
480 }
481
482 #[fasync::run_singlethreaded(test)]
483 async fn open_in_namespace_rejects_fake_root_namespace_entry() {
484 assert_matches!(
485 open_in_namespace("/fake", fio::PERM_READABLE),
486 Err(OpenError::Namespace(zx_status::Status::NOT_FOUND))
487 );
488 }
489
490 #[fasync::run_singlethreaded(test)]
493 async fn write_in_namespace_creates_file() {
494 let tempdir = TempDir::new().unwrap();
495 let path = tempdir.path().join(Path::new("new-file")).to_str().unwrap().to_owned();
496
497 let data = b"\x80"; write_in_namespace(&path, data).await.unwrap();
500
501 let contents = std::fs::read(&path).unwrap();
503 assert_eq!(&contents, &data);
504 }
505
506 #[fasync::run_singlethreaded(test)]
507 async fn write_in_namespace_overwrites_existing_file() {
508 let tempdir = TempDir::new().unwrap();
509 let path = tempdir.path().join(Path::new("existing-file")).to_str().unwrap().to_owned();
510
511 let original_data = b"\x80\x81"; write_in_namespace(&path, original_data).await.unwrap();
514
515 let new_data = b"\x82"; write_in_namespace(&path, new_data).await.unwrap();
518
519 let contents = std::fs::read(&path).unwrap();
521 assert_eq!(&contents, &new_data);
522 }
523
524 #[fasync::run_singlethreaded(test)]
525 async fn write_in_namespace_fails_on_invalid_namespace_entry() {
526 assert_matches!(
527 write_in_namespace("/fake", b"").await,
528 Err(WriteNamedError { path, source: WriteError::Open(_) }) if path == "/fake"
529 );
530 let err = write_in_namespace("/fake", b"").await.unwrap_err();
531 assert_eq!(err.path(), "/fake");
532 assert_matches!(err.into_inner(), WriteError::Open(_));
533 }
534
535 #[fasync::run_singlethreaded(test)]
538 async fn write_writes_to_file() {
539 let tempdir = TempDir::new().unwrap();
540 let dir = directory::open_in_namespace(
541 tempdir.path().to_str().unwrap(),
542 fio::PERM_READABLE | fio::PERM_WRITABLE,
543 )
544 .unwrap();
545
546 let file =
548 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
549 .await
550 .unwrap();
551 let data = b"\x80"; write(&file, data).await.unwrap();
553
554 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
556 assert_eq!(&contents, &data);
557 }
558
559 #[fasync::run_singlethreaded(test)]
560 async fn write_writes_to_file_in_chunks_if_needed() {
561 let tempdir = TempDir::new().unwrap();
562 let dir = directory::open_in_namespace(
563 tempdir.path().to_str().unwrap(),
564 fio::PERM_READABLE | fio::PERM_WRITABLE,
565 )
566 .unwrap();
567
568 let file =
570 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
571 .await
572 .unwrap();
573 let data = "abc".repeat(10000);
574 write(&file, &data).await.unwrap();
575
576 let contents = std::fs::read_to_string(tempdir.path().join(Path::new("file"))).unwrap();
578 assert_eq!(&contents, &data);
579 }
580
581 #[fasync::run_singlethreaded(test)]
582 async fn write_appends_to_file() {
583 let tempdir = TempDir::new().unwrap();
584 let dir = directory::open_in_namespace(
585 tempdir.path().to_str().unwrap(),
586 fio::PERM_READABLE | fio::PERM_WRITABLE,
587 )
588 .unwrap();
589
590 let file =
592 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
593 .await
594 .unwrap();
595 write(&file, "Hello ").await.unwrap();
596 write(&file, "World!\n").await.unwrap();
597 close(file).await.unwrap();
598
599 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
601 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
602 }
603
604 #[fasync::run_singlethreaded(test)]
605 async fn write_stream() {
606 let tempdir = TempDir::new().unwrap();
607 let dir = directory::open_in_namespace(
608 tempdir.path().to_str().unwrap(),
609 fio::PERM_READABLE | fio::PERM_WRITABLE,
610 )
611 .unwrap();
612
613 let file =
615 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
616 .await
617 .unwrap();
618 write(&file, "Hello ").await.unwrap();
619 write(&file, "World!\n").await.unwrap();
620 close(file).await.unwrap();
621
622 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
624 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
625 }
626
627 #[fasync::run_singlethreaded(test)]
630 async fn read_reads_to_end_of_file() {
631 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
632
633 let contents = read(&file).await.unwrap();
634 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
635 }
636
637 #[fasync::run_singlethreaded(test)]
638 async fn read_reads_from_current_position() {
639 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
640
641 let _: Vec<u8> = file.read(1).await.unwrap().unwrap();
643
644 let contents = read(&file).await.unwrap();
646 assert_eq!(&contents[..], "ello World!\n".as_bytes());
647 }
648
649 #[fasync::run_singlethreaded(test)]
652 async fn read_in_namespace_reads_contents() {
653 let contents = read_in_namespace("/pkg/data/file").await.unwrap();
654 assert_eq!(&contents[..], DATA_FILE_CONTENTS.as_bytes());
655 }
656
657 #[fasync::run_singlethreaded(test)]
658 async fn read_in_namespace_fails_on_invalid_namespace_entry() {
659 assert_matches!(
660 read_in_namespace("/fake").await,
661 Err(ReadNamedError { path, source: ReadError::Open(_) }) if path == "/fake"
662 );
663 let err = read_in_namespace("/fake").await.unwrap_err();
664 assert_eq!(err.path(), "/fake");
665 assert_matches!(err.into_inner(), ReadError::Open(_));
666 }
667
668 #[fasync::run_singlethreaded(test)]
671 async fn read_to_string_reads_data_file() {
672 let file = open_in_namespace("/pkg/data/file", fio::PERM_READABLE).unwrap();
673 assert_eq!(read_to_string(&file).await.unwrap(), DATA_FILE_CONTENTS);
674 }
675
676 #[fasync::run_singlethreaded(test)]
679 async fn read_in_namespace_to_string_reads_data_file() {
680 assert_eq!(
681 read_in_namespace_to_string("/pkg/data/file").await.unwrap(),
682 DATA_FILE_CONTENTS
683 );
684 }
685
686 #[fasync::run_singlethreaded(test)]
689 async fn write_fidl_writes_to_file() {
690 let tempdir = TempDir::new().unwrap();
691 let dir = directory::open_in_namespace(
692 tempdir.path().to_str().unwrap(),
693 fio::PERM_READABLE | fio::PERM_WRITABLE,
694 )
695 .unwrap();
696
697 let file =
699 directory::open_file(&dir, "file", fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE)
700 .await
701 .unwrap();
702
703 let mut data = DataTable1 {
704 num: Some(42),
705 string: Some(DATA_FILE_CONTENTS.to_string()),
706 ..Default::default()
707 };
708
709 let fidl_bytes = persist(&data).unwrap();
711
712 write_fidl(&file, &mut data).await.unwrap();
713
714 let contents = std::fs::read(tempdir.path().join(Path::new("file"))).unwrap();
716 assert_eq!(&contents, &fidl_bytes);
717 }
718
719 #[fasync::run_singlethreaded(test)]
720 async fn read_fidl_reads_from_file() {
721 let file = open_in_namespace("/pkg/data/fidl_file", fio::PERM_READABLE).unwrap();
722
723 let contents = read_fidl::<DataTable2>(&file).await.unwrap();
724
725 let data = DataTable2 {
726 num: Some(42),
727 string: Some(DATA_FILE_CONTENTS.to_string()),
728 new_field: None,
729 ..Default::default()
730 };
731 assert_eq!(&contents, &data);
732 }
733
734 #[test]
735 fn extract_stream_from_on_open_event_with_stream() {
736 let vmo = zx::Vmo::create(0).unwrap();
737 let stream = zx::Stream::create(zx::StreamOptions::empty(), &vmo, 0).unwrap();
738 let event = fio::FileEvent::OnOpen_ {
739 s: 0,
740 info: Some(Box::new(fio::NodeInfoDeprecated::File(fio::FileObject {
741 stream: Some(stream),
742 event: None,
743 }))),
744 };
745 let stream = extract_stream_from_on_open_event(event)
746 .expect("Not a file")
747 .expect("Stream not present");
748 assert!(!stream.is_invalid_handle());
749 }
750
751 #[test]
752 fn extract_stream_from_on_open_event_without_stream() {
753 let event = fio::FileEvent::OnOpen_ {
754 s: 0,
755 info: Some(Box::new(fio::NodeInfoDeprecated::File(fio::FileObject {
756 stream: None,
757 event: None,
758 }))),
759 };
760 let stream = extract_stream_from_on_open_event(event).expect("Not a file");
761 assert!(stream.is_none());
762 }
763
764 #[test]
765 fn extract_stream_from_on_open_event_with_open_error() {
766 let event = fio::FileEvent::OnOpen_ { s: zx::Status::NOT_FOUND.into_raw(), info: None };
767 let result = extract_stream_from_on_open_event(event);
768 assert_matches!(result, Err(OpenError::OpenError(zx::Status::NOT_FOUND)));
769 }
770
771 #[test]
772 fn extract_stream_from_on_open_event_not_a_file() {
773 let event = fio::FileEvent::OnOpen_ {
774 s: 0,
775 info: Some(Box::new(fio::NodeInfoDeprecated::Service(fio::Service))),
776 };
777 let result = extract_stream_from_on_open_event(event);
778 assert_matches!(
779 result,
780 Err(OpenError::UnexpectedNodeKind { expected: Kind::File, actual: Kind::Service })
781 );
782 }
783
784 #[test]
785 fn extract_stream_from_on_representation_event_with_stream() {
786 let vmo = zx::Vmo::create(0).unwrap();
787 let stream = zx::Stream::create(zx::StreamOptions::empty(), &vmo, 0).unwrap();
788 let event = fio::FileEvent::OnRepresentation {
789 payload: fio::Representation::File(fio::FileInfo {
790 stream: Some(stream),
791 ..Default::default()
792 }),
793 };
794 let stream = extract_stream_from_on_open_event(event)
795 .expect("Not a file")
796 .expect("Stream not present");
797 assert!(!stream.is_invalid_handle());
798 }
799
800 #[test]
801 fn extract_stream_from_on_representation_event_without_stream() {
802 let event = fio::FileEvent::OnRepresentation {
803 payload: fio::Representation::File(fio::FileInfo::default()),
804 };
805 let stream = extract_stream_from_on_open_event(event).expect("Not a file");
806 assert!(stream.is_none());
807 }
808
809 #[test]
810 fn extract_stream_from_on_representation_event_not_a_file() {
811 let event = fio::FileEvent::OnRepresentation {
812 payload: fio::Representation::Directory(Default::default()),
813 };
814 let result = extract_stream_from_on_open_event(event);
815 assert_matches!(
816 result,
817 Err(OpenError::UnexpectedNodeKind { expected: Kind::File, actual: Kind::Directory })
818 );
819 }
820
821 #[test]
822 fn read_contents_of_stream_with_contents() {
823 let data = b"file-contents".repeat(1000);
824 let vmo = zx::Vmo::create(data.len() as u64).unwrap();
825 vmo.write(&data, 0).unwrap();
826 let stream = zx::Stream::create(zx::StreamOptions::MODE_READ, &vmo, 0).unwrap();
827 let contents = read_contents_of_stream(stream).unwrap();
828 assert_eq!(contents, data);
829 }
830
831 #[test]
832 fn read_contents_of_stream_with_empty_stream() {
833 let vmo = zx::Vmo::create(0).unwrap();
834 let stream = zx::Stream::create(zx::StreamOptions::MODE_READ, &vmo, 0).unwrap();
835 let contents = read_contents_of_stream(stream).unwrap();
836 assert!(contents.is_empty());
837 }
838
839 fn serve_file(file: Arc<VmoFile>, flags: fio::Flags) -> fio::FileProxy {
840 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::FileMarker>();
841 flags.to_object_request(server_end).handle(|object_request| {
842 vfs::file::serve(file, ExecutionScope::new(), &flags, object_request)
843 });
844 proxy
845 }
846
847 #[fasync::run_singlethreaded(test)]
848 async fn read_file_with_on_open_event_with_stream() {
849 let data = b"file-contents".repeat(1000);
850 let vmo_file = read_only(&data);
851 const FLAGS: fio::Flags = fio::PERM_READABLE.union(fio::Flags::FLAG_SEND_REPRESENTATION);
852
853 {
854 let file = serve_file(vmo_file.clone(), FLAGS);
856 let event = take_on_open_event(&file).await.unwrap();
857 extract_stream_from_on_open_event(event).unwrap().expect("Stream not present");
858 }
859
860 let file = serve_file(vmo_file.clone(), FLAGS);
861 let contents = read_file_with_on_open_event(file).await.unwrap();
862 assert_eq!(contents, data);
863 }
864
865 #[fasync::run_singlethreaded(test)]
866 async fn read_missing_file_in_namespace() {
867 assert_matches!(
868 read_in_namespace("/pkg/data/missing").await,
869 Err(e) if e.is_not_found_error()
870 );
871 }
872
873 #[test]
874 fn write_to_stream_with_contents() {
875 let data = b"file-contents".repeat(1000);
876 let vmo = zx::Vmo::create(data.len() as u64).unwrap();
877 let stream = zx::Stream::create(zx::StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
878 write_to_stream(stream, &data).unwrap();
879
880 let mut read_back = vec![0; data.len()];
881 vmo.read(&mut read_back, 0).unwrap();
882 assert_eq!(read_back, data);
883 }
884
885 #[test]
886 fn write_to_stream_empty() {
887 let data = b"";
888 let vmo = zx::Vmo::create(0).unwrap();
889 let stream = zx::Stream::create(zx::StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
890 write_to_stream(stream, data).unwrap();
891 assert_eq!(vmo.get_size().unwrap(), 0);
892 }
893}