Skip to main content

ext4_parser/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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                // Too small to even fit the first copy of the ext4 Super Block.
56                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                // TODO(https://fxbug.dev/42073143): Handle other types.
104            }
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        // Create a device that is Ext4 formatted.
246        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        // Write to the allocated extent of this file.
269        let tree = construct_fs(
270            FsSourceType::BlockDevice(block_client_end1),
271            /* read_only= */ 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        // Construct Ext4 fs again, and verify that the written data is still there.
301        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            /* read_only= */ 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        // Write to the allocated extent of this file.
362        let tree = construct_fs(
363            FsSourceType::BlockDevice(block_client_end1),
364            /* read_only= */ 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        // There is not enough allocated bytes in this file to write this new content.
375        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        // Construct Ext4 fs again, and verify that the written data is still there.
401        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            /* read_only= */ 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        // Clone VMO to observe underlying changes made by the server.
438        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        // Write to the allocated extent of this file.
460        let tree = construct_fs(
461            FsSourceType::BlockDevice(block_client_end),
462            /* read_only= */ 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        // The write is stored in the device cache but has not yet been been flushed to the
486        // underlying VMO.
487        assert_eq!(
488            old_vmo_contents, vmo_contents_after_write,
489            "Data should be cached and not yet flushed to VmoBackedServer"
490        );
491
492        // Closing the file will call sync to flush the contents to the backing VMO.
493        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        // Data should now be flushed to underlying VMO.
503        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            /* read_only= */ 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        // `read_to_string` loops read until no bytes are read back. So for non-empty strings, we
582        // expect to see two more read requests.
583        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        // Perform opens and reads on another file. Should see them reflected in the inspector
598        // metrics.
599        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            /* read_only= */ 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        // Check original contents
657        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        // Open with TRUNCATE, reading from this should return empty string.
663        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        // Write to the file and verify we see new contents.
673        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        // Check that writing past original file size fails.
683        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        // Check that overwriting the file partially is not supported.
692        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        // We see the content written previously.
702        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}