devices/virtio/gpu/
snapshot.rs

1// Copyright 2025 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Utilities for working with directories for snapshots.
6
7use std::collections::BTreeMap as Map;
8use std::path::Path;
9use std::path::PathBuf;
10
11use anyhow::Context;
12use serde::Deserialize;
13use serde::Serialize;
14
15fn get_files_recursively(directory: &Path, paths: &mut Vec<PathBuf>) -> anyhow::Result<()> {
16    if directory.is_dir() {
17        for entry in std::fs::read_dir(directory)? {
18            let entry = entry?;
19            let entry_path = entry.path();
20            if entry_path.is_dir() {
21                get_files_recursively(&entry_path, paths)?;
22            } else {
23                paths.push(entry_path.to_path_buf());
24            }
25        }
26    }
27    Ok(())
28}
29
30fn get_files_under(directory: &Path) -> anyhow::Result<Vec<PathBuf>> {
31    let mut paths = Vec::new();
32    get_files_recursively(directory, &mut paths)?;
33    Ok(paths)
34}
35
36// TODO: use an actual archive/zip when a common crate is available in
37// both Android and Chrome.
38#[derive(Serialize, Deserialize)]
39pub struct DirectorySnapshot {
40    files: Map<PathBuf, Vec<u8>>,
41}
42
43pub fn pack_directory_to_snapshot(directory: &Path) -> anyhow::Result<DirectorySnapshot> {
44    let directory_files = get_files_under(directory).with_context(|| {
45        format!(
46            "failed to list snapshot files under {}",
47            directory.display()
48        )
49    })?;
50
51    let mut snapshot = DirectorySnapshot { files: Map::new() };
52
53    for path in directory_files.into_iter() {
54        let contents: Vec<u8> = std::fs::read(&path)
55            .with_context(|| format!("failed to read snapshot file {}", path.display()))?;
56
57        let relative_path = path
58            .strip_prefix(directory)
59            .with_context(|| {
60                format!(
61                    "failed to strip {} from {}",
62                    directory.display(),
63                    path.display()
64                )
65            })?
66            .to_path_buf();
67
68        snapshot.files.insert(relative_path, contents);
69    }
70
71    Ok(snapshot)
72}
73
74pub fn unpack_snapshot_to_directory(
75    directory: &Path,
76    snapshot: DirectorySnapshot,
77) -> anyhow::Result<()> {
78    for (path, contents) in snapshot.files.into_iter() {
79        let path = directory.join(path);
80        let path_directory = path
81            .parent()
82            .with_context(|| format!("failed to get parent directory for {}", path.display()))?;
83        std::fs::create_dir_all(path_directory)
84            .with_context(|| format!("failed to create directories for {}", path.display()))?;
85        std::fs::write(&path, contents)
86            .with_context(|| format!("failed to unpack snapshot to {}", path.display()))?;
87    }
88
89    Ok(())
90}