pub struct Move<'a> { /* private fields */ }
Expand description
Moves a file from the given path to the specified destination.
source
and dest
must be on the same filesystem.
If replace_using_temp
is specified, the destination file will be
replaced using the given temporary path.
- Errors:
- Io - copying / renaming
Implementations§
source§impl<'a> Move<'a>
impl<'a> Move<'a>
sourcepub fn from_source(source: &'a Path) -> Move<'a>
pub fn from_source(source: &'a Path) -> Move<'a>
Specify source file
Examples found in repository?
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
fn copy_files_and_run<R: Read + Seek>(
archive_buffer: R,
_extract_path: &Path,
with_elevated_task: bool,
msiexec_args: &[&str],
) -> Result {
// FIXME: We need to create a memory buffer with the MSI and then run it.
// (instead of extracting the MSI to a temp path)
//
// The tricky part is the MSI need to be exposed and spawned so the memory allocation
// shouldn't drop but we should be able to pass the reference so we can drop it once the installation
// is done, otherwise we have a huge memory leak.
let tmp_dir = tempfile::Builder::new().tempdir()?.into_path();
// extract the buffer to the tmp_dir
// we extract our signed archive into our final directory without any temp file
let mut extractor = Extract::from_cursor(archive_buffer, ArchiveFormat::Zip);
// extract the msi
extractor.extract_into(&tmp_dir)?;
let paths = read_dir(&tmp_dir)?;
// This consumes the TempDir without deleting directory on the filesystem,
// meaning that the directory will no longer be automatically deleted.
for path in paths {
let found_path = path?.path();
// we support 2 type of files exe & msi for now
// If it's an `exe` we expect an installer not a runtime.
if found_path.extension() == Some(OsStr::new("exe")) {
// Run the EXE
Command::new(found_path)
.spawn()
.expect("installer failed to start");
exit(0);
} else if found_path.extension() == Some(OsStr::new("msi")) {
if with_elevated_task {
if let Some(bin_name) = current_exe()
.ok()
.and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
.and_then(|s| s.into_string().ok())
{
let product_name = bin_name.replace(".exe", "");
// Check if there is a task that enables the updater to skip the UAC prompt
let update_task_name = format!("Update {} - Skip UAC", product_name);
if let Ok(output) = Command::new("schtasks")
.arg("/QUERY")
.arg("/TN")
.arg(update_task_name.clone())
.output()
{
if output.status.success() {
// Rename the MSI to the match file name the Skip UAC task is expecting it to be
let temp_msi = tmp_dir.with_file_name(bin_name).with_extension("msi");
Move::from_source(&found_path)
.to_dest(&temp_msi)
.expect("Unable to move update MSI");
let exit_status = Command::new("schtasks")
.arg("/RUN")
.arg("/TN")
.arg(update_task_name)
.status()
.expect("failed to start updater task");
if exit_status.success() {
// Successfully launched task that skips the UAC prompt
exit(0);
}
}
// Failed to run update task. Following UAC Path
}
}
}
// we need to wrap the current exe path in quotes for Start-Process
let mut current_exe_arg = std::ffi::OsString::new();
current_exe_arg.push("\"");
current_exe_arg.push(current_exe()?);
current_exe_arg.push("\"");
let mut msi_path_arg = std::ffi::OsString::new();
msi_path_arg.push("\"\"\"");
msi_path_arg.push(&found_path);
msi_path_arg.push("\"\"\"");
// run the installer and relaunch the application
let system_root = std::env::var("SYSTEMROOT");
let powershell_path = system_root.as_ref().map_or_else(
|_| "powershell.exe".to_string(),
|p| format!("{p}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"),
);
let powershell_install_res = Command::new(powershell_path)
.args(["-NoProfile", "-windowstyle", "hidden"])
.args([
"Start-Process",
"-Wait",
"-FilePath",
"$env:SYSTEMROOT\\System32\\msiexec.exe",
"-ArgumentList",
])
.arg("/i,")
.arg(msi_path_arg)
.arg(format!(", {}, /promptrestart;", msiexec_args.join(", ")))
.arg("Start-Process")
.arg(current_exe_arg)
.spawn();
if powershell_install_res.is_err() {
// fallback to running msiexec directly - relaunch won't be available
// we use this here in case powershell fails in an older machine somehow
let msiexec_path = system_root.as_ref().map_or_else(
|_| "msiexec.exe".to_string(),
|p| format!("{p}\\System32\\msiexec.exe"),
);
let _ = Command::new(msiexec_path)
.arg("/i")
.arg(found_path)
.args(msiexec_args)
.arg("/promptrestart")
.spawn();
}
exit(0);
}
}
Ok(())
}
sourcepub fn replace_using_temp(&mut self, temp: &'a Path) -> &mut Self
pub fn replace_using_temp(&mut self, temp: &'a Path) -> &mut Self
If specified and the destination file already exists, the “destination” file will be moved to the given temporary location before the “source” file is moved to the “destination” file.
In the event of an io
error while renaming “source” to “destination”,
the temporary file will be moved back to “destination”.
The temp
dir must be explicitly provided since rename
operations require
files to live on the same filesystem.
sourcepub fn to_dest(&self, dest: &Path) -> Result<()>
pub fn to_dest(&self, dest: &Path) -> Result<()>
Move source file to specified destination (replace whole directory)
Examples found in repository?
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
fn copy_files_and_run<R: Read + Seek>(
archive_buffer: R,
_extract_path: &Path,
with_elevated_task: bool,
msiexec_args: &[&str],
) -> Result {
// FIXME: We need to create a memory buffer with the MSI and then run it.
// (instead of extracting the MSI to a temp path)
//
// The tricky part is the MSI need to be exposed and spawned so the memory allocation
// shouldn't drop but we should be able to pass the reference so we can drop it once the installation
// is done, otherwise we have a huge memory leak.
let tmp_dir = tempfile::Builder::new().tempdir()?.into_path();
// extract the buffer to the tmp_dir
// we extract our signed archive into our final directory without any temp file
let mut extractor = Extract::from_cursor(archive_buffer, ArchiveFormat::Zip);
// extract the msi
extractor.extract_into(&tmp_dir)?;
let paths = read_dir(&tmp_dir)?;
// This consumes the TempDir without deleting directory on the filesystem,
// meaning that the directory will no longer be automatically deleted.
for path in paths {
let found_path = path?.path();
// we support 2 type of files exe & msi for now
// If it's an `exe` we expect an installer not a runtime.
if found_path.extension() == Some(OsStr::new("exe")) {
// Run the EXE
Command::new(found_path)
.spawn()
.expect("installer failed to start");
exit(0);
} else if found_path.extension() == Some(OsStr::new("msi")) {
if with_elevated_task {
if let Some(bin_name) = current_exe()
.ok()
.and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
.and_then(|s| s.into_string().ok())
{
let product_name = bin_name.replace(".exe", "");
// Check if there is a task that enables the updater to skip the UAC prompt
let update_task_name = format!("Update {} - Skip UAC", product_name);
if let Ok(output) = Command::new("schtasks")
.arg("/QUERY")
.arg("/TN")
.arg(update_task_name.clone())
.output()
{
if output.status.success() {
// Rename the MSI to the match file name the Skip UAC task is expecting it to be
let temp_msi = tmp_dir.with_file_name(bin_name).with_extension("msi");
Move::from_source(&found_path)
.to_dest(&temp_msi)
.expect("Unable to move update MSI");
let exit_status = Command::new("schtasks")
.arg("/RUN")
.arg("/TN")
.arg(update_task_name)
.status()
.expect("failed to start updater task");
if exit_status.success() {
// Successfully launched task that skips the UAC prompt
exit(0);
}
}
// Failed to run update task. Following UAC Path
}
}
}
// we need to wrap the current exe path in quotes for Start-Process
let mut current_exe_arg = std::ffi::OsString::new();
current_exe_arg.push("\"");
current_exe_arg.push(current_exe()?);
current_exe_arg.push("\"");
let mut msi_path_arg = std::ffi::OsString::new();
msi_path_arg.push("\"\"\"");
msi_path_arg.push(&found_path);
msi_path_arg.push("\"\"\"");
// run the installer and relaunch the application
let system_root = std::env::var("SYSTEMROOT");
let powershell_path = system_root.as_ref().map_or_else(
|_| "powershell.exe".to_string(),
|p| format!("{p}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"),
);
let powershell_install_res = Command::new(powershell_path)
.args(["-NoProfile", "-windowstyle", "hidden"])
.args([
"Start-Process",
"-Wait",
"-FilePath",
"$env:SYSTEMROOT\\System32\\msiexec.exe",
"-ArgumentList",
])
.arg("/i,")
.arg(msi_path_arg)
.arg(format!(", {}, /promptrestart;", msiexec_args.join(", ")))
.arg("Start-Process")
.arg(current_exe_arg)
.spawn();
if powershell_install_res.is_err() {
// fallback to running msiexec directly - relaunch won't be available
// we use this here in case powershell fails in an older machine somehow
let msiexec_path = system_root.as_ref().map_or_else(
|_| "msiexec.exe".to_string(),
|p| format!("{p}\\System32\\msiexec.exe"),
);
let _ = Command::new(msiexec_path)
.arg("/i")
.arg(found_path)
.args(msiexec_args)
.arg("/promptrestart")
.spawn();
}
exit(0);
}
}
Ok(())
}
sourcepub fn walk_to_dest(&self, dest: &Path) -> Result<()>
pub fn walk_to_dest(&self, dest: &Path) -> Result<()>
Walk in the source and copy all files and create directories if needed by replacing existing elements. (equivalent to a cp -R)