• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 use std::path::Path;
6 use std::path::PathBuf;
7 
8 use serde::Deserialize;
9 use serde::Serialize;
10 use serde_keyvalue::FromKeyValues;
11 
12 static VHOST_VSOCK_DEFAULT_PATH: &str = "/dev/vhost-vsock";
13 
default_vsock_path() -> PathBuf14 fn default_vsock_path() -> PathBuf {
15     PathBuf::from(VHOST_VSOCK_DEFAULT_PATH)
16 }
17 
18 #[derive(Debug, Deserialize, Serialize, PartialEq, Eq, FromKeyValues)]
19 #[serde(deny_unknown_fields)]
20 /// Configuration for a Vsock device.
21 pub struct VsockConfig {
22     /// CID to be used for this vsock device.
23     pub cid: u64,
24     /// Path to the vhost-vsock device.
25     #[serde(default = "default_vsock_path", rename = "device")]
26     pub vhost_device: PathBuf,
27 }
28 
29 impl VsockConfig {
30     /// Create a new vsock configuration. If `vhost_device` is `None`, the default vhost-vsock
31     /// device path will be used.
new<P: AsRef<Path>>(cid: u64, vhost_device: Option<P>) -> Self32     pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: Option<P>) -> Self {
33         Self {
34             cid,
35             #[cfg(any(target_os = "android", target_os = "linux"))]
36             vhost_device: vhost_device
37                 .map(|p| PathBuf::from(p.as_ref()))
38                 .unwrap_or_else(|| PathBuf::from(VHOST_VSOCK_DEFAULT_PATH)),
39         }
40     }
41 }
42 
43 #[cfg(test)]
44 mod tests {
45     use serde_keyvalue::from_key_values;
46     use serde_keyvalue::ErrorKind;
47     use serde_keyvalue::ParseError;
48 
49     use super::*;
50 
from_vsock_arg(options: &str) -> Result<VsockConfig, ParseError>51     fn from_vsock_arg(options: &str) -> Result<VsockConfig, ParseError> {
52         from_key_values(options)
53     }
54 
55     #[test]
params_from_key_values()56     fn params_from_key_values() {
57         // Default device
58         assert_eq!(
59             from_vsock_arg("cid=56").unwrap(),
60             VsockConfig {
61                 vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
62                 cid: 56,
63             }
64         );
65 
66         // No argument
67         assert_eq!(
68             from_vsock_arg("").unwrap_err(),
69             ParseError {
70                 kind: ErrorKind::SerdeError("missing field `cid`".into()),
71                 pos: 0
72             }
73         );
74 
75         // CID passed without key
76         assert_eq!(
77             from_vsock_arg("78").unwrap(),
78             VsockConfig {
79                 #[cfg(any(target_os = "android", target_os = "linux"))]
80                 vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
81                 cid: 78,
82             }
83         );
84 
85         // CID passed twice
86         assert_eq!(
87             from_vsock_arg("cid=42,cid=56").unwrap_err(),
88             ParseError {
89                 kind: ErrorKind::SerdeError("duplicate field `cid`".into()),
90                 pos: 0,
91             }
92         );
93 
94         // Invalid argument
95         assert_eq!(
96             from_vsock_arg("invalid=foo").unwrap_err(),
97             ParseError {
98                 kind: ErrorKind::SerdeError(
99                     "unknown field `invalid`, expected `cid` or `device`".into()
100                 ),
101                 pos: 0,
102             }
103         );
104 
105         // Path device
106         assert_eq!(
107             from_vsock_arg("device=/some/path,cid=56").unwrap(),
108             VsockConfig {
109                 vhost_device: "/some/path".into(),
110                 cid: 56,
111             }
112         );
113 
114         // CID passed without key
115         assert_eq!(
116             from_vsock_arg("56,device=/some/path").unwrap(),
117             VsockConfig {
118                 vhost_device: "/some/path".into(),
119                 cid: 56,
120             }
121         );
122 
123         // Missing cid
124         assert_eq!(
125             from_vsock_arg("device=42").unwrap_err(),
126             ParseError {
127                 kind: ErrorKind::SerdeError("missing field `cid`".into()),
128                 pos: 0,
129             }
130         );
131 
132         // Device passed twice
133         assert_eq!(
134             from_vsock_arg("cid=56,device=42,device=/some/path").unwrap_err(),
135             ParseError {
136                 kind: ErrorKind::SerdeError("duplicate field `device`".into()),
137                 pos: 0,
138             }
139         );
140     }
141 }
142