Struct tauri::api::file::Move

source ·
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§

Specify source file

Examples found in repository?
src/updater/core.rs (line 773)
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(())
}

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.

Move source file to specified destination (replace whole directory)

Examples found in repository?
src/updater/core.rs (line 774)
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(())
}

Walk in the source and copy all files and create directories if needed by replacing existing elements. (equivalent to a cp -R)

Trait Implementations§

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more