1use crate::directory::ExtDirectory;
6use crate::file::ExtFile;
7use crate::types::ExtAttributes;
8use ext4_lib::processor::Ext4Processor;
9use ext4_lib::readers::{BlockDeviceReader, ReaderWriter, VmoReader};
10use ext4_lib::structs::{self, EntryType, MIN_EXT4_SIZE};
11use fidl::endpoints::ClientEnd;
12use fidl_fuchsia_storage_block::BlockMarker;
13use log::error;
14use std::sync::Arc;
15
16mod directory;
17mod file;
18mod node;
19mod types;
20
21pub enum FsSourceType {
22 BlockDevice(ClientEnd<BlockMarker>),
23 Vmo(zx::Vmo),
24}
25
26#[derive(Debug, PartialEq)]
27pub enum ConstructFsError {
28 VmoReadError(zx::Status),
29 ParsingError(structs::ParsingError),
30 FileVmoError(zx::Status),
31 NodeError(zx::Status),
32}
33
34impl From<structs::ParsingError> for ConstructFsError {
35 fn from(value: structs::ParsingError) -> Self {
36 Self::ParsingError(value)
37 }
38}
39
40pub fn construct_fs(
42 source: FsSourceType,
43 inspector: &fuchsia_inspect::Inspector,
44) -> Result<Arc<ExtDirectory>, ConstructFsError> {
45 construct_fs_internal(source, true, inspector)
47}
48
49fn construct_fs_internal(
50 source: FsSourceType,
51 read_only: bool,
52 inspector: &fuchsia_inspect::Inspector,
53) -> Result<Arc<ExtDirectory>, ConstructFsError> {
54 let reader: Arc<dyn ReaderWriter> = match source {
55 FsSourceType::BlockDevice(block_device) => {
56 Arc::new(BlockDeviceReader::from_client_end(block_device).map_err(|e| {
57 error!("Error constructing file system: {}", e);
58 ConstructFsError::VmoReadError(zx::Status::IO_INVALID)
59 })?)
60 }
61 FsSourceType::Vmo(vmo) => {
62 let size = vmo.get_size().map_err(ConstructFsError::VmoReadError)?;
63 if size < MIN_EXT4_SIZE as u64 {
64 return Err(ConstructFsError::VmoReadError(zx::Status::NO_SPACE));
66 }
67
68 Arc::new(VmoReader::new(Arc::new(vmo)))
69 }
70 };
71 let processor = Arc::new(Ext4Processor::new(reader, read_only));
72 let dir = build_fs_dir(processor.clone(), structs::ROOT_INODE_NUM, read_only)?;
73 processor.record_statistics(inspector.root());
74 Ok(dir)
75}
76
77fn build_fs_dir(
78 processor: Arc<Ext4Processor>,
79 ino: u32,
80 read_only: bool,
81) -> Result<Arc<ExtDirectory>, ConstructFsError> {
82 let inode = processor.inode(ino)?;
83 let entries = processor.entries_from_inode(&inode)?;
84 let attributes = ExtAttributes::from_inode(inode);
85 let xattrs = processor.inode_xattrs(ino)?;
86 let dir = ExtDirectory::new(ino as u64, attributes, xattrs);
87
88 for entry in entries {
89 let entry_name = entry.name()?;
90 if entry_name == "." || entry_name == ".." {
91 continue;
92 }
93
94 let entry_ino = u32::from(entry.e2d_ino);
95 match EntryType::from_u8(entry.e2d_type)? {
96 EntryType::Directory => {
97 dir.insert_child(
98 entry_name,
99 build_fs_dir(processor.clone(), entry_ino, read_only)?,
100 )
101 .map_err(ConstructFsError::NodeError)?;
102 }
103 EntryType::RegularFile => {
104 dir.insert_child(
105 entry_name,
106 ExtFile::from_processor(processor.clone(), entry_ino, read_only)
107 .map_err(ConstructFsError::NodeError)?,
108 )
109 .map_err(ConstructFsError::NodeError)?;
110 }
111 _ => {
112 }
114 }
115 }
116
117 Ok(dir)
118}
119
120#[cfg(test)]
121mod tests {
122 use super::{FsSourceType, construct_fs, construct_fs_internal};
123
124 use ext4_lib::structs::MIN_EXT4_SIZE;
125 use fuchsia_fs::directory::{DirEntry, DirentKind, open_file, open_node, readdir};
126 use fuchsia_fs::file::{WriteError, read_to_string, write};
127 use std::fs;
128 use std::sync::Arc;
129 use vmo_backed_block_server::{InitialContents, VmoBackedServerOptions};
130 use zx::{HandleBased, Status, Vmo};
131 use {fidl_fuchsia_io as fio, fidl_fuchsia_storage_block as fblock, fuchsia_async as fasync};
132
133 #[fuchsia::test]
134 fn image_too_small() {
135 let vmo = Vmo::create(10).expect("VMO is created");
136 vmo.write(b"too small", 0).expect("VMO write() succeeds");
137 let buffer = FsSourceType::Vmo(vmo);
138
139 assert!(
140 construct_fs(buffer, &fuchsia_inspect::Inspector::default()).is_err(),
141 "Expected failed parsing of VMO."
142 );
143 }
144
145 #[fuchsia::test]
146 fn invalid_fs() {
147 let vmo = Vmo::create(MIN_EXT4_SIZE as u64).expect("VMO is created");
148 vmo.write(b"not ext4", 0).expect("VMO write() succeeds");
149 let buffer = FsSourceType::Vmo(vmo);
150
151 assert!(
152 construct_fs(buffer, &fuchsia_inspect::Inspector::default()).is_err(),
153 "Expected failed parsing of VMO."
154 );
155 }
156
157 #[fuchsia::test]
158 async fn list_root() {
159 let data = fs::read("/pkg/data/nest.img").expect("Unable to read file");
160 let vmo = Vmo::create(data.len() as u64).expect("VMO is created");
161 vmo.write(data.as_slice(), 0).expect("VMO write() succeeds");
162 let buffer = FsSourceType::Vmo(vmo);
163
164 let tree = construct_fs(buffer, &fuchsia_inspect::Inspector::default())
165 .expect("construct_fs parses the vmo");
166 let root = vfs::directory::serve(tree, fio::PERM_READABLE);
167
168 let expected = vec![
169 DirEntry { name: String::from("file1"), kind: DirentKind::File },
170 DirEntry { name: String::from("inner"), kind: DirentKind::Directory },
171 DirEntry { name: String::from("lost+found"), kind: DirentKind::Directory },
172 ];
173 assert_eq!(readdir(&root).await.unwrap(), expected);
174
175 let file = open_file(&root, "file1", fio::PERM_READABLE).await.unwrap();
176 assert_eq!(read_to_string(&file).await.unwrap(), "file1 contents.\n");
177 file.close().await.unwrap().map_err(zx::Status::from_raw).unwrap();
178 root.close().await.unwrap().map_err(zx::Status::from_raw).unwrap();
179 }
180
181 #[fuchsia::test]
182 async fn get_dac_attributes() {
183 let data = fs::read("/pkg/data/dac_attributes.img").expect("Unable to read file");
184 let vmo = Vmo::create(data.len() as u64).expect("VMO is created");
185 vmo.write(data.as_slice(), 0).expect("VMO write() succeeds");
186 let buffer = FsSourceType::Vmo(vmo);
187
188 let tree = construct_fs(buffer, &fuchsia_inspect::Inspector::default())
189 .expect("construct_fs parses the VMO");
190 let root = vfs::directory::serve(tree, fio::PERM_READABLE);
191
192 let expected_entries = vec![
193 DirEntry { name: String::from("dir_1000"), kind: DirentKind::Directory },
194 DirEntry { name: String::from("dir_root"), kind: DirentKind::Directory },
195 DirEntry { name: String::from("file_1000"), kind: DirentKind::File },
196 DirEntry { name: String::from("file_root"), kind: DirentKind::File },
197 DirEntry { name: String::from("lost+found"), kind: DirentKind::Directory },
198 ];
199 assert_eq!(readdir(&root).await.unwrap(), expected_entries);
200
201 #[derive(Debug, PartialEq)]
202 struct Node {
203 name: String,
204 mode: u32,
205 uid: u32,
206 gid: u32,
207 }
208
209 let expected_attributes = vec![
210 Node { name: String::from("dir_1000"), mode: 0x416D, uid: 1000, gid: 1000 },
211 Node { name: String::from("dir_root"), mode: 0x4140, uid: 0, gid: 0 },
212 Node { name: String::from("file_1000"), mode: 0x8124, uid: 1000, gid: 1000 },
213 Node { name: String::from("file_root"), mode: 0x8100, uid: 0, gid: 0 },
214 ];
215
216 let attributes_query = fio::NodeAttributesQuery::MODE
217 | fio::NodeAttributesQuery::UID
218 | fio::NodeAttributesQuery::GID;
219 for expected_node in &expected_attributes {
220 let node_proxy = open_node(&root, expected_node.name.as_str(), fio::PERM_READABLE)
221 .await
222 .expect("node open failed");
223 let (mut_attrs, _immut_attrs) = node_proxy
224 .get_attributes(attributes_query)
225 .await
226 .expect("node get_attributes() failed")
227 .map_err(Status::from_raw)
228 .expect("node get_attributes() error");
229
230 let node = Node {
231 name: expected_node.name.clone(),
232 mode: mut_attrs.mode.expect("node attributes missing mode"),
233 uid: mut_attrs.uid.expect("node attributes missing uid"),
234 gid: mut_attrs.gid.expect("node attributes missing gid"),
235 };
236
237 node_proxy
238 .close()
239 .await
240 .expect("node close failed")
241 .map_err(Status::from_raw)
242 .expect("node close error");
243
244 assert_eq!(node, *expected_node);
245 }
246
247 root.close().await.unwrap().map_err(Status::from_raw).unwrap();
248 }
249
250 #[fuchsia::test]
251 async fn test_constructing_writeable_fs_and_writing_to_allocated_region() {
252 let data = fs::read("/pkg/data/nest.img").expect("failed to read file");
254 let vmo = Vmo::create(data.len() as u64).expect("failed to create VMO");
255 vmo.write(data.as_slice(), 0).expect("failed to write to VMO");
256 let server = Arc::new(
257 VmoBackedServerOptions {
258 block_size: 512,
259 initial_contents: InitialContents::FromVmo(vmo),
260 ..Default::default()
261 }
262 .build()
263 .expect("build from VmoBackedServerOptions failed"),
264 );
265
266 let server_clone = server.clone();
267 let (block_client_end1, block_server_end1) =
268 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
269 std::thread::spawn(move || {
270 let mut executor = fasync::TestExecutor::new();
271 let _task =
272 executor.run_singlethreaded(server_clone.serve(block_server_end1.into_stream()));
273 });
274
275 let tree = construct_fs_internal(
277 FsSourceType::BlockDevice(block_client_end1),
278 false,
279 &fuchsia_inspect::Inspector::default(),
280 )
281 .expect("failed to parse the vmo");
282 let root = vfs::directory::serve(tree, fio::PERM_READABLE | fio::PERM_WRITABLE);
283 let file = open_file(&root, "file1", fio::PERM_READABLE | fio::PERM_WRITABLE)
284 .await
285 .expect("failed to open file");
286 let original_contents = "file1 contents.\n";
287 assert_eq!(read_to_string(&file).await.expect("failed to read file"), original_contents);
288 let new_contents = "new";
289 let offset = 5;
290 file.seek(fio::SeekOrigin::Start, offset)
291 .await
292 .expect("failed FIDL seek")
293 .map_err(zx::Status::from_raw)
294 .expect("failed to seek file");
295 write(&file, new_contents).await.expect("failed to write to file");
296 file.close()
297 .await
298 .expect("failed FIDL file close")
299 .map_err(zx::Status::from_raw)
300 .expect("failed to close file");
301 root.close()
302 .await
303 .expect("failed FIDL dir close")
304 .map_err(zx::Status::from_raw)
305 .expect("failed to close root");
306
307 let server_clone = server.clone();
309 let (block_client_end2, block_server_end2) =
310 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
311 std::thread::spawn(move || {
312 let mut executor = fasync::TestExecutor::new();
313 let _task =
314 executor.run_singlethreaded(server_clone.serve(block_server_end2.into_stream()));
315 });
316 let tree = construct_fs_internal(
317 FsSourceType::BlockDevice(block_client_end2),
318 true,
319 &fuchsia_inspect::Inspector::default(),
320 )
321 .expect("construct_fs parses the vmo");
322 let root = vfs::directory::serve(tree, fio::PERM_READABLE);
323 let file =
324 open_file(&root, "file1", fio::PERM_READABLE).await.expect("failed to open file");
325 let mut expected_bytes = original_contents.as_bytes().to_vec();
326 expected_bytes[offset as usize..offset as usize + new_contents.len()]
327 .copy_from_slice(new_contents.as_bytes());
328 assert_eq!(
329 read_to_string(&file).await.expect("failed to read file"),
330 String::from_utf8(expected_bytes).unwrap()
331 );
332 file.close()
333 .await
334 .expect("failed FIDL file close")
335 .map_err(zx::Status::from_raw)
336 .expect("failed to close file");
337 root.close()
338 .await
339 .expect("failed FIDL dir close")
340 .map_err(zx::Status::from_raw)
341 .expect("failed to close root");
342 }
343
344 #[fuchsia::test]
345 async fn test_writing_past_eof_fails() {
346 let data = fs::read("/pkg/data/nest.img").expect("failed to read file");
347 let vmo = Vmo::create(data.len() as u64).expect("failed to create VMO");
348 vmo.write(data.as_slice(), 0).expect("failed to write to VMO");
349 let server = Arc::new(
350 VmoBackedServerOptions {
351 block_size: 512,
352 initial_contents: InitialContents::FromVmo(vmo),
353 ..Default::default()
354 }
355 .build()
356 .expect("build from VmoBackedServerOptions failed"),
357 );
358
359 let server_clone = server.clone();
360 let (block_client_end1, block_server_end1) =
361 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
362 std::thread::spawn(move || {
363 let mut executor = fasync::TestExecutor::new();
364 let _task =
365 executor.run_singlethreaded(server_clone.serve(block_server_end1.into_stream()));
366 });
367
368 let tree = construct_fs_internal(
370 FsSourceType::BlockDevice(block_client_end1),
371 false,
372 &fuchsia_inspect::Inspector::default(),
373 )
374 .expect("failed to parse the vmo");
375 let root = vfs::directory::serve(tree, fio::PERM_READABLE | fio::PERM_WRITABLE);
376 let file = open_file(&root, "file1", fio::PERM_READABLE | fio::PERM_WRITABLE)
377 .await
378 .expect("failed to open file");
379 let original_contents = read_to_string(&file).await.expect("failed to read file");
380
381 let new_contents = [1u8; 8192];
383 file.seek(fio::SeekOrigin::Start, 0)
384 .await
385 .expect("failed FIDL seek")
386 .map_err(zx::Status::from_raw)
387 .expect("failed to seek file");
388 let error = write(&file, &new_contents)
389 .await
390 .expect_err("write to unallocated region passed unexpectedly");
391 match error {
392 WriteError::WriteError(status) => assert_eq!(status, zx::Status::NOT_SUPPORTED),
393 _ => panic!("Unexpected error: {:?}", error),
394 }
395
396 file.close()
397 .await
398 .expect("failed FIDL file close")
399 .map_err(zx::Status::from_raw)
400 .expect("failed to close file");
401 root.close()
402 .await
403 .expect("failed FIDL dir close")
404 .map_err(zx::Status::from_raw)
405 .expect("failed to close root");
406
407 let server_clone = server.clone();
409 let (block_client_end2, block_server_end2) =
410 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
411 std::thread::spawn(move || {
412 let mut executor = fasync::TestExecutor::new();
413 let _task =
414 executor.run_singlethreaded(server_clone.serve(block_server_end2.into_stream()));
415 });
416 let tree = construct_fs_internal(
417 FsSourceType::BlockDevice(block_client_end2),
418 true,
419 &fuchsia_inspect::Inspector::default(),
420 )
421 .expect("construct_fs parses the vmo");
422 let root = vfs::directory::serve(tree, fio::PERM_READABLE);
423 let file =
424 open_file(&root, "file1", fio::PERM_READABLE).await.expect("failed to open file");
425 assert_eq!(read_to_string(&file).await.expect("failed to read file"), original_contents);
426 file.close()
427 .await
428 .expect("failed FIDL file close")
429 .map_err(zx::Status::from_raw)
430 .expect("failed to close file");
431 root.close()
432 .await
433 .expect("failed FIDL dir close")
434 .map_err(zx::Status::from_raw)
435 .expect("failed to close root");
436 }
437
438 #[fuchsia::test]
439 async fn test_file_sync() {
440 let data = fs::read("/pkg/data/nest.img").expect("failed to read file");
441 let vmo = Vmo::create(data.len() as u64).expect("failed to create VMO");
442 vmo.write(data.as_slice(), 0).expect("failed to write to VMO");
443
444 let vmo_clone = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed to clone vmo");
446
447 let server = Arc::new(
448 VmoBackedServerOptions {
449 block_size: 512,
450 initial_contents: InitialContents::FromVmo(vmo),
451 ..Default::default()
452 }
453 .build()
454 .expect("build from VmoBackedServerOptions failed"),
455 );
456
457 let server_clone = server.clone();
458 let (block_client_end, block_server_end) =
459 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
460 std::thread::spawn(move || {
461 let mut executor = fasync::TestExecutor::new();
462 let _task =
463 executor.run_singlethreaded(server_clone.serve(block_server_end.into_stream()));
464 });
465
466 let tree = construct_fs_internal(
468 FsSourceType::BlockDevice(block_client_end),
469 false,
470 &fuchsia_inspect::Inspector::default(),
471 )
472 .expect("failed to parse the vmo");
473 let root = vfs::directory::serve(tree, fio::PERM_READABLE | fio::PERM_WRITABLE);
474 let file = open_file(&root, "file1", fio::PERM_READABLE | fio::PERM_WRITABLE)
475 .await
476 .expect("failed to open file");
477
478 let mut old_vmo_contents = vec![0u8; data.len()];
479 vmo_clone.read(&mut old_vmo_contents, 0).expect("failed to read from vmo clone");
480
481 let new_contents = "FILE1 CONTENTS!\n";
482 file.seek(fio::SeekOrigin::Start, 0)
483 .await
484 .expect("failed FIDL seek")
485 .map_err(zx::Status::from_raw)
486 .expect("failed to seek file");
487 write(&file, new_contents).await.expect("failed to write to file");
488
489 let mut vmo_contents_after_write = vec![0u8; data.len()];
490 vmo_clone.read(&mut vmo_contents_after_write, 0).expect("failed to read from vmo clone");
491
492 assert_eq!(
495 old_vmo_contents, vmo_contents_after_write,
496 "Data should be cached and not yet flushed to VmoBackedServer"
497 );
498
499 file.close()
501 .await
502 .expect("sync check failed")
503 .map_err(zx::Status::from_raw)
504 .expect("sync error");
505
506 let mut vmo_contents_after_sync = vec![0u8; data.len()];
507 vmo_clone.read(&mut vmo_contents_after_sync, 0).expect("failed to read from vmo clone");
508
509 assert_ne!(
511 old_vmo_contents, vmo_contents_after_sync,
512 "Data should be flushed to VmoBackedServer after sync"
513 );
514
515 root.close()
516 .await
517 .expect("failed FIDL dir close")
518 .map_err(zx::Status::from_raw)
519 .expect("failed to close root");
520 }
521
522 #[fuchsia::test]
523 async fn test_metrics_of_fs_with_multiple_files() {
524 let data = fs::read("/pkg/data/nest.img").expect("failed to read file");
525 let vmo = Vmo::create(data.len() as u64).expect("failed to create VMO");
526 vmo.write(data.as_slice(), 0).expect("failed to write to VMO");
527 let server = Arc::new(
528 VmoBackedServerOptions {
529 block_size: 512,
530 initial_contents: InitialContents::FromVmo(vmo),
531 ..Default::default()
532 }
533 .build()
534 .expect("build from VmoBackedServerOptions failed"),
535 );
536
537 let server_clone = server.clone();
538 let (block_client_end, block_server_end) =
539 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
540 std::thread::spawn(move || {
541 let mut executor = fasync::TestExecutor::new();
542 let _task =
543 executor.run_singlethreaded(server_clone.serve(block_server_end.into_stream()));
544 });
545
546 let inspector = fuchsia_inspect::Inspector::default();
547 let tree = construct_fs_internal(
548 FsSourceType::BlockDevice(block_client_end),
549 false,
550 &inspector,
551 )
552 .expect("failed to parse the vmo");
553 let root = vfs::directory::serve(tree, fio::PERM_READABLE | fio::PERM_WRITABLE);
554
555 let file1 = open_file(
556 &root,
557 "file1",
558 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::FILE_TRUNCATE,
559 )
560 .await
561 .expect("open with truncate should succeed");
562 let contents = read_to_string(&file1).await.expect("failed to read file");
563 assert_eq!(contents, "");
564 diagnostics_assertions::assert_data_tree!(inspector, root: {
565 file_metrics: {
566 num_open_requests: 1u64,
567 num_read_requests: 1u64,
568 num_truncate_requests: 1u64,
569 num_write_requests: 0u64,
570 num_writes_past_eof_attempts: 0u64,
571 num_successful_overwrites: 0u64,
572 num_blocks_overwritten: 0u64,
573 }
574 });
575 file1
576 .seek(fio::SeekOrigin::Start, 0)
577 .await
578 .expect("failed to seek")
579 .map_err(zx::Status::from_raw)
580 .expect("seek error");
581 write(&file1, "FILE1 CONTENTS!\n").await.expect("failed to write to file");
582 file1
583 .seek(fio::SeekOrigin::Start, 0)
584 .await
585 .expect("failed to seek")
586 .map_err(zx::Status::from_raw)
587 .expect("seek error");
588 let new_contents = read_to_string(&file1).await.expect("failed to read file");
591 assert_eq!(new_contents, "FILE1 CONTENTS!\n");
592 diagnostics_assertions::assert_data_tree!(inspector, root: {
593 file_metrics: {
594 num_open_requests: 1u64,
595 num_read_requests: 3u64,
596 num_truncate_requests: 1u64,
597 num_write_requests: 1u64,
598 num_writes_past_eof_attempts: 0u64,
599 num_successful_overwrites: 1u64,
600 num_blocks_overwritten: 1u64,
601 }
602 });
603
604 let file2 = open_file(&root, "inner/file2", fio::PERM_READABLE | fio::PERM_WRITABLE)
607 .await
608 .expect("failed to open inner/file2");
609 let _contents = read_to_string(&file2).await.expect("failed to read file2");
610
611 diagnostics_assertions::assert_data_tree!(inspector, root: {
612 file_metrics: {
613 num_open_requests: 2u64,
614 num_read_requests: 5u64,
615 num_truncate_requests: 1u64,
616 num_write_requests: 1u64,
617 num_writes_past_eof_attempts: 0u64,
618 num_successful_overwrites: 1u64,
619 num_blocks_overwritten: 1u64,
620 }
621 });
622
623 root.close()
624 .await
625 .expect("failed FIDL dir close")
626 .map_err(zx::Status::from_raw)
627 .expect("failed to close root");
628 }
629
630 #[fuchsia::test]
631 async fn test_truncate_and_write() {
632 let data = fs::read("/pkg/data/1file.img").expect("failed to read file");
633 let vmo = Vmo::create(data.len() as u64).expect("failed to create VMO");
634 vmo.write(data.as_slice(), 0).expect("failed to write to VMO");
635 let server = Arc::new(
636 VmoBackedServerOptions {
637 block_size: 512,
638 initial_contents: InitialContents::FromVmo(vmo),
639 ..Default::default()
640 }
641 .build()
642 .expect("build from VmoBackedServerOptions failed"),
643 );
644
645 let server_clone = server.clone();
646 let (block_client_end, block_server_end) =
647 fidl::endpoints::create_endpoints::<fblock::BlockMarker>();
648 std::thread::spawn(move || {
649 let mut executor = fasync::TestExecutor::new();
650 let _task =
651 executor.run_singlethreaded(server_clone.serve(block_server_end.into_stream()));
652 });
653
654 let inspector = fuchsia_inspect::Inspector::default();
655 let tree = construct_fs_internal(
656 FsSourceType::BlockDevice(block_client_end),
657 false,
658 &inspector,
659 )
660 .expect("failed to parse the vmo");
661 let root = vfs::directory::serve(tree, fio::PERM_READABLE | fio::PERM_WRITABLE);
662
663 let file = open_file(&root, "file1", fio::PERM_READABLE).await.expect("open failed");
665 let original_contents = read_to_string(&file).await.expect("read failed");
666 assert_eq!(original_contents, "file1 contents.\n");
667 file.close().await.unwrap().map_err(zx::Status::from_raw).unwrap();
668
669 let file = open_file(
671 &root,
672 "file1",
673 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::FILE_TRUNCATE,
674 )
675 .await
676 .expect("open with truncate failed");
677 assert_eq!(read_to_string(&file).await.expect("read failed"), "");
678
679 let new_content = "FILE1 CONTENTS.\n";
681 write(&file, new_content).await.expect("write failed");
682 file.seek(fio::SeekOrigin::Start, 0)
683 .await
684 .expect("seek failed")
685 .map_err(zx::Status::from_raw)
686 .expect("seek error");
687 assert_eq!(read_to_string(&file).await.expect("read failed"), new_content);
688
689 let huge_content = vec![1u8; 50];
691 let error =
692 write(&file, &huge_content).await.expect_err("write past allocated size should fail");
693 match error {
694 WriteError::WriteError(status) => assert_eq!(status, zx::Status::NOT_SUPPORTED),
695 _ => panic!("Unexpected error: {:?}", error),
696 }
697
698 let partial_content = vec![1u8; 2];
700 let error = write(&file, &partial_content)
701 .await
702 .expect_err("write past allocated size should fail");
703 match error {
704 WriteError::WriteError(status) => assert_eq!(status, zx::Status::NOT_SUPPORTED),
705 _ => panic!("Unexpected error: {:?}", error),
706 }
707
708 file.seek(fio::SeekOrigin::Start, 0)
710 .await
711 .expect("seek failed")
712 .map_err(zx::Status::from_raw)
713 .expect("seek error");
714 assert_eq!(read_to_string(&file).await.expect("read failed"), new_content);
715
716 file.close().await.unwrap().map_err(zx::Status::from_raw).unwrap();
717 root.close().await.unwrap().map_err(zx::Status::from_raw).unwrap();
718 }
719}