From 5b80a0b95e2c3e8f5b8e39ac6bd9748015d7b953 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 4 Jul 2019 13:54:51 +0200 Subject: [PATCH] Add downloading of main components, fix dir permissions Fixes #2, #3 --- updates/fetch.go | 16 ++++++++------- updates/file.go | 1 + updates/filename.go | 2 ++ updates/get.go | 8 +++++--- updates/latest.go | 2 ++ updates/main.go | 46 ++++++++++++------------------------------- updates/updater.go | 48 +++++++++++++++++++++++++++++++++------------ 7 files changed, 67 insertions(+), 56 deletions(-) diff --git a/updates/fetch.go b/updates/fetch.go index f1cd07de..4d3d3c02 100644 --- a/updates/fetch.go +++ b/updates/fetch.go @@ -15,6 +15,7 @@ import ( "github.com/google/renameio" "github.com/safing/portbase/log" + "github.com/safing/portbase/utils" ) var ( @@ -37,15 +38,15 @@ func fetchFile(realFilepath, updateFilepath string, tries int) error { // check destination dir dirPath := filepath.Dir(realFilepath) - err = CheckDir(dirPath) + err = utils.EnsureDirectory(dirPath, 0755) if err != nil { - return fmt.Errorf("updates: could not create updates folder: %s", dirPath) + return fmt.Errorf("could not create updates folder: %s", dirPath) } // open file for writing - atomicFile, err := renameio.TempFile(filepath.Join(updateStoragePath, "tmp"), realFilepath) + atomicFile, err := renameio.TempFile(downloadTmpPath, realFilepath) if err != nil { - return fmt.Errorf("updates: could not create temp file for download: %s", err) + return fmt.Errorf("could not create temp file for download: %s", err) } defer atomicFile.Cleanup() @@ -62,7 +63,7 @@ func fetchFile(realFilepath, updateFilepath string, tries int) error { return fmt.Errorf("failed downloading %s: %s", downloadURL, err) } if resp.ContentLength != n { - return fmt.Errorf("download unfinished, written %d out of %d bytes.", n, resp.ContentLength) + return fmt.Errorf("download unfinished, written %d out of %d bytes", n, resp.ContentLength) } // finalize file @@ -72,7 +73,8 @@ func fetchFile(realFilepath, updateFilepath string, tries int) error { } // set permissions if runtime.GOOS != "windows" { - err = os.Chmod(realFilepath, 0644) + // FIXME: only set executable files to 0755, set other to 0644 + err = os.Chmod(realFilepath, 0755) if err != nil { log.Warningf("updates: failed to set permissions on downloaded file %s: %s", realFilepath, err) } @@ -108,7 +110,7 @@ func fetchData(downloadPath string, tries int) ([]byte, error) { return nil, fmt.Errorf("failed downloading %s: %s", downloadURL, err) } if resp.ContentLength != n { - return nil, fmt.Errorf("download unfinished, written %d out of %d bytes.", n, resp.ContentLength) + return nil, fmt.Errorf("download unfinished, written %d out of %d bytes", n, resp.ContentLength) } return buf.Bytes(), nil diff --git a/updates/file.go b/updates/file.go index 4850dc58..5fbab847 100644 --- a/updates/file.go +++ b/updates/file.go @@ -7,6 +7,7 @@ type File struct { stable bool } +// NewFile combines update file attributes into an easy to use object. func NewFile(filepath string, version string, stable bool) *File { return &File{ filepath: filepath, diff --git a/updates/filename.go b/updates/filename.go index ae565c24..1768255d 100644 --- a/updates/filename.go +++ b/updates/filename.go @@ -8,6 +8,7 @@ import ( var versionRegex = regexp.MustCompile("_v[0-9]+-[0-9]+-[0-9]+b?") +// GetIdentifierAndVersion splits the given file path into its identifier and version. func GetIdentifierAndVersion(versionedPath string) (identifier, version string, ok bool) { // extract version rawVersion := versionRegex.FindString(versionedPath) @@ -27,6 +28,7 @@ func GetIdentifierAndVersion(versionedPath string) (identifier, version string, return versionedPath[:i] + versionedPath[i+len(rawVersion):], version, true } +// GetVersionedPath combines the identifier and version and returns it as a file path. func GetVersionedPath(identifier, version string) (versionedPath string) { // split in half splittedFilePath := strings.SplitN(identifier, ".", 2) diff --git a/updates/get.go b/updates/get.go index 3ac13b8e..9d4b7ac8 100644 --- a/updates/get.go +++ b/updates/get.go @@ -9,10 +9,12 @@ import ( "runtime" "github.com/safing/portbase/log" + "github.com/safing/portbase/utils" ) +// Errors var ( - ErrNotFound = errors.New("the requested file could not be found") + ErrNotFound = errors.New("the requested file could not be found") ErrNotAvailableLocally = errors.New("the requested file is not available locally") ) @@ -81,12 +83,12 @@ func loadOrFetchFile(identifier string, fetch bool) (*File, error) { } // check download dir - err := CheckDir(filepath.Join(updateStoragePath, "tmp")) + err := utils.EnsureDirectory(downloadTmpPath, 0755) if err != nil { return nil, fmt.Errorf("could not prepare tmp directory for download: %s", err) } - if (!fetch) { + if !fetch { return nil, ErrNotAvailableLocally } diff --git a/updates/latest.go b/updates/latest.go index 83bb11b6..57109a7f 100644 --- a/updates/latest.go +++ b/updates/latest.go @@ -62,6 +62,7 @@ func LoadLatest() error { return nil } +// ScanForLatest scan the local update directory and returns a map of the latest/newest component versions. func ScanForLatest(baseDir string, hardFail bool) (latest map[string]string, lastError error) { var added int latest = make(map[string]string) @@ -117,6 +118,7 @@ func ScanForLatest(baseDir string, hardFail bool) (latest map[string]string, las return latest, nil } +// LoadIndexes loads the current update indexes from disk. func LoadIndexes() error { data, err := ioutil.ReadFile(filepath.Join(updateStoragePath, "stable.json")) if err != nil { diff --git a/updates/main.go b/updates/main.go index 3b9e68cc..395e2f92 100644 --- a/updates/main.go +++ b/updates/main.go @@ -2,7 +2,6 @@ package updates import ( "errors" - "fmt" "os" "path/filepath" @@ -10,21 +9,24 @@ import ( "github.com/safing/portbase/info" "github.com/safing/portbase/log" "github.com/safing/portbase/modules" + "github.com/safing/portbase/utils" ) var ( updateStoragePath string + downloadTmpPath string ) // SetDatabaseRoot tells the updates module where the database is - and where to put its stuff. func SetDatabaseRoot(path string) { if updateStoragePath == "" { updateStoragePath = filepath.Join(path, "updates") + downloadTmpPath = filepath.Join(updateStoragePath, "tmp") } } func init() { - modules.Register("updates", prep, start, nil, "core") + modules.Register("updates", prep, start, stop, "core") } func prep() error { @@ -33,8 +35,14 @@ func prep() error { return errors.New("database root is not set") } updateStoragePath = filepath.Join(dbRoot, "updates") + downloadTmpPath = filepath.Join(updateStoragePath, "tmp") - err := CheckDir(updateStoragePath) + err := utils.EnsureDirectory(updateStoragePath, 0755) + if err != nil { + return err + } + + err = utils.EnsureDirectory(downloadTmpPath, 0700) if err != nil { return err } @@ -70,34 +78,6 @@ func start() error { } func stop() error { - return os.RemoveAll(filepath.Join(updateStoragePath, "tmp")) -} - -func CheckDir(dirPath string) error { - f, err := os.Stat(dirPath) - if err == nil { - // file exists - if f.IsDir() { - return nil - } - err = os.Remove(dirPath) - if err != nil { - return fmt.Errorf("could not remove file %s to place dir: %s", dirPath, err) - } - err = os.MkdirAll(dirPath, 0755) - if err != nil { - return fmt.Errorf("could not create dir %s: %s", dirPath, err) - } - return nil - } - // file does not exist - if os.IsNotExist(err) { - err = os.MkdirAll(dirPath, 0755) - if err != nil { - return fmt.Errorf("could not create dir %s: %s", dirPath, err) - } - return nil - } - // other error - return fmt.Errorf("failed to access %s: %s", dirPath, err) + // delete download tmp dir + return os.RemoveAll(downloadTmpPath) } diff --git a/updates/updater.go b/updates/updater.go index 8dab77b2..d10984c9 100644 --- a/updates/updater.go +++ b/updates/updater.go @@ -3,7 +3,9 @@ package updates import ( "encoding/json" "errors" + "fmt" "io/ioutil" + "path" "path/filepath" "runtime" "time" @@ -22,18 +24,24 @@ func updater() { } } -func CheckForUpdates() error { +func markFileForDownload(identifier string) { + // get file + _, ok := localUpdates[identifier] + // only mark if it does not yet exist + if !ok { + localUpdates[identifier] = "loading..." + } +} - // ensure core components are updated - var err error - if runtime.GOOS == "windows" { - _, err = GetPlatformFile("pmctl/pmctl.exe") - } else { - _, err = GetPlatformFile("pmctl/pmctl") - } - if err != nil { - log.Errorf("updates: failed to mark pmctl/pmctl as used to ensure updates: %s", err) - } +func markPlatformFileForDownload(identifier string) { + // add platform prefix + identifier = path.Join(fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH), identifier) + // mark file + markFileForDownload(identifier) +} + +// CheckForUpdates checks if updates are available and downloads updates of used components. +func CheckForUpdates() (err error) { // download new index var data []byte @@ -60,6 +68,19 @@ func CheckForUpdates() error { // FIXME IN STABLE: correct log line log.Infof("updates: downloaded new update index: stable.json (alpha until we actually reach stable)") + // ensure important components are always updated + updatesLock.Lock() + if runtime.GOOS == "windows" { + markPlatformFileForDownload("control/portmaster-control.exe") + markPlatformFileForDownload("app/portmaster-app.exe") + markPlatformFileForDownload("notifier/portmaster-notifier.exe") + } else { + markPlatformFileForDownload("control/portmaster-control") + markPlatformFileForDownload("app/portmaster-app") + markPlatformFileForDownload("notifier/portmaster-notifier") + } + updatesLock.Unlock() + // update existing files log.Tracef("updates: updating existing files") updatesLock.RLock() @@ -67,16 +88,17 @@ func CheckForUpdates() error { oldVersion, ok := localUpdates[identifier] if ok && newVersion != oldVersion { + log.Tracef("updates: updating %s to %s", identifier, newVersion) filePath := GetVersionedPath(identifier, newVersion) realFilePath := filepath.Join(updateStoragePath, filePath) for tries := 0; tries < 3; tries++ { - err := fetchFile(realFilePath, filePath, tries) + err = fetchFile(realFilePath, filePath, tries) if err == nil { break } } if err != nil { - log.Warningf("failed to update %s to %s: %s", identifier, newVersion, err) + log.Warningf("updates: failed to update %s to %s: %s", identifier, newVersion, err) } }