Skip to main content

hvctrl/vmware/
vmrun.rs

1use crate::{
2    exec_cmd_utf8,
3    types::*,
4    vmware::{read_vmware_inventory, read_vmware_preferences},
5};
6use std::{borrow::Cow, process::Command, time::Duration};
7
8pub enum HostType {
9    Player,
10    Workstation,
11    Fusion,
12}
13
14impl HostType {
15    pub fn as_str(&self) -> &'static str {
16        match self {
17            Self::Player => "player",
18            Self::Workstation => "ws",
19            Self::Fusion => "fusion",
20        }
21    }
22}
23
24impl ToString for HostType {
25    fn to_string(&self) -> String { self.as_str().to_string() }
26}
27
28impl<T: AsRef<str>> From<T> for HostType {
29    fn from(x: T) -> Self {
30        match x.as_ref() {
31            "player" => Self::Player,
32            "ws" => Self::Workstation,
33            "fusion" => Self::Fusion,
34            x => panic!("Unexpected HostType: {}", x),
35        }
36    }
37}
38
39pub enum WriteVar<'a> {
40    GuestVar(&'a str, &'a str),
41    RuntimeConfig(&'a str, &'a str),
42    GuestEnv(&'a str, &'a str),
43}
44
45pub enum ReadVar<'a> {
46    GuestVar(&'a str),
47    RuntimeConfig(&'a str),
48    GuestEnv(&'a str),
49}
50
51#[derive(Debug, Clone, Eq, PartialEq)]
52pub struct ProcInfo {
53    pub pid: u32,
54    pub owner: String,
55    pub cmd: String,
56}
57
58#[derive(Debug, Clone)]
59pub struct VmRun {
60    host_type: &'static str,
61    executable_path: String,
62    use_inventory: bool,
63    vm_path: Option<String>,
64    vm_password: Option<String>,
65    guest_username: Option<String>,
66    guest_password: Option<String>,
67    gui: bool,
68}
69
70impl Default for VmRun {
71    fn default() -> Self { Self::new() }
72}
73
74impl VmRun {
75    pub fn new() -> Self {
76        Self {
77            host_type: "ws",
78            executable_path: "vmrun".to_string(),
79            use_inventory: true,
80            vm_path: None,
81            vm_password: None,
82            guest_username: None,
83            guest_password: None,
84            gui: true,
85        }
86    }
87
88    impl_setter!(
89        /// Sets the path to vmrun.
90        executable_path: String
91    );
92
93    pub fn host_type<T: Into<HostType>>(&mut self, host_type: T) -> &mut Self {
94        let host_type = host_type.into();
95        match host_type {
96            HostType::Player => self.use_inventory = false,
97            _ => self.use_inventory = true,
98        }
99        self.host_type = host_type.as_str();
100        self
101    }
102
103    impl_setter!(@opt vm_path: String);
104    impl_setter!(@opt vm_password: String);
105    impl_setter!(@opt guest_username: String);
106    impl_setter!(@opt guest_password: String);
107    impl_setter!(use_inventory: bool);
108    impl_setter!(gui: bool);
109
110    #[inline]
111    fn build_auth(&self) -> Vec<&str> {
112        let mut v = Vec::with_capacity(6);
113        if let Some(x) = &self.guest_username {
114            v.extend(&["-gu", x]);
115        }
116        if let Some(x) = &self.guest_password {
117            v.extend(&["-gp", x]);
118        }
119        if let Some(x) = &self.vm_password {
120            v.extend(&["-vp", x]);
121        }
122        v
123    }
124
125    fn get_vm(&self) -> VmResult<&str> {
126        self.vm_path
127            .as_deref()
128            .ok_or_else(|| VmError::from(ErrorKind::VmIsNotSpecified))
129    }
130
131    #[inline]
132    fn cmd(&self) -> Command {
133        let mut cmd = Command::new(&self.executable_path);
134        cmd.args(&["-T", self.host_type]);
135        cmd.args(&self.build_auth());
136        cmd
137    }
138
139    #[inline]
140    fn handle_error(s: &str) -> VmError {
141        use ErrorKind::*;
142        use VmPowerState::*;
143        starts_err!(s, "No Vm name provided", VmIsNotSpecified);
144        starts_err!(s, "Cannot open VM: ", VmNotFound);
145        starts_err!(
146            s,
147            "The virtual machine is not powered on: ",
148            InvalidPowerState(NotRunning)
149        );
150        starts_err!(
151            s,
152            "A snapshot with the name already exists",
153            SnapshotExists
154        );
155        starts_err!(
156            s,
157            "Invalid user name or password for the guest OS",
158            AuthenticationFailed
159        );
160        starts_err!(
161            s,
162            "The VMware Tools are not running in the virtual machine: ",
163            ServiceIsNotRunning
164        );
165        starts_err!(s, "Unrecognized command: ", UnsupportedCommand);
166        VmError::from(Repr::Unknown(format!("Unknown error: {}", s)))
167    }
168
169    #[inline]
170    fn check(s: String) -> VmResult<String> {
171        match s.strip_prefix("Error: ") {
172            Some(s) => Err(Self::handle_error(s.trim())),
173            None => Ok(s),
174        }
175    }
176
177    fn exec(cmd: &mut Command) -> VmResult<String> {
178        let (stdout, stderr) = exec_cmd_utf8(cmd)?;
179        if !stderr.is_empty() {
180            Self::check(stderr)
181        } else {
182            Self::check(stdout)
183        }
184    }
185
186    /// Gets vmrun version, e.g., `vmrun version 1.17.0 build-17801498`.
187    pub fn version(&self) -> VmResult<String> {
188        let s = Self::exec(&mut self.cmd())?;
189        let v = s
190            .lines()
191            .nth(2)
192            .unwrap()
193            .strip_prefix("vmrun version ")
194            .unwrap();
195        Ok(v.to_string())
196    }
197
198    pub fn start_vm(&self, gui: bool) -> VmResult<()> {
199        let mut cmd = self.cmd();
200        cmd.args(&["start", self.get_vm()?]);
201        if !gui {
202            cmd.arg("nogui");
203        }
204        Self::exec(&mut cmd)?;
205        Ok(())
206    }
207
208    pub fn stop_vm(&self, hard_stop: Option<bool>) -> VmResult<()> {
209        let mut cmd = self.cmd();
210        cmd.args(&["stop", self.get_vm()?]);
211        if let Some(hard_stop) = hard_stop {
212            cmd.arg(if hard_stop { "soft" } else { "hard" });
213        }
214        Self::exec(&mut cmd)?;
215        Ok(())
216    }
217
218    pub fn reset_vm(&self, hard_stop: Option<bool>) -> VmResult<()> {
219        let mut cmd = self.cmd();
220        cmd.args(&["reset", self.get_vm()?]);
221        if let Some(hard_stop) = hard_stop {
222            cmd.arg(if hard_stop { "soft" } else { "hard" });
223        }
224        Self::exec(&mut cmd)?;
225        Ok(())
226    }
227
228    pub fn suspend_vm(&self, hard_stop: Option<bool>) -> VmResult<()> {
229        let mut cmd = self.cmd();
230        cmd.args(&["suspend", self.get_vm()?]);
231        if let Some(hard_stop) = hard_stop {
232            cmd.arg(if hard_stop { "soft" } else { "hard" });
233        }
234        Self::exec(&mut cmd)?;
235        Ok(())
236    }
237
238    pub fn pause_vm(&self) -> VmResult<()> {
239        let mut cmd = self.cmd();
240        cmd.args(&["pause", self.get_vm()?]);
241        Self::exec(&mut cmd)?;
242        Ok(())
243    }
244
245    pub fn unpause_vm(&self) -> VmResult<()> {
246        let mut cmd = self.cmd();
247        cmd.args(&["unpause", self.get_vm()?]);
248        Self::exec(&mut cmd)?;
249        Ok(())
250    }
251
252    pub fn list_all_vms(&self) -> VmResult<Vec<Vm>> {
253        let p = std::env::var("APPDATA").expect("Failed to get %APPDATA%");
254        let vms = if self.use_inventory {
255            read_vmware_inventory(&format!(r"{}\VMware\inventory.vmls", p))?
256        } else {
257            read_vmware_preferences(&format!(r"{}\VMware\preferences.ini", p))?
258        };
259
260        if vms.is_none() {
261            return vmerr!(Repr::Unknown(
262                "Cannot parse preferences file".to_string()
263            ));
264        }
265        Ok(vms.unwrap())
266    }
267
268    pub fn list_running_vms(&self) -> VmResult<Vec<Vm>> {
269        let mut cmd = self.cmd();
270        cmd.arg("list");
271        let s = Self::exec(&mut cmd)?;
272        let mut l = s.lines();
273        let n = match l.next() {
274            Some(s) => s
275                .strip_prefix("Total running VMs: ")
276                .expect("Unexpected list response")
277                .parse::<usize>()
278                .expect("Failed to parse to usize"),
279            None => return Ok(vec![]),
280        };
281        let mut ret = Vec::with_capacity(n);
282        for s in l {
283            ret.push(Vm {
284                id: None,
285                name: None,
286                path: Some(s.to_string()),
287            });
288        }
289        Ok(ret)
290    }
291
292    pub fn list_snapshots(&self) -> VmResult<Vec<Snapshot>> {
293        let mut cmd = self.cmd();
294        cmd.args(&["listSnapshots", self.get_vm()?]);
295        let s = Self::exec(&mut cmd)?;
296        let mut l = s.lines();
297        let n = match l.next() {
298            Some(s) => s
299                .strip_prefix("Total snapshots: ")
300                .expect("Unexpected list response")
301                .parse::<usize>()
302                .expect("Failed to parse to usize"),
303            None => return Ok(vec![]),
304        };
305        let mut ret = Vec::with_capacity(n);
306        for s in l {
307            ret.push(Snapshot {
308                id: None,
309                name: Some(s.to_string()),
310                detail: None,
311            });
312        }
313        Ok(ret)
314    }
315
316    pub fn is_snapshot_exists(&self, name: &str) -> VmResult<bool> {
317        let ss = self.list_snapshots()?;
318        Ok(ss.iter().any(|x| x.name.as_deref().unwrap() == name))
319    }
320
321    pub fn snapshot(&self, name: &str) -> VmResult<()> {
322        let mut cmd = self.cmd();
323        cmd.args(&["snapshot", self.get_vm()?, name]);
324        Self::exec(&mut cmd)?;
325        Ok(())
326    }
327
328    pub fn delete_snapshot(
329        &self,
330        name: &str,
331        delete_children: bool,
332    ) -> VmResult<()> {
333        let mut cmd = self.cmd();
334        cmd.args(&["deleteSnapshot", self.get_vm()?, name]);
335        if delete_children {
336            cmd.arg("andDeleteChildren");
337        }
338        Self::exec(&mut cmd)?;
339        Ok(())
340    }
341
342    pub fn revert_to_snapshot(&self, name: &str) -> VmResult<()> {
343        let mut cmd = self.cmd();
344        cmd.args(&["revertToSnapshot", self.get_vm()?, name]);
345        Self::exec(&mut cmd)?;
346        Ok(())
347    }
348
349    pub fn run_program_in_guest(
350        &self,
351        no_wait: bool,
352        active_window: bool,
353        interactive: bool,
354        program_args: &[&str],
355    ) -> VmResult<()> {
356        let mut cmd = self.cmd();
357        cmd.args(&["runProgramInGuest", self.get_vm()?]);
358        if no_wait {
359            cmd.arg("-noWait");
360        }
361        if active_window {
362            cmd.arg("-activeWindow");
363        }
364        if interactive {
365            cmd.arg("-interactive");
366        }
367        cmd.args(program_args);
368        Self::exec(&mut cmd)?;
369        Ok(())
370    }
371
372    pub fn file_exists_in_guest(&self, guest_path: &str) -> VmResult<bool> {
373        let s = Self::exec(self.cmd().args(&[
374            "fileExistsInGuest",
375            self.get_vm()?,
376            guest_path,
377        ]))?;
378        match s.as_str().trim() {
379            "The file exists." => Ok(true),
380            "The file does not exist." => Ok(false),
381            _ => vmerr!(ErrorKind::UnexpectedResponse(s)),
382        }
383    }
384
385    pub fn directory_exists_in_guest(
386        &self,
387        guest_path: &str,
388    ) -> VmResult<bool> {
389        let s = Self::exec(self.cmd().args(&[
390            "directoryExistsInGuest",
391            self.get_vm()?,
392            guest_path,
393        ]))?;
394        match s.as_str().trim() {
395            "The directory exists." => Ok(true),
396            "The directory does not exist." => Ok(false),
397            _ => vmerr!(ErrorKind::UnexpectedResponse(s)),
398        }
399    }
400
401    pub fn set_shared_folder_state(
402        &self,
403        name: &str,
404        host_path: &str,
405        writable: bool,
406    ) -> VmResult<()> {
407        let mut cmd = self.cmd();
408        cmd.args(&["setSharedFolderState", name, host_path]);
409        cmd.arg(if writable { "writable" } else { "readonly" });
410        Self::exec(&mut cmd)?;
411        Ok(())
412    }
413
414    pub fn add_shared_folder(
415        &self,
416        name: &str,
417        host_path: &str,
418    ) -> VmResult<()> {
419        let mut cmd = self.cmd();
420        cmd.args(&["addSharedFolder", name, host_path]);
421        Self::exec(&mut cmd)?;
422        Ok(())
423    }
424
425    pub fn remove_shared_folder(&self, name: &str) -> VmResult<()> {
426        let mut cmd = self.cmd();
427        cmd.args(&["removeSharedFolder", name]);
428        Self::exec(&mut cmd)?;
429        Ok(())
430    }
431
432    pub fn enable_shared_folders(
433        &self,
434        name: &str,
435        only_runtime: bool,
436    ) -> VmResult<()> {
437        let mut cmd = self.cmd();
438        cmd.args(&["enableSharedFolders", name]);
439        if only_runtime {
440            cmd.arg("runtime");
441        }
442        Self::exec(&mut cmd)?;
443        Ok(())
444    }
445
446    pub fn disable_shared_folders(
447        &self,
448        name: &str,
449        only_runtime: bool,
450    ) -> VmResult<()> {
451        let mut cmd = self.cmd();
452        cmd.args(&["disableSharedFolders", name]);
453        if only_runtime {
454            cmd.arg("runtime");
455        }
456        Self::exec(&mut cmd)?;
457        Ok(())
458    }
459
460    pub fn list_processes_in_guest(&self) -> VmResult<Vec<ProcInfo>> {
461        let s = Self::exec(
462            self.cmd().args(&["listProcessesInGuest", self.get_vm()?]),
463        )?;
464        let mut l = s.lines();
465        let n = match l.next() {
466            Some(s) => s
467                .strip_prefix("Process list: ")
468                .expect("Unexpected list response")
469                .parse::<usize>()
470                .expect("Failed to parse to usize"),
471            None => return Ok(vec![]),
472        };
473        let mut ret = Vec::with_capacity(n);
474        for l in l {
475            let v: Vec<&str> = l.splitn(3, ", ").collect();
476            assert_eq!(v.len(), 3);
477            let (pid, owner, cmd) = (v[0], v[1], v[2]);
478            let pid = pid.strip_prefix("pid=").expect("Unexpected pid");
479            let pid: u32 = pid.parse().unwrap();
480            let owner = owner
481                .strip_prefix("owner=")
482                .expect("Unexpected owner")
483                .to_string();
484            let cmd = cmd
485                .strip_prefix("cmd=")
486                .expect("Unexpected process command")
487                .to_string();
488            ret.push(ProcInfo { pid, owner, cmd })
489        }
490        Ok(ret)
491    }
492
493    pub fn kill_process_in_guest(&self, pid: u32) -> VmResult<()> {
494        Self::exec(self.cmd().args(&[
495            "killProcessInGuest",
496            self.get_vm()?,
497            &pid.to_string(),
498        ]))?;
499        Ok(())
500    }
501
502    pub fn delete_file_in_guest(&self, guest_path: &str) -> VmResult<()> {
503        Self::exec(self.cmd().args(&[
504            "deleteFileInGuest",
505            self.get_vm()?,
506            guest_path,
507        ]))?;
508        Ok(())
509    }
510
511    pub fn create_directory_in_guest(&self, guest_path: &str) -> VmResult<()> {
512        Self::exec(self.cmd().args(&[
513            "createDirectoryInGuest",
514            self.get_vm()?,
515            guest_path,
516        ]))?;
517        Ok(())
518    }
519
520    pub fn delete_directory_in_guest(&self, guest_path: &str) -> VmResult<()> {
521        Self::exec(self.cmd().args(&[
522            "deleteDirectoryInGuest",
523            self.get_vm()?,
524            guest_path,
525        ]))?;
526        Ok(())
527    }
528
529    /// Creates a temp file in guest.
530    ///
531    /// Returns the path to the temp file.
532    pub fn create_temp_file_in_guest(&self) -> VmResult<String> {
533        let s = Self::exec(
534            self.cmd().args(&["createTempFileInGuest", self.get_vm()?]),
535        )?;
536        Ok(s)
537    }
538
539    pub fn list_directory_in_guest(
540        &self,
541        guest_path: &str,
542    ) -> VmResult<Vec<String>> {
543        let s = Self::exec(self.cmd().args(&[
544            "listDirectoryInGuest",
545            self.get_vm()?,
546            guest_path,
547        ]))?;
548        Ok(s.lines().skip(1).map(|x| x.to_string()).collect())
549    }
550
551    pub fn copy_file_from_host_to_guest(
552        &self,
553        host_path: &str,
554        guest_path: &str,
555    ) -> VmResult<()> {
556        Self::exec(self.cmd().args(&[
557            "CopyFileFromHostToGuest",
558            self.get_vm()?,
559            host_path,
560            guest_path,
561        ]))?;
562        Ok(())
563    }
564
565    pub fn copy_file_from_guest_to_host(
566        &self,
567        guest_path: &str,
568        host_path: &str,
569    ) -> VmResult<()> {
570        Self::exec(self.cmd().args(&[
571            "CopyFileFromGuestToHost",
572            self.get_vm()?,
573            guest_path,
574            host_path,
575        ]))?;
576        Ok(())
577    }
578
579    pub fn rename_file_in_guest(
580        &self,
581        old_path: &str,
582        new_path: &str,
583    ) -> VmResult<()> {
584        Self::exec(self.cmd().args(&[
585            "renameFileInGuest",
586            self.get_vm()?,
587            old_path,
588            new_path,
589        ]))?;
590        Ok(())
591    }
592
593    pub fn type_keystrokes_in_guest(&self, keystroke: &str) -> VmResult<()> {
594        Self::exec(self.cmd().args(&[
595            "typeKeystrokesInGuest",
596            self.get_vm()?,
597            keystroke,
598        ]))?;
599        Ok(())
600    }
601
602    pub fn capture_screen(&self, host_path: &str) -> VmResult<()> {
603        Self::exec(self.cmd().args(&[
604            "captureScreen",
605            self.get_vm()?,
606            host_path,
607        ]))?;
608        Ok(())
609    }
610
611    pub fn write_variable(&self, variable: WriteVar) -> VmResult<()> {
612        let mut cmd = self.cmd();
613        cmd.args(&["writeVariable", self.get_vm()?]);
614        match variable {
615            WriteVar::GuestVar(name, value) => {
616                cmd.args(&["guestVar", name, value])
617            }
618            WriteVar::RuntimeConfig(name, value) => {
619                cmd.args(&["runtimeConfig", name, value])
620            }
621            WriteVar::GuestEnv(name, value) => {
622                cmd.args(&["guestEnv", name, value])
623            }
624        };
625        Self::exec(&mut cmd)?;
626        Ok(())
627    }
628
629    pub fn read_variable(&self, variable: ReadVar) -> VmResult<Option<String>> {
630        let mut cmd = self.cmd();
631        cmd.args(&["readVariable", self.get_vm()?]);
632        match variable {
633            ReadVar::GuestVar(name) => cmd.args(&["guestVar", name]),
634            ReadVar::RuntimeConfig(name) => cmd.args(&["runtimeConfig", name]),
635            ReadVar::GuestEnv(name) => cmd.args(&["guestEnv", name]),
636        };
637        let s = Self::exec(&mut cmd)?;
638        Ok(if s.is_empty() { None } else { Some(s) })
639    }
640
641    pub fn get_guest_ip_address(&self, wait: bool) -> VmResult<String> {
642        let mut cmd = self.cmd();
643        cmd.args(&["getGuestIPAddress", self.get_vm()?]);
644        if wait {
645            cmd.arg("-wait");
646        }
647        let s = Self::exec(&mut cmd)?;
648        Ok(s)
649    }
650
651    pub fn install_tools(&self) -> VmResult<()> {
652        Self::exec(self.cmd().args(&["installTools", self.get_vm()?]))?;
653        Ok(())
654    }
655
656    pub fn check_tools_state(&self) -> VmResult<bool> {
657        let s =
658            Self::exec(self.cmd().args(&["checkToolsState", self.get_vm()?]))?;
659        match s.as_str() {
660            "installed" => Ok(true),
661            "unknown" => Ok(false),
662            "running" => Ok(true),
663            _ => vmerr!(ErrorKind::UnexpectedResponse(s)),
664        }
665    }
666
667    pub fn delete_vm(&self) -> VmResult<()> {
668        Self::exec(self.cmd().args(&["deleteVM", self.get_vm()?]))?;
669        Ok(())
670    }
671}
672
673impl VmCmd for VmRun {
674    fn list_vms(&self) -> VmResult<Vec<Vm>> { self.list_all_vms() }
675
676    /// Due to the specification of vmrun, VmRun does not support this function.
677    fn set_vm_by_id(&mut self, _id: &str) -> VmResult<()> {
678        vmerr!(ErrorKind::UnsupportedCommand)
679    }
680
681    fn set_vm_by_name(&mut self, name: &str) -> VmResult<()> {
682        for vm in self.list_vms()? {
683            if vm.name.as_deref() == Some(name) {
684                self.vm_path = vm.path;
685                return Ok(());
686            }
687        }
688        vmerr!(ErrorKind::VmNotFound)
689    }
690
691    fn set_vm_by_path(&mut self, path: &str) -> VmResult<()> {
692        for vm in self.list_vms()? {
693            if vm.path.as_deref() == Some(path) {
694                self.vm_path = vm.path;
695                return Ok(());
696            }
697        }
698        vmerr!(ErrorKind::VmNotFound)
699    }
700}
701
702impl PowerCmd for VmRun {
703    fn start(&self) -> VmResult<()> {
704        if self.is_running()? {
705            return vmerr!(ErrorKind::InvalidPowerState(VmPowerState::Running));
706        }
707        self.start_vm(self.gui)
708    }
709
710    fn stop<D: Into<Option<Duration>>>(&self, _timeout: D) -> VmResult<()> {
711        self.stop_vm(Some(false))
712    }
713
714    fn hard_stop(&self) -> VmResult<()> { self.stop_vm(Some(true)) }
715
716    fn suspend(&self) -> VmResult<()> { self.suspend_vm(Some(true)) }
717
718    fn resume(&self) -> VmResult<()> { self.start() }
719
720    fn is_running(&self) -> VmResult<bool> {
721        let vm_path = self.get_vm()?;
722        Ok(self
723            .list_running_vms()?
724            .iter()
725            .any(|vm| vm.path.as_deref().unwrap() == vm_path))
726    }
727
728    fn reboot<D: Into<Option<Duration>>>(&self, _timeout: D) -> VmResult<()> {
729        self.reset_vm(Some(false))
730    }
731
732    fn hard_reboot(&self) -> VmResult<()> { self.reset_vm(Some(true)) }
733
734    fn pause(&self) -> VmResult<()> { self.pause_vm() }
735
736    fn unpause(&self) -> VmResult<()> { self.unpause_vm() }
737}
738
739impl SnapshotCmd for VmRun {
740    fn list_snapshots(&self) -> VmResult<Vec<Snapshot>> {
741        Self::list_snapshots(self)
742    }
743
744    fn take_snapshot(&self, name: &str) -> VmResult<()> { self.snapshot(name) }
745
746    fn revert_snapshot(&self, name: &str) -> VmResult<()> {
747        if !self.is_snapshot_exists(name)? {
748            return vmerr!(ErrorKind::SnapshotNotFound);
749        }
750        self.revert_to_snapshot(name)
751    }
752
753    fn delete_snapshot(&self, name: &str) -> VmResult<()> {
754        if !self.is_snapshot_exists(name)? {
755            return vmerr!(ErrorKind::SnapshotNotFound);
756        }
757        self.delete_snapshot(name, true)
758    }
759}
760
761impl GuestCmd for VmRun {
762    fn exec_cmd(&self, guest_args: &[&str]) -> VmResult<()> {
763        self.run_program_in_guest(true, true, false, guest_args)
764    }
765
766    fn copy_from_guest_to_host(
767        &self,
768        from_guest_path: &str,
769        to_host_path: &str,
770    ) -> VmResult<()> {
771        self.copy_file_from_guest_to_host(from_guest_path, to_host_path)
772    }
773
774    fn copy_from_host_to_guest(
775        &self,
776        from_host_path: &str,
777        to_guest_path: &str,
778    ) -> VmResult<()> {
779        fn get_file_name<'a>(
780            p: &'a std::path::Path,
781            from_host_path: &str,
782        ) -> VmResult<Cow<'a, str>> {
783            p.file_name().map(|x| x.to_string_lossy()).ok_or_else(|| {
784                vmerr!(@r ErrorKind::InvalidParameter(
785                    from_host_path.to_string()
786                ))
787            })
788        }
789        let host_path = std::path::Path::new(from_host_path);
790        if !host_path.exists() {
791            return vmerr!(ErrorKind::HostFileNotFound);
792        }
793        if to_guest_path.is_empty() {
794            return vmerr!(ErrorKind::GuestFileNotFound);
795        }
796
797        // copyFileFromHostToGuest cannot copy if the specified guest path is a directory.
798        if to_guest_path.ends_with('\\') || to_guest_path.ends_with('/') {
799            // directory
800            let file_name = get_file_name(host_path, from_host_path)?;
801            let to_guest_path = format!("{}{}", to_guest_path, file_name);
802            self.copy_file_from_host_to_guest(from_host_path, &to_guest_path)
803        } else if self.directory_exists_in_guest(to_guest_path)? {
804            // directory
805            let file_name = get_file_name(host_path, from_host_path)?;
806            let guest_path_separator =
807                if to_guest_path.chars().next().unwrap() == '/' {
808                    '/'
809                } else {
810                    '\\'
811                };
812            let to_guest_path = format!(
813                "{}{}{}",
814                to_guest_path, guest_path_separator, file_name
815            );
816            self.copy_file_from_host_to_guest(from_host_path, &to_guest_path)
817        } else {
818            // file name
819            self.copy_file_from_host_to_guest(from_host_path, to_guest_path)
820        }
821    }
822}