safing-portmaster/base/updater/file.go
Daniel Hååvi 80664d1a27
Restructure modules ()
* Move portbase into monorepo

* Add new simple module mgr

* [WIP] Switch to new simple module mgr

* Add StateMgr and more worker variants

* [WIP] Switch more modules

* [WIP] Switch more modules

* [WIP] swtich more modules

* [WIP] switch all SPN modules

* [WIP] switch all service modules

* [WIP] Convert all workers to the new module system

* [WIP] add new task system to module manager

* [WIP] Add second take for scheduling workers

* [WIP] Add FIXME for bugs in new scheduler

* [WIP] Add minor improvements to scheduler

* [WIP] Add new worker scheduler

* [WIP] Fix more bug related to new module system

* [WIP] Fix start handing of the new module system

* [WIP] Improve startup process

* [WIP] Fix minor issues

* [WIP] Fix missing subsystem in settings

* [WIP] Initialize managers in constructor

* [WIP] Move module event initialization to constrictors

* [WIP] Fix setting for enabling and disabling the SPN module

* [WIP] Move API registeration into module construction

* [WIP] Update states mgr for all modules

* [WIP] Add CmdLine operation support

* Add state helper methods to module group and instance

* Add notification and module status handling to status package

* Fix starting issues

* Remove pilot widget and update security lock to new status data

* Remove debug logs

* Improve http server shutdown

* Add workaround for cleanly shutting down firewall+netquery

* Improve logging

* Add syncing states with notifications for new module system

* Improve starting, stopping, shutdown; resolve FIXMEs/TODOs

* [WIP] Fix most unit tests

* Review new module system and fix minor issues

* Push shutdown and restart events again via API

* Set sleep mode via interface

* Update example/template module

* [WIP] Fix spn/cabin unit test

* Remove deprecated UI elements

* Make log output more similar for the logging transition phase

* Switch spn hub and observer cmds to new module system

* Fix log sources

* Make worker mgr less error prone

* Fix tests and minor issues

* Fix observation hub

* Improve shutdown and restart handling

* Split up big connection.go source file

* Move varint and dsd packages to structures repo

* Improve expansion test

* Fix linter warnings

* Fix interception module on windows

* Fix linter errors

---------

Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
2024-08-09 18:15:48 +03:00

156 lines
4 KiB
Go

package updater
import (
"errors"
"io"
"io/fs"
"os"
"strings"
semver "github.com/hashicorp/go-version"
"github.com/safing/jess/filesig"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/utils"
)
// File represents a file from the update system.
type File struct {
resource *Resource
version *ResourceVersion
notifier *notifier
versionedPath string
storagePath string
}
// Identifier returns the identifier of the file.
func (file *File) Identifier() string {
return file.resource.Identifier
}
// Version returns the version of the file.
func (file *File) Version() string {
return file.version.VersionNumber
}
// SemVer returns the semantic version of the file.
func (file *File) SemVer() *semver.Version {
return file.version.semVer
}
// EqualsVersion normalizes the given version and checks equality with semver.
func (file *File) EqualsVersion(version string) bool {
return file.version.EqualsVersion(version)
}
// Path returns the absolute filepath of the file.
func (file *File) Path() string {
return file.storagePath
}
// SigningMetadata returns the metadata to be included in signatures.
func (file *File) SigningMetadata() map[string]string {
return map[string]string{
"id": file.Identifier(),
"version": file.Version(),
}
}
// Verify verifies the given file.
func (file *File) Verify() ([]*filesig.FileData, error) {
// Check if verification is configured.
if file.resource.VerificationOptions == nil {
return nil, ErrVerificationNotConfigured
}
// Verify file.
fileData, err := filesig.VerifyFile(
file.storagePath,
file.storagePath+filesig.Extension,
file.SigningMetadata(),
file.resource.VerificationOptions.TrustStore,
)
if err != nil {
switch file.resource.VerificationOptions.DiskLoadPolicy {
case SignaturePolicyRequire:
return nil, err
case SignaturePolicyWarn:
log.Warningf("%s: failed to verify %s: %s", file.resource.registry.Name, file.storagePath, err)
case SignaturePolicyDisable:
log.Debugf("%s: failed to verify %s: %s", file.resource.registry.Name, file.storagePath, err)
}
}
return fileData, nil
}
// Blacklist notifies the update system that this file is somehow broken, and should be ignored from now on, until restarted.
func (file *File) Blacklist() error {
return file.resource.Blacklist(file.version.VersionNumber)
}
// markActiveWithLocking marks the file as active, locking the resource in the process.
func (file *File) markActiveWithLocking() {
file.resource.Lock()
defer file.resource.Unlock()
// update last used version
if file.resource.ActiveVersion != file.version {
log.Debugf("updater: setting active version of resource %s from %s to %s", file.resource.Identifier, file.resource.ActiveVersion, file.version.VersionNumber)
file.resource.ActiveVersion = file.version
}
}
// Unpacker describes the function that is passed to
// File.Unpack. It receives a reader to the compressed/packed
// file and should return a reader that provides
// unpacked file contents. If the returned reader implements
// io.Closer it's close method is invoked when an error
// or io.EOF is returned from Read().
type Unpacker func(io.Reader) (io.Reader, error)
// Unpack returns the path to the unpacked version of file and
// unpacks it on demand using unpacker.
func (file *File) Unpack(suffix string, unpacker Unpacker) (string, error) {
path := strings.TrimSuffix(file.Path(), suffix)
if suffix == "" {
path += "-unpacked"
}
_, err := os.Stat(path)
if err == nil {
return path, nil
}
if !errors.Is(err, fs.ErrNotExist) {
return "", err
}
f, err := os.Open(file.Path())
if err != nil {
return "", err
}
defer func() {
_ = f.Close()
}()
r, err := unpacker(f)
if err != nil {
return "", err
}
ioErr := utils.CreateAtomic(path, r, &utils.AtomicFileOptions{
TempDir: file.resource.registry.TmpDir().Path,
})
if c, ok := r.(io.Closer); ok {
if err := c.Close(); err != nil && ioErr == nil {
// if ioErr is already set we ignore the error from
// closing the unpacker.
ioErr = err
}
}
return path, ioErr
}