mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
[WIP] Fix edge upgrade edge cases
This commit is contained in:
parent
89b533f949
commit
1b6ee722f3
6 changed files with 124 additions and 29 deletions
|
@ -1,14 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
<DirectoryRef Id="INSTALLDIR">
|
||||
<Directory Id="BinaryDir" Name="binary" />
|
||||
<Directory Id="IntelDir" Name="intel" />
|
||||
<DirectoryRef Id="TARGETDIR">
|
||||
<Directory Id="CommonAppDataFolder" Name="CommonAppData">
|
||||
<Directory Id="PortmasterDir" Name="Portmaster">
|
||||
<Directory Id="IntelDir" Name="intel" />
|
||||
</Directory>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
</Fragment>
|
||||
|
||||
<Fragment>
|
||||
<Component Id="BinaryFiles" Directory="BinaryDir" Guid="850cdd31-424d-45f5-b8f0-95df950ebd0d">
|
||||
<Component Id="BinaryFiles" Directory="INSTALLDIR" Guid="850cdd31-424d-45f5-b8f0-95df950ebd0d">
|
||||
<File Id="BinIndexJson" Source="..\..\..\..\binaries\bin-index.json" />
|
||||
<File Id="PortmasterCoreExe" Source="..\..\..\..\binaries\portmaster-core.exe" />
|
||||
<File Id="PortmasterKextSys" Source="..\..\..\..\binaries\portmaster-kext.sys" />
|
||||
|
|
|
@ -1,13 +1,34 @@
|
|||
!define NSIS_HOOK_POSTINSTALL "NSIS_HOOK_POSTINSTALL_"
|
||||
!macro NSIS_HOOK_PREINSTALL
|
||||
; Current working directory is <project-dir>\target\release\nsis\x64
|
||||
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
File "..\..\..\..\binaries\bin-index.json"
|
||||
File "..\..\..\..\binaries\portmaster-core.exe"
|
||||
File "..\..\..\..\binaries\portmaster-kext.sys"
|
||||
File "..\..\..\..\binaries\portmaster.zip"
|
||||
File "..\..\..\..\binaries\assets.zip"
|
||||
|
||||
SetOutPath "$COMMONPROGRAMDATA\Portmaster\intel"
|
||||
|
||||
File "..\..\..\..\binaries\intel-index.json"
|
||||
File "..\..\..\..\binaries\base.dsdl"
|
||||
File "..\..\..\..\binaries\geoipv4.mmdb"
|
||||
File "..\..\..\..\binaries\geoipv6.mmdb"
|
||||
File "..\..\..\..\binaries\index.dsd"
|
||||
File "..\..\..\..\binaries\intermediate.dsdl"
|
||||
File "..\..\..\..\binaries\urgent.dsdl"
|
||||
|
||||
; restire previous state
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
!macro NSIS_HOOK_POSTINSTALL_
|
||||
ExecWait '"$INSTDIR\portmaster-start.exe" install core-service --data="$INSTDIR\data"'
|
||||
!macroend
|
||||
|
||||
!macro NSIS_HOOK_POSTINSTALL
|
||||
ExecWait 'sc.exe create PortmasterCore binPath= "$INSTDIR\portmaster-core.exe" --data="$COMMONPROGRAMDATA\Portmaster\data"'
|
||||
!macroend
|
||||
|
||||
!define NSIS_HOOK_PREUNINSTALL "NSIS_HOOK_PREUNINSTALL_"
|
||||
|
||||
!macro NSIS_HOOK_PREUNINSTALL_
|
||||
!macro NSIS_HOOK_PREUNINSTALL
|
||||
ExecWait 'sc.exe stop PortmasterCore'
|
||||
ExecWait 'sc.exe delete PortmasterCore'
|
||||
!macroend
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<Fragment>
|
||||
<CustomAction Id="InstallPortmasterService"
|
||||
Directory="INSTALLDIR"
|
||||
ExeCommand="sc.exe create PortmasterCore binPath= "[INSTALLDIR]binary\portmaster-core.exe --data [INSTALLDIR]data""
|
||||
ExeCommand="sc.exe create PortmasterCore binPath= "[INSTALLDIR]portmaster-core.exe --data [CommonAppDataFolder]Portmaster\data""
|
||||
Execute="commit"
|
||||
Return="check"
|
||||
Impersonate="no"
|
||||
|
|
|
@ -134,8 +134,8 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
}
|
||||
binaryUpdateIndex = updates.UpdateIndex{
|
||||
Directory: binaryFolder, // Default: C:/Program Files/Portmaster
|
||||
DownloadDirectory: os.ExpandEnv("%ProgramData%/Portmaster/new_binary"),
|
||||
PurgeDirectory: os.ExpandEnv("%ProgramData%/Portmaster/old_binary"),
|
||||
DownloadDirectory: os.ExpandEnv("$ProgramData/Portmaster/new_binary"),
|
||||
PurgeDirectory: filepath.Join(binaryFolder, "old_binary"), // Default: C:/Program Files/Portmaster/old_binary
|
||||
Ignore: []string{"databases", "intel", "config.json"},
|
||||
IndexURLs: []string{"http://192.168.88.11:8000/test-binary.json"},
|
||||
IndexFile: "bin-index.json",
|
||||
|
@ -144,9 +144,9 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
}
|
||||
|
||||
intelUpdateIndex = updates.UpdateIndex{
|
||||
Directory: os.ExpandEnv("%ProgramData%/Portmaster/intel"),
|
||||
DownloadDirectory: os.ExpandEnv("%ProgramData%/Portmaster/new_intel"),
|
||||
PurgeDirectory: os.ExpandEnv("%ProgramData%/Portmaster/old_intel"),
|
||||
Directory: os.ExpandEnv("$ProgramData/Portmaster/intel"),
|
||||
DownloadDirectory: os.ExpandEnv("$ProgramData/Portmaster/new_intel"),
|
||||
PurgeDirectory: os.ExpandEnv("$ProgramData/Portmaster/old_intel"),
|
||||
IndexURLs: []string{"http://192.168.88.11:8000/test-intel.json"},
|
||||
IndexFile: "intel-index.json",
|
||||
AutoApply: true,
|
||||
|
|
|
@ -36,7 +36,7 @@ const (
|
|||
// fetching resources from the update server.
|
||||
var UserAgent = fmt.Sprintf("Portmaster (%s %s)", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
// UpdateIndex holds the configuration for the updates module
|
||||
// UpdateIndex holds the configuration for the updates module.
|
||||
type UpdateIndex struct {
|
||||
Directory string
|
||||
DownloadDirectory string
|
||||
|
@ -143,7 +143,7 @@ func (u *Updates) applyUpdates(_ *mgr.WorkerCtx) error {
|
|||
currentBundle := u.registry.bundle
|
||||
downloadBundle := u.downloader.bundle
|
||||
log.Infof("update: starting update: %s %s -> %s", currentBundle.Name, currentBundle.Version, downloadBundle.Version)
|
||||
err := u.registry.performUpgrade(u.downloader.dir, u.downloader.indexFile)
|
||||
err := u.registry.performRecoverableUpgrade(u.downloader.dir, u.downloader.indexFile)
|
||||
if err != nil {
|
||||
// TODO(vladimir): Send notification to UI
|
||||
log.Errorf("updates: failed to apply updates: %s", err)
|
||||
|
|
|
@ -3,6 +3,7 @@ package updates
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
@ -67,11 +68,8 @@ func (r *Registry) performUpgrade(downloadDir string, indexFile string) error {
|
|||
return fmt.Errorf("invalid update: %w", err)
|
||||
}
|
||||
|
||||
// Create purge dir.
|
||||
err = os.MkdirAll(r.purgeDir, defaultDirMode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
// Make sure purge dir is empty.
|
||||
_ = os.RemoveAll(r.purgeDir)
|
||||
|
||||
// Read all files in the current version folder.
|
||||
files, err := os.ReadDir(r.dir)
|
||||
|
@ -79,12 +77,18 @@ func (r *Registry) performUpgrade(downloadDir string, indexFile string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Create purge dir. Calling this after ReadDIr is important.
|
||||
err = os.MkdirAll(r.purgeDir, defaultDirMode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
|
||||
// Move current version files into purge folder.
|
||||
log.Debugf("updates: removing the old version")
|
||||
for _, file := range files {
|
||||
currentFilepath := filepath.Join(r.dir, file.Name())
|
||||
purgePath := filepath.Join(r.purgeDir, file.Name())
|
||||
err := os.Rename(currentFilepath, purgePath)
|
||||
err := moveFile(currentFilepath, purgePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to move file %s: %w", currentFilepath, err)
|
||||
}
|
||||
|
@ -93,7 +97,7 @@ func (r *Registry) performUpgrade(downloadDir string, indexFile string) error {
|
|||
// Move the new index file
|
||||
log.Debugf("updates: installing the new version")
|
||||
newIndexFile := filepath.Join(r.dir, indexFile)
|
||||
err = os.Rename(indexFilepath, newIndexFile)
|
||||
err = moveFile(indexFilepath, newIndexFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to move index file %s: %w", indexFile, err)
|
||||
}
|
||||
|
@ -102,9 +106,9 @@ func (r *Registry) performUpgrade(downloadDir string, indexFile string) error {
|
|||
for _, artifact := range bundle.Artifacts {
|
||||
fromFilepath := filepath.Join(downloadDir, artifact.Filename)
|
||||
toFilepath := filepath.Join(r.dir, artifact.Filename)
|
||||
err = os.Rename(fromFilepath, toFilepath)
|
||||
err = moveFile(fromFilepath, toFilepath)
|
||||
if err != nil {
|
||||
log.Errorf("failed to move file %s: %s", fromFilepath, err)
|
||||
return fmt.Errorf("failed to move file %s: %w", fromFilepath, err)
|
||||
} else {
|
||||
log.Debugf("updates: %s moved", artifact.Filename)
|
||||
}
|
||||
|
@ -125,9 +129,76 @@ func (r *Registry) performUpgrade(downloadDir string, indexFile string) error {
|
|||
|
||||
log.Infof("updates: update complete")
|
||||
|
||||
err = r.CleanOldFiles()
|
||||
return nil
|
||||
}
|
||||
|
||||
func moveFile(currentPath, newPath string) error {
|
||||
err := os.Rename(currentPath, newPath)
|
||||
if err == nil {
|
||||
// Moving was successful return
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("updates: failed to move '%s' fallback to copy+delete: %s -> %s", err, currentPath, newPath)
|
||||
|
||||
// Failed to move, try copy and delete
|
||||
currentFile, err := os.Open(currentPath)
|
||||
if err != nil {
|
||||
log.Warningf("updates: error while cleaning old file: %s", err)
|
||||
return err
|
||||
}
|
||||
defer func() { _ = currentFile.Close() }()
|
||||
|
||||
newFile, err := os.Create(newPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = newFile.Close() }()
|
||||
|
||||
_, err = io.Copy(newFile, currentFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure file is closed before deletion.
|
||||
_ = currentFile.Close()
|
||||
currentFile = nil
|
||||
|
||||
err = os.Remove(currentPath)
|
||||
if err != nil {
|
||||
log.Errorf("updates: failed to delete while moving file: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) performRecoverableUpgrade(downloadDir string, indexFile string) error {
|
||||
upgradeError := r.performUpgrade(downloadDir, indexFile)
|
||||
if upgradeError != nil {
|
||||
err := r.recover()
|
||||
recoverStatus := "(recovery successful)"
|
||||
if err != nil {
|
||||
recoverStatus = "(recovery failed)"
|
||||
log.Errorf("updates: failed to recover: %s", err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("upgrade failed: %w %s", upgradeError, recoverStatus)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) recover() error {
|
||||
files, err := os.ReadDir(r.purgeDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
recoverPath := filepath.Join(r.purgeDir, file.Name())
|
||||
currentFilepath := filepath.Join(r.dir, file.Name())
|
||||
err := moveFile(recoverPath, currentFilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Add table
Reference in a new issue