1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    crate::platform::PlatformServices, anyhow::Error,
    fidl_fuchsia_virtualization::LinuxManagerProxy, fuchsia_zircon_status as zx_status,
    guest_cli_args as arguments, std::fmt,
};

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum WipeResult {
    WipeCompleted,
    IncorrectGuestState,
    WipeFailure(i32),
    UnsupportedGuest(arguments::GuestType),
}

impl fmt::Display for WipeResult {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            WipeResult::WipeCompleted => write!(f, "Successfully wiped guest"),
            WipeResult::WipeFailure(status) => {
                write!(f, "Failed to wipe data: {}", zx_status::Status::from_raw(*status))
            }
            WipeResult::IncorrectGuestState => {
                write!(
                    f,
                    concat!(
                        "The VM has already started. Please stop the guest ",
                        "(by restarting the host or issuing a guest stop command) and retry."
                    )
                )
            }
            WipeResult::UnsupportedGuest(guest) => {
                write!(f, "Wipe is not supported for '{}'. Only 'termina' is supported", guest)
            }
        }
    }
}

pub async fn handle_wipe<P: PlatformServices>(
    services: &P,
    args: &arguments::wipe_args::WipeArgs,
) -> Result<WipeResult, Error> {
    if args.guest_type != arguments::GuestType::Termina {
        return Ok(WipeResult::UnsupportedGuest(args.guest_type));
    }

    let linux_manager = services.connect_to_linux_manager().await?;
    do_wipe(linux_manager).await
}

async fn do_wipe(proxy: LinuxManagerProxy) -> Result<WipeResult, Error> {
    let result = match proxy.wipe_data().await?.map_err(zx_status::Status::from_raw) {
        Err(zx_status::Status::BAD_STATE) => WipeResult::IncorrectGuestState,
        Err(status) => WipeResult::WipeFailure(status.into_raw()),
        Ok(()) => WipeResult::WipeCompleted,
    };

    Ok(result)
}

#[cfg(test)]
mod test {
    use {
        super::*, crate::platform::FuchsiaPlatformServices,
        fidl::endpoints::create_proxy_and_stream, fidl_fuchsia_virtualization::LinuxManagerMarker,
        fuchsia_async as fasync, futures::StreamExt,
    };

    fn serve_mock_manager(response: zx_status::Status) -> LinuxManagerProxy {
        let (proxy, mut stream) = create_proxy_and_stream::<LinuxManagerMarker>()
            .expect("failed to create LinuxManager proxy/stream");
        fasync::Task::local(async move {
            let responder = stream
                .next()
                .await
                .expect("mock manager expected a request")
                .unwrap()
                .into_wipe_data()
                .expect("unexpected call to mock manager");

            if response == zx_status::Status::OK {
                responder.send(Ok(())).expect("failed to send mock response");
            } else {
                responder.send(Err(response.into_raw())).expect("failed to send mock response");
            }
        })
        .detach();

        proxy
    }

    #[fasync::run_until_stalled(test)]
    async fn unsupported_guest_type() {
        let services = FuchsiaPlatformServices::new();
        let result = handle_wipe(
            &services,
            &arguments::wipe_args::WipeArgs { guest_type: arguments::GuestType::Debian },
        )
        .await
        .unwrap();

        assert_eq!(result, WipeResult::UnsupportedGuest(arguments::GuestType::Debian));
    }

    #[fasync::run_until_stalled(test)]
    async fn incorrect_guest_state() {
        let proxy = serve_mock_manager(zx_status::Status::BAD_STATE);
        let result = do_wipe(proxy).await.unwrap();
        assert_eq!(result, WipeResult::IncorrectGuestState);
    }

    #[fasync::run_until_stalled(test)]
    async fn guest_wipe_failure() {
        let proxy = serve_mock_manager(zx_status::Status::NOT_FOUND);
        let result = do_wipe(proxy).await.unwrap();
        assert_eq!(result, WipeResult::WipeFailure(zx_status::Status::NOT_FOUND.into_raw()));
    }

    #[fasync::run_until_stalled(test)]
    async fn guest_successfully_wiped() {
        let proxy = serve_mock_manager(zx_status::Status::OK);
        let result = do_wipe(proxy).await.unwrap();
        assert_eq!(result, WipeResult::WipeCompleted);
    }
}