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