mirror of
https://github.com/safing/portbase
synced 2025-04-19 08:59:09 +00:00
156 lines
4 KiB
Go
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/portbase/log"
|
|
"github.com/safing/portbase/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
|
|
}
|