[WIP] New updater first working prototype
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
package updater
|
||||
|
||||
// Export exports the list of resources.
|
||||
func (reg *ResourceRegistry) Export() map[string]*Resource {
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// // Export exports the list of resources.
|
||||
// func (reg *ResourceRegistry) Export() map[string]*Resource {
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
// copy the map
|
||||
copiedResources := make(map[string]*Resource)
|
||||
for key, val := range reg.resources {
|
||||
copiedResources[key] = val.Export()
|
||||
}
|
||||
// // copy the map
|
||||
// copiedResources := make(map[string]*Resource)
|
||||
// for key, val := range reg.resources {
|
||||
// copiedResources[key] = val.Export()
|
||||
// }
|
||||
|
||||
return copiedResources
|
||||
}
|
||||
// return copiedResources
|
||||
// }
|
||||
|
||||
@@ -1,347 +1,347 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
// import (
|
||||
// "bytes"
|
||||
// "context"
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "hash"
|
||||
// "io"
|
||||
// "net/http"
|
||||
// "net/url"
|
||||
// "os"
|
||||
// "path"
|
||||
// "path/filepath"
|
||||
// "time"
|
||||
|
||||
"github.com/safing/jess/filesig"
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils/renameio"
|
||||
)
|
||||
// "github.com/safing/jess/filesig"
|
||||
// "github.com/safing/jess/lhash"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/utils/renameio"
|
||||
// )
|
||||
|
||||
func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client, rv *ResourceVersion, tries int) error {
|
||||
// backoff when retrying
|
||||
if tries > 0 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil // module is shutting down
|
||||
case <-time.After(time.Duration(tries*tries) * time.Second):
|
||||
}
|
||||
}
|
||||
// func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client, rv *ResourceVersion, tries int) error {
|
||||
// // backoff when retrying
|
||||
// if tries > 0 {
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// return nil // module is shutting down
|
||||
// case <-time.After(time.Duration(tries*tries) * time.Second):
|
||||
// }
|
||||
// }
|
||||
|
||||
// check destination dir
|
||||
dirPath := filepath.Dir(rv.storagePath())
|
||||
err := reg.storageDir.EnsureAbsPath(dirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create updates folder: %s", dirPath)
|
||||
}
|
||||
// // check destination dir
|
||||
// dirPath := filepath.Dir(rv.storagePath())
|
||||
// err := reg.storageDir.EnsureAbsPath(dirPath)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("could not create updates folder: %s", dirPath)
|
||||
// }
|
||||
|
||||
// If verification is enabled, download signature first.
|
||||
var (
|
||||
verifiedHash *lhash.LabeledHash
|
||||
sigFileData []byte
|
||||
)
|
||||
if rv.resource.VerificationOptions != nil {
|
||||
verifiedHash, sigFileData, err = reg.fetchAndVerifySigFile(
|
||||
ctx, client,
|
||||
rv.resource.VerificationOptions,
|
||||
rv.versionedSigPath(), rv.SigningMetadata(),
|
||||
tries,
|
||||
)
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("signature verification failed: %w", err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// // If verification is enabled, download signature first.
|
||||
// var (
|
||||
// verifiedHash *lhash.LabeledHash
|
||||
// sigFileData []byte
|
||||
// )
|
||||
// if rv.resource.VerificationOptions != nil {
|
||||
// verifiedHash, sigFileData, err = reg.fetchAndVerifySigFile(
|
||||
// ctx, client,
|
||||
// rv.resource.VerificationOptions,
|
||||
// rv.versionedSigPath(), rv.SigningMetadata(),
|
||||
// tries,
|
||||
// )
|
||||
// if err != nil {
|
||||
// switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return fmt.Errorf("signature verification failed: %w", err)
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// open file for writing
|
||||
atomicFile, err := renameio.TempFile(reg.tmpDir.Path, rv.storagePath())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create temp file for download: %w", err)
|
||||
}
|
||||
defer atomicFile.Cleanup() //nolint:errcheck // ignore error for now, tmp dir will be cleaned later again anyway
|
||||
// // open file for writing
|
||||
// atomicFile, err := renameio.TempFile(reg.tmpDir.Path, rv.storagePath())
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("could not create temp file for download: %w", err)
|
||||
// }
|
||||
// defer atomicFile.Cleanup() //nolint:errcheck // ignore error for now, tmp dir will be cleaned later again anyway
|
||||
|
||||
// start file download
|
||||
resp, downloadURL, err := reg.makeRequest(ctx, client, rv.versionedPath(), tries)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
// // start file download
|
||||
// resp, downloadURL, err := reg.makeRequest(ctx, client, rv.versionedPath(), tries)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = resp.Body.Close()
|
||||
// }()
|
||||
|
||||
// Write to the hasher at the same time, if needed.
|
||||
var hasher hash.Hash
|
||||
var writeDst io.Writer = atomicFile
|
||||
if verifiedHash != nil {
|
||||
hasher = verifiedHash.Algorithm().RawHasher()
|
||||
writeDst = io.MultiWriter(hasher, atomicFile)
|
||||
}
|
||||
// // Write to the hasher at the same time, if needed.
|
||||
// var hasher hash.Hash
|
||||
// var writeDst io.Writer = atomicFile
|
||||
// if verifiedHash != nil {
|
||||
// hasher = verifiedHash.Algorithm().RawHasher()
|
||||
// writeDst = io.MultiWriter(hasher, atomicFile)
|
||||
// }
|
||||
|
||||
// Download and write file.
|
||||
n, err := io.Copy(writeDst, resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download %q: %w", downloadURL, err)
|
||||
}
|
||||
if resp.ContentLength != n {
|
||||
return fmt.Errorf("failed to finish download of %q: written %d out of %d bytes", downloadURL, n, resp.ContentLength)
|
||||
}
|
||||
// // Download and write file.
|
||||
// n, err := io.Copy(writeDst, resp.Body)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to download %q: %w", downloadURL, err)
|
||||
// }
|
||||
// if resp.ContentLength != n {
|
||||
// return fmt.Errorf("failed to finish download of %q: written %d out of %d bytes", downloadURL, n, resp.ContentLength)
|
||||
// }
|
||||
|
||||
// Before file is finalized, check if hash, if available.
|
||||
if hasher != nil {
|
||||
downloadDigest := hasher.Sum(nil)
|
||||
if verifiedHash.EqualRaw(downloadDigest) {
|
||||
log.Infof("%s: verified signature of %s", reg.Name, downloadURL)
|
||||
} else {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return errors.New("file does not match signed checksum")
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: checksum does not match file from %s", reg.Name, downloadURL)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: checksum does not match file from %s", reg.Name, downloadURL)
|
||||
}
|
||||
// // Before file is finalized, check if hash, if available.
|
||||
// if hasher != nil {
|
||||
// downloadDigest := hasher.Sum(nil)
|
||||
// if verifiedHash.EqualRaw(downloadDigest) {
|
||||
// log.Infof("%s: verified signature of %s", reg.Name, downloadURL)
|
||||
// } else {
|
||||
// switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return errors.New("file does not match signed checksum")
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: checksum does not match file from %s", reg.Name, downloadURL)
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: checksum does not match file from %s", reg.Name, downloadURL)
|
||||
// }
|
||||
|
||||
// Reset hasher to signal that the sig should not be written.
|
||||
hasher = nil
|
||||
}
|
||||
}
|
||||
// // Reset hasher to signal that the sig should not be written.
|
||||
// hasher = nil
|
||||
// }
|
||||
// }
|
||||
|
||||
// Write signature file, if we have one and if verification succeeded.
|
||||
if len(sigFileData) > 0 && hasher != nil {
|
||||
sigFilePath := rv.storagePath() + filesig.Extension
|
||||
err := os.WriteFile(sigFilePath, sigFileData, 0o0644) //nolint:gosec
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("failed to write signature file %s: %w", sigFilePath, err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: failed to write signature file %s: %s", reg.Name, sigFilePath, err)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: failed to write signature file %s: %s", reg.Name, sigFilePath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Write signature file, if we have one and if verification succeeded.
|
||||
// if len(sigFileData) > 0 && hasher != nil {
|
||||
// sigFilePath := rv.storagePath() + filesig.Extension
|
||||
// err := os.WriteFile(sigFilePath, sigFileData, 0o0644) //nolint:gosec
|
||||
// if err != nil {
|
||||
// switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return fmt.Errorf("failed to write signature file %s: %w", sigFilePath, err)
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: failed to write signature file %s: %s", reg.Name, sigFilePath, err)
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: failed to write signature file %s: %s", reg.Name, sigFilePath, err)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// finalize file
|
||||
err = atomicFile.CloseAtomicallyReplace()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: failed to finalize file %s: %w", reg.Name, rv.storagePath(), err)
|
||||
}
|
||||
// set permissions
|
||||
if !onWindows {
|
||||
// TODO: only set executable files to 0755, set other to 0644
|
||||
err = os.Chmod(rv.storagePath(), 0o0755) //nolint:gosec // See TODO above.
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to set permissions on downloaded file %s: %s", reg.Name, rv.storagePath(), err)
|
||||
}
|
||||
}
|
||||
// // finalize file
|
||||
// err = atomicFile.CloseAtomicallyReplace()
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("%s: failed to finalize file %s: %w", reg.Name, rv.storagePath(), err)
|
||||
// }
|
||||
// // set permissions
|
||||
// if !onWindows {
|
||||
// // TODO: only set executable files to 0755, set other to 0644
|
||||
// err = os.Chmod(rv.storagePath(), 0o0755) //nolint:gosec // See TODO above.
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to set permissions on downloaded file %s: %s", reg.Name, rv.storagePath(), err)
|
||||
// }
|
||||
// }
|
||||
|
||||
log.Debugf("%s: fetched %s and stored to %s", reg.Name, downloadURL, rv.storagePath())
|
||||
return nil
|
||||
}
|
||||
// log.Debugf("%s: fetched %s and stored to %s", reg.Name, downloadURL, rv.storagePath())
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) fetchMissingSig(ctx context.Context, client *http.Client, rv *ResourceVersion, tries int) error {
|
||||
// backoff when retrying
|
||||
if tries > 0 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil // module is shutting down
|
||||
case <-time.After(time.Duration(tries*tries) * time.Second):
|
||||
}
|
||||
}
|
||||
// func (reg *ResourceRegistry) fetchMissingSig(ctx context.Context, client *http.Client, rv *ResourceVersion, tries int) error {
|
||||
// // backoff when retrying
|
||||
// if tries > 0 {
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// return nil // module is shutting down
|
||||
// case <-time.After(time.Duration(tries*tries) * time.Second):
|
||||
// }
|
||||
// }
|
||||
|
||||
// Check destination dir.
|
||||
dirPath := filepath.Dir(rv.storagePath())
|
||||
err := reg.storageDir.EnsureAbsPath(dirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create updates folder: %s", dirPath)
|
||||
}
|
||||
// // Check destination dir.
|
||||
// dirPath := filepath.Dir(rv.storagePath())
|
||||
// err := reg.storageDir.EnsureAbsPath(dirPath)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("could not create updates folder: %s", dirPath)
|
||||
// }
|
||||
|
||||
// Download and verify the missing signature.
|
||||
verifiedHash, sigFileData, err := reg.fetchAndVerifySigFile(
|
||||
ctx, client,
|
||||
rv.resource.VerificationOptions,
|
||||
rv.versionedSigPath(), rv.SigningMetadata(),
|
||||
tries,
|
||||
)
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("signature verification failed: %w", err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// // Download and verify the missing signature.
|
||||
// verifiedHash, sigFileData, err := reg.fetchAndVerifySigFile(
|
||||
// ctx, client,
|
||||
// rv.resource.VerificationOptions,
|
||||
// rv.versionedSigPath(), rv.SigningMetadata(),
|
||||
// tries,
|
||||
// )
|
||||
// if err != nil {
|
||||
// switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return fmt.Errorf("signature verification failed: %w", err)
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Check if the signature matches the resource file.
|
||||
ok, err := verifiedHash.MatchesFile(rv.storagePath())
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("error while verifying resource file: %w", err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: error while verifying resource file %s", reg.Name, rv.storagePath())
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: error while verifying resource file %s", reg.Name, rv.storagePath())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !ok {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return errors.New("resource file does not match signed checksum")
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: checksum does not match resource file from %s", reg.Name, rv.storagePath())
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: checksum does not match resource file from %s", reg.Name, rv.storagePath())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// // Check if the signature matches the resource file.
|
||||
// ok, err := verifiedHash.MatchesFile(rv.storagePath())
|
||||
// if err != nil {
|
||||
// switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return fmt.Errorf("error while verifying resource file: %w", err)
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: error while verifying resource file %s", reg.Name, rv.storagePath())
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: error while verifying resource file %s", reg.Name, rv.storagePath())
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
// if !ok {
|
||||
// switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return errors.New("resource file does not match signed checksum")
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: checksum does not match resource file from %s", reg.Name, rv.storagePath())
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: checksum does not match resource file from %s", reg.Name, rv.storagePath())
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Write signature file.
|
||||
err = os.WriteFile(rv.storageSigPath(), sigFileData, 0o0644) //nolint:gosec
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("failed to write signature file %s: %w", rv.storageSigPath(), err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: failed to write signature file %s: %s", reg.Name, rv.storageSigPath(), err)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: failed to write signature file %s: %s", reg.Name, rv.storageSigPath(), err)
|
||||
}
|
||||
}
|
||||
// // Write signature file.
|
||||
// err = os.WriteFile(rv.storageSigPath(), sigFileData, 0o0644) //nolint:gosec
|
||||
// if err != nil {
|
||||
// switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return fmt.Errorf("failed to write signature file %s: %w", rv.storageSigPath(), err)
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: failed to write signature file %s: %s", reg.Name, rv.storageSigPath(), err)
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: failed to write signature file %s: %s", reg.Name, rv.storageSigPath(), err)
|
||||
// }
|
||||
// }
|
||||
|
||||
log.Debugf("%s: fetched %s and stored to %s", reg.Name, rv.versionedSigPath(), rv.storageSigPath())
|
||||
return nil
|
||||
}
|
||||
// log.Debugf("%s: fetched %s and stored to %s", reg.Name, rv.versionedSigPath(), rv.storageSigPath())
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) fetchAndVerifySigFile(ctx context.Context, client *http.Client, verifOpts *VerificationOptions, sigFilePath string, requiredMetadata map[string]string, tries int) (*lhash.LabeledHash, []byte, error) {
|
||||
// Download signature file.
|
||||
resp, _, err := reg.makeRequest(ctx, client, sigFilePath, tries)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
sigFileData, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// func (reg *ResourceRegistry) fetchAndVerifySigFile(ctx context.Context, client *http.Client, verifOpts *VerificationOptions, sigFilePath string, requiredMetadata map[string]string, tries int) (*lhash.LabeledHash, []byte, error) {
|
||||
// // Download signature file.
|
||||
// resp, _, err := reg.makeRequest(ctx, client, sigFilePath, tries)
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = resp.Body.Close()
|
||||
// }()
|
||||
// sigFileData, err := io.ReadAll(resp.Body)
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
|
||||
// Extract all signatures.
|
||||
sigs, err := filesig.ParseSigFile(sigFileData)
|
||||
switch {
|
||||
case len(sigs) == 0 && err != nil:
|
||||
return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
case len(sigs) == 0:
|
||||
return nil, nil, errors.New("no signatures found in signature file")
|
||||
case err != nil:
|
||||
return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
}
|
||||
// // Extract all signatures.
|
||||
// sigs, err := filesig.ParseSigFile(sigFileData)
|
||||
// switch {
|
||||
// case len(sigs) == 0 && err != nil:
|
||||
// return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
// case len(sigs) == 0:
|
||||
// return nil, nil, errors.New("no signatures found in signature file")
|
||||
// case err != nil:
|
||||
// return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
// }
|
||||
|
||||
// Verify all signatures.
|
||||
var verifiedHash *lhash.LabeledHash
|
||||
for _, sig := range sigs {
|
||||
fd, err := filesig.VerifyFileData(
|
||||
sig,
|
||||
requiredMetadata,
|
||||
verifOpts.TrustStore,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, sigFileData, err
|
||||
}
|
||||
// // Verify all signatures.
|
||||
// var verifiedHash *lhash.LabeledHash
|
||||
// for _, sig := range sigs {
|
||||
// fd, err := filesig.VerifyFileData(
|
||||
// sig,
|
||||
// requiredMetadata,
|
||||
// verifOpts.TrustStore,
|
||||
// )
|
||||
// if err != nil {
|
||||
// return nil, sigFileData, err
|
||||
// }
|
||||
|
||||
// Save or check verified hash.
|
||||
if verifiedHash == nil {
|
||||
verifiedHash = fd.FileHash()
|
||||
} else if !fd.FileHash().Equal(verifiedHash) {
|
||||
// Return an error if two valid hashes mismatch.
|
||||
// For simplicity, all hash algorithms must be the same for now.
|
||||
return nil, sigFileData, errors.New("file hashes from different signatures do not match")
|
||||
}
|
||||
}
|
||||
// // Save or check verified hash.
|
||||
// if verifiedHash == nil {
|
||||
// verifiedHash = fd.FileHash()
|
||||
// } else if !fd.FileHash().Equal(verifiedHash) {
|
||||
// // Return an error if two valid hashes mismatch.
|
||||
// // For simplicity, all hash algorithms must be the same for now.
|
||||
// return nil, sigFileData, errors.New("file hashes from different signatures do not match")
|
||||
// }
|
||||
// }
|
||||
|
||||
return verifiedHash, sigFileData, nil
|
||||
}
|
||||
// return verifiedHash, sigFileData, nil
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) fetchData(ctx context.Context, client *http.Client, downloadPath string, tries int) (fileData []byte, downloadedFrom string, err error) {
|
||||
// backoff when retrying
|
||||
if tries > 0 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, "", nil // module is shutting down
|
||||
case <-time.After(time.Duration(tries*tries) * time.Second):
|
||||
}
|
||||
}
|
||||
// func (reg *ResourceRegistry) fetchData(ctx context.Context, client *http.Client, downloadPath string, tries int) (fileData []byte, downloadedFrom string, err error) {
|
||||
// // backoff when retrying
|
||||
// if tries > 0 {
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// return nil, "", nil // module is shutting down
|
||||
// case <-time.After(time.Duration(tries*tries) * time.Second):
|
||||
// }
|
||||
// }
|
||||
|
||||
// start file download
|
||||
resp, downloadURL, err := reg.makeRequest(ctx, client, downloadPath, tries)
|
||||
if err != nil {
|
||||
return nil, downloadURL, err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
// // start file download
|
||||
// resp, downloadURL, err := reg.makeRequest(ctx, client, downloadPath, tries)
|
||||
// if err != nil {
|
||||
// return nil, downloadURL, err
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = resp.Body.Close()
|
||||
// }()
|
||||
|
||||
// download and write file
|
||||
buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength))
|
||||
n, err := io.Copy(buf, resp.Body)
|
||||
if err != nil {
|
||||
return nil, downloadURL, fmt.Errorf("failed to download %q: %w", downloadURL, err)
|
||||
}
|
||||
if resp.ContentLength != n {
|
||||
return nil, downloadURL, fmt.Errorf("failed to finish download of %q: written %d out of %d bytes", downloadURL, n, resp.ContentLength)
|
||||
}
|
||||
// // download and write file
|
||||
// buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength))
|
||||
// n, err := io.Copy(buf, resp.Body)
|
||||
// if err != nil {
|
||||
// return nil, downloadURL, fmt.Errorf("failed to download %q: %w", downloadURL, err)
|
||||
// }
|
||||
// if resp.ContentLength != n {
|
||||
// return nil, downloadURL, fmt.Errorf("failed to finish download of %q: written %d out of %d bytes", downloadURL, n, resp.ContentLength)
|
||||
// }
|
||||
|
||||
return buf.Bytes(), downloadURL, nil
|
||||
}
|
||||
// return buf.Bytes(), downloadURL, nil
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) makeRequest(ctx context.Context, client *http.Client, downloadPath string, tries int) (resp *http.Response, downloadURL string, err error) {
|
||||
// parse update URL
|
||||
updateBaseURL := reg.UpdateURLs[tries%len(reg.UpdateURLs)]
|
||||
u, err := url.Parse(updateBaseURL)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to parse update URL %q: %w", updateBaseURL, err)
|
||||
}
|
||||
// add download path
|
||||
u.Path = path.Join(u.Path, downloadPath)
|
||||
// compile URL
|
||||
downloadURL = u.String()
|
||||
// func (reg *ResourceRegistry) makeRequest(ctx context.Context, client *http.Client, downloadPath string, tries int) (resp *http.Response, downloadURL string, err error) {
|
||||
// // parse update URL
|
||||
// updateBaseURL := reg.UpdateURLs[tries%len(reg.UpdateURLs)]
|
||||
// u, err := url.Parse(updateBaseURL)
|
||||
// if err != nil {
|
||||
// return nil, "", fmt.Errorf("failed to parse update URL %q: %w", updateBaseURL, err)
|
||||
// }
|
||||
// // add download path
|
||||
// u.Path = path.Join(u.Path, downloadPath)
|
||||
// // compile URL
|
||||
// downloadURL = u.String()
|
||||
|
||||
// create request
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to create request for %q: %w", downloadURL, err)
|
||||
}
|
||||
// // create request
|
||||
// req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL, http.NoBody)
|
||||
// if err != nil {
|
||||
// return nil, "", fmt.Errorf("failed to create request for %q: %w", downloadURL, err)
|
||||
// }
|
||||
|
||||
// set user agent
|
||||
if reg.UserAgent != "" {
|
||||
req.Header.Set("User-Agent", reg.UserAgent)
|
||||
}
|
||||
// // set user agent
|
||||
// if reg.UserAgent != "" {
|
||||
// req.Header.Set("User-Agent", reg.UserAgent)
|
||||
// }
|
||||
|
||||
// start request
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to make request to %q: %w", downloadURL, err)
|
||||
}
|
||||
// // start request
|
||||
// resp, err = client.Do(req)
|
||||
// if err != nil {
|
||||
// return nil, "", fmt.Errorf("failed to make request to %q: %w", downloadURL, err)
|
||||
// }
|
||||
|
||||
// check return code
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
_ = resp.Body.Close()
|
||||
return nil, "", fmt.Errorf("failed to fetch %q: %d %s", downloadURL, resp.StatusCode, resp.Status)
|
||||
}
|
||||
// // check return code
|
||||
// if resp.StatusCode != http.StatusOK {
|
||||
// _ = resp.Body.Close()
|
||||
// return nil, "", fmt.Errorf("failed to fetch %q: %d %s", downloadURL, resp.StatusCode, resp.Status)
|
||||
// }
|
||||
|
||||
return resp, downloadURL, err
|
||||
}
|
||||
// return resp, downloadURL, err
|
||||
// }
|
||||
|
||||
@@ -1,105 +1,97 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
semver "github.com/hashicorp/go-version"
|
||||
// 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
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// // 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
|
||||
}
|
||||
// // 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
|
||||
}
|
||||
// // 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)
|
||||
}
|
||||
// // 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
|
||||
}
|
||||
// // 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(),
|
||||
}
|
||||
}
|
||||
// // 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
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
// // 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
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// 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()
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// // 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
|
||||
@@ -107,50 +99,50 @@ func (file *File) markActiveWithLocking() {
|
||||
// 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)
|
||||
// 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)
|
||||
// func (file *File) Unpack(suffix string, unpacker Unpacker) (string, error) {
|
||||
// path := strings.TrimSuffix(file.Path(), suffix)
|
||||
|
||||
if suffix == "" {
|
||||
path += "-unpacked"
|
||||
}
|
||||
// if suffix == "" {
|
||||
// path += "-unpacked"
|
||||
// }
|
||||
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return path, nil
|
||||
}
|
||||
// _, err := os.Stat(path)
|
||||
// if err == nil {
|
||||
// return path, nil
|
||||
// }
|
||||
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return "", err
|
||||
}
|
||||
// if !errors.Is(err, fs.ErrNotExist) {
|
||||
// return "", err
|
||||
// }
|
||||
|
||||
f, err := os.Open(file.Path())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
// f, err := os.Open(file.Path())
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = f.Close()
|
||||
// }()
|
||||
|
||||
r, err := unpacker(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// r, err := unpacker(f)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
|
||||
ioErr := utils.CreateAtomic(path, r, &utils.AtomicFileOptions{
|
||||
TempDir: file.resource.registry.TmpDir().Path,
|
||||
})
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// return path, ioErr
|
||||
// }
|
||||
|
||||
@@ -2,7 +2,6 @@ package updater
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Errors returned by the updater package.
|
||||
@@ -14,75 +13,75 @@ var (
|
||||
|
||||
// GetFile returns the selected (mostly newest) file with the given
|
||||
// identifier or an error, if it fails.
|
||||
func (reg *ResourceRegistry) GetFile(identifier string) (*File, error) {
|
||||
return nil, fmt.Errorf("invalid file: %s", identifier)
|
||||
// reg.RLock()
|
||||
// res, ok := reg.resources[identifier]
|
||||
// reg.RUnlock()
|
||||
// if !ok {
|
||||
// return nil, ErrNotFound
|
||||
// }
|
||||
// func (reg *ResourceRegistry) GetFile(identifier string) (*File, error) {
|
||||
// return nil, fmt.Errorf("invalid file: %s", identifier)
|
||||
// reg.RLock()
|
||||
// res, ok := reg.resources[identifier]
|
||||
// reg.RUnlock()
|
||||
// if !ok {
|
||||
// return nil, ErrNotFound
|
||||
// }
|
||||
|
||||
// file := res.GetFile()
|
||||
// // check if file is available locally
|
||||
// if file.version.Available {
|
||||
// file.markActiveWithLocking()
|
||||
// file := res.GetFile()
|
||||
// // check if file is available locally
|
||||
// if file.version.Available {
|
||||
// file.markActiveWithLocking()
|
||||
|
||||
// // Verify file, if configured.
|
||||
// _, err := file.Verify()
|
||||
// if err != nil && !errors.Is(err, ErrVerificationNotConfigured) {
|
||||
// // TODO: If verification is required, try deleting the resource and downloading it again.
|
||||
// return nil, fmt.Errorf("failed to verify file: %w", err)
|
||||
// }
|
||||
// // Verify file, if configured.
|
||||
// _, err := file.Verify()
|
||||
// if err != nil && !errors.Is(err, ErrVerificationNotConfigured) {
|
||||
// // TODO: If verification is required, try deleting the resource and downloading it again.
|
||||
// return nil, fmt.Errorf("failed to verify file: %w", err)
|
||||
// }
|
||||
|
||||
// return file, nil
|
||||
// }
|
||||
// return file, nil
|
||||
// }
|
||||
|
||||
// // check if online
|
||||
// if !reg.Online {
|
||||
// return nil, ErrNotAvailableLocally
|
||||
// }
|
||||
// // check if online
|
||||
// if !reg.Online {
|
||||
// return nil, ErrNotAvailableLocally
|
||||
// }
|
||||
|
||||
// // check download dir
|
||||
// err := reg.tmpDir.Ensure()
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("could not prepare tmp directory for download: %w", err)
|
||||
// }
|
||||
// // check download dir
|
||||
// err := reg.tmpDir.Ensure()
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("could not prepare tmp directory for download: %w", err)
|
||||
// }
|
||||
|
||||
// // Start registry operation.
|
||||
// reg.state.StartOperation(StateFetching)
|
||||
// defer reg.state.EndOperation()
|
||||
// // Start registry operation.
|
||||
// reg.state.StartOperation(StateFetching)
|
||||
// defer reg.state.EndOperation()
|
||||
|
||||
// // download file
|
||||
// log.Tracef("%s: starting download of %s", reg.Name, file.versionedPath)
|
||||
// client := &http.Client{}
|
||||
// for tries := range 5 {
|
||||
// err = reg.fetchFile(context.TODO(), client, file.version, tries)
|
||||
// if err != nil {
|
||||
// log.Tracef("%s: failed to download %s: %s, retrying (%d)", reg.Name, file.versionedPath, err, tries+1)
|
||||
// } else {
|
||||
// file.markActiveWithLocking()
|
||||
// // download file
|
||||
// log.Tracef("%s: starting download of %s", reg.Name, file.versionedPath)
|
||||
// client := &http.Client{}
|
||||
// for tries := range 5 {
|
||||
// err = reg.fetchFile(context.TODO(), client, file.version, tries)
|
||||
// if err != nil {
|
||||
// log.Tracef("%s: failed to download %s: %s, retrying (%d)", reg.Name, file.versionedPath, err, tries+1)
|
||||
// } else {
|
||||
// file.markActiveWithLocking()
|
||||
|
||||
// // TODO: We just download the file - should we verify it again?
|
||||
// return file, nil
|
||||
// }
|
||||
// }
|
||||
// log.Warningf("%s: failed to download %s: %s", reg.Name, file.versionedPath, err)
|
||||
// return nil, err
|
||||
}
|
||||
// // TODO: We just download the file - should we verify it again?
|
||||
// return file, nil
|
||||
// }
|
||||
// }
|
||||
// log.Warningf("%s: failed to download %s: %s", reg.Name, file.versionedPath, err)
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// GetVersion returns the selected version of the given identifier.
|
||||
// The returned resource version may not be modified.
|
||||
func (reg *ResourceRegistry) GetVersion(identifier string) (*ResourceVersion, error) {
|
||||
reg.RLock()
|
||||
res, ok := reg.resources[identifier]
|
||||
reg.RUnlock()
|
||||
if !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
// func (reg *ResourceRegistry) GetVersion(identifier string) (*ResourceVersion, error) {
|
||||
// reg.RLock()
|
||||
// res, ok := reg.resources[identifier]
|
||||
// reg.RUnlock()
|
||||
// if !ok {
|
||||
// return nil, ErrNotFound
|
||||
// }
|
||||
|
||||
res.Lock()
|
||||
defer res.Unlock()
|
||||
// res.Lock()
|
||||
// defer res.Unlock()
|
||||
|
||||
return res.SelectedVersion, nil
|
||||
}
|
||||
// return res.SelectedVersion, nil
|
||||
// }
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
// import (
|
||||
// "github.com/tevino/abool"
|
||||
// )
|
||||
|
||||
type notifier struct {
|
||||
upgradeAvailable *abool.AtomicBool
|
||||
notifyChannel chan struct{}
|
||||
}
|
||||
// type notifier struct {
|
||||
// upgradeAvailable *abool.AtomicBool
|
||||
// notifyChannel chan struct{}
|
||||
// }
|
||||
|
||||
func newNotifier() *notifier {
|
||||
return ¬ifier{
|
||||
upgradeAvailable: abool.NewBool(false),
|
||||
notifyChannel: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
// func newNotifier() *notifier {
|
||||
// return ¬ifier{
|
||||
// upgradeAvailable: abool.NewBool(false),
|
||||
// notifyChannel: make(chan struct{}),
|
||||
// }
|
||||
// }
|
||||
|
||||
func (n *notifier) markAsUpgradeable() {
|
||||
if n.upgradeAvailable.SetToIf(false, true) {
|
||||
close(n.notifyChannel)
|
||||
}
|
||||
}
|
||||
// func (n *notifier) markAsUpgradeable() {
|
||||
// if n.upgradeAvailable.SetToIf(false, true) {
|
||||
// close(n.notifyChannel)
|
||||
// }
|
||||
// }
|
||||
|
||||
// UpgradeAvailable returns whether an upgrade is available for this file.
|
||||
func (file *File) UpgradeAvailable() bool {
|
||||
return file.notifier.upgradeAvailable.IsSet()
|
||||
}
|
||||
// // UpgradeAvailable returns whether an upgrade is available for this file.
|
||||
// func (file *File) UpgradeAvailable() bool {
|
||||
// return file.notifier.upgradeAvailable.IsSet()
|
||||
// }
|
||||
|
||||
// WaitForAvailableUpgrade blocks (selectable) until an upgrade for this file is available.
|
||||
func (file *File) WaitForAvailableUpgrade() <-chan struct{} {
|
||||
return file.notifier.notifyChannel
|
||||
}
|
||||
// // WaitForAvailableUpgrade blocks (selectable) until an upgrade for this file is available.
|
||||
// func (file *File) WaitForAvailableUpgrade() <-chan struct{} {
|
||||
// return file.notifier.notifyChannel
|
||||
// }
|
||||
|
||||
@@ -1,270 +1,270 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
// import (
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "runtime"
|
||||
// "strings"
|
||||
// "sync"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/utils"
|
||||
// )
|
||||
|
||||
const (
|
||||
onWindows = runtime.GOOS == "windows"
|
||||
)
|
||||
// const (
|
||||
// onWindows = runtime.GOOS == "windows"
|
||||
// )
|
||||
|
||||
// ResourceRegistry is a registry for managing update resources.
|
||||
type ResourceRegistry struct {
|
||||
sync.RWMutex
|
||||
// type ResourceRegistry struct {
|
||||
// sync.RWMutex
|
||||
|
||||
Name string
|
||||
storageDir *utils.DirStructure
|
||||
tmpDir *utils.DirStructure
|
||||
indexes []*Index
|
||||
state *RegistryState
|
||||
// Name string
|
||||
// storageDir *utils.DirStructure
|
||||
// tmpDir *utils.DirStructure
|
||||
// indexes []*Index
|
||||
// state *RegistryState
|
||||
|
||||
resources map[string]*Resource
|
||||
UpdateURLs []string
|
||||
UserAgent string
|
||||
MandatoryUpdates []string
|
||||
AutoUnpack []string
|
||||
// resources map[string]*Resource
|
||||
// UpdateURLs []string
|
||||
// UserAgent string
|
||||
// MandatoryUpdates []string
|
||||
// AutoUnpack []string
|
||||
|
||||
// Verification holds a map of VerificationOptions assigned to their
|
||||
// applicable identifier path prefix.
|
||||
// Use an empty string to denote the default.
|
||||
// Use empty options to disable verification for a path prefix.
|
||||
Verification map[string]*VerificationOptions
|
||||
// // Verification holds a map of VerificationOptions assigned to their
|
||||
// // applicable identifier path prefix.
|
||||
// // Use an empty string to denote the default.
|
||||
// // Use empty options to disable verification for a path prefix.
|
||||
// Verification map[string]*VerificationOptions
|
||||
|
||||
// UsePreReleases signifies that pre-releases should be used when selecting a
|
||||
// version. Even if false, a pre-release version will still be used if it is
|
||||
// defined as the current version by an index.
|
||||
UsePreReleases bool
|
||||
// // UsePreReleases signifies that pre-releases should be used when selecting a
|
||||
// // version. Even if false, a pre-release version will still be used if it is
|
||||
// // defined as the current version by an index.
|
||||
// UsePreReleases bool
|
||||
|
||||
// DevMode specifies if a local 0.0.0 version should be always chosen, when available.
|
||||
DevMode bool
|
||||
// // DevMode specifies if a local 0.0.0 version should be always chosen, when available.
|
||||
// DevMode bool
|
||||
|
||||
// Online specifies if resources may be downloaded if not available locally.
|
||||
Online bool
|
||||
// // Online specifies if resources may be downloaded if not available locally.
|
||||
// Online bool
|
||||
|
||||
// StateNotifyFunc may be set to receive any changes to the registry state.
|
||||
// The specified function may lock the state, but may not block or take a
|
||||
// lot of time.
|
||||
StateNotifyFunc func(*RegistryState)
|
||||
}
|
||||
// // StateNotifyFunc may be set to receive any changes to the registry state.
|
||||
// // The specified function may lock the state, but may not block or take a
|
||||
// // lot of time.
|
||||
// StateNotifyFunc func(*RegistryState)
|
||||
// }
|
||||
|
||||
// AddIndex adds a new index to the resource registry.
|
||||
// The order is important, as indexes added later will override the current
|
||||
// release from earlier indexes.
|
||||
func (reg *ResourceRegistry) AddIndex(idx Index) {
|
||||
reg.Lock()
|
||||
defer reg.Unlock()
|
||||
// // AddIndex adds a new index to the resource registry.
|
||||
// // The order is important, as indexes added later will override the current
|
||||
// // release from earlier indexes.
|
||||
// func (reg *ResourceRegistry) AddIndex(idx Index) {
|
||||
// reg.Lock()
|
||||
// defer reg.Unlock()
|
||||
|
||||
// Get channel name from path.
|
||||
idx.Channel = strings.TrimSuffix(
|
||||
filepath.Base(idx.Path), filepath.Ext(idx.Path),
|
||||
)
|
||||
// // Get channel name from path.
|
||||
// idx.Channel = strings.TrimSuffix(
|
||||
// filepath.Base(idx.Path), filepath.Ext(idx.Path),
|
||||
// )
|
||||
|
||||
reg.indexes = append(reg.indexes, &idx)
|
||||
}
|
||||
// reg.indexes = append(reg.indexes, &idx)
|
||||
// }
|
||||
|
||||
// PreInitUpdateState sets the initial update state of the registry before initialization.
|
||||
func (reg *ResourceRegistry) PreInitUpdateState(s UpdateState) error {
|
||||
if reg.state != nil {
|
||||
return errors.New("registry already initialized")
|
||||
}
|
||||
// // PreInitUpdateState sets the initial update state of the registry before initialization.
|
||||
// func (reg *ResourceRegistry) PreInitUpdateState(s UpdateState) error {
|
||||
// if reg.state != nil {
|
||||
// return errors.New("registry already initialized")
|
||||
// }
|
||||
|
||||
reg.state = &RegistryState{
|
||||
Updates: s,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// reg.state = &RegistryState{
|
||||
// Updates: s,
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Initialize initializes a raw registry struct and makes it ready for usage.
|
||||
func (reg *ResourceRegistry) Initialize(storageDir *utils.DirStructure) error {
|
||||
// check if storage dir is available
|
||||
err := storageDir.Ensure()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// // Initialize initializes a raw registry struct and makes it ready for usage.
|
||||
// func (reg *ResourceRegistry) Initialize(storageDir *utils.DirStructure) error {
|
||||
// // check if storage dir is available
|
||||
// err := storageDir.Ensure()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// set default name
|
||||
if reg.Name == "" {
|
||||
reg.Name = "updater"
|
||||
}
|
||||
// // set default name
|
||||
// if reg.Name == "" {
|
||||
// reg.Name = "updater"
|
||||
// }
|
||||
|
||||
// initialize private attributes
|
||||
reg.storageDir = storageDir
|
||||
reg.tmpDir = storageDir.ChildDir("tmp", 0o0700)
|
||||
reg.resources = make(map[string]*Resource)
|
||||
if reg.state == nil {
|
||||
reg.state = &RegistryState{}
|
||||
}
|
||||
reg.state.ID = StateReady
|
||||
reg.state.reg = reg
|
||||
// // initialize private attributes
|
||||
// reg.storageDir = storageDir
|
||||
// reg.tmpDir = storageDir.ChildDir("tmp", 0o0700)
|
||||
// reg.resources = make(map[string]*Resource)
|
||||
// if reg.state == nil {
|
||||
// reg.state = &RegistryState{}
|
||||
// }
|
||||
// reg.state.ID = StateReady
|
||||
// reg.state.reg = reg
|
||||
|
||||
// remove tmp dir to delete old entries
|
||||
err = reg.Cleanup()
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to remove tmp dir: %s", reg.Name, err)
|
||||
}
|
||||
// // remove tmp dir to delete old entries
|
||||
// err = reg.Cleanup()
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to remove tmp dir: %s", reg.Name, err)
|
||||
// }
|
||||
|
||||
// (re-)create tmp dir
|
||||
err = reg.tmpDir.Ensure()
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to create tmp dir: %s", reg.Name, err)
|
||||
}
|
||||
// // (re-)create tmp dir
|
||||
// err = reg.tmpDir.Ensure()
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to create tmp dir: %s", reg.Name, err)
|
||||
// }
|
||||
|
||||
// Check verification options.
|
||||
if reg.Verification != nil {
|
||||
for prefix, opts := range reg.Verification {
|
||||
// Check if verification is disable for this prefix.
|
||||
if opts == nil {
|
||||
continue
|
||||
}
|
||||
// // Check verification options.
|
||||
// if reg.Verification != nil {
|
||||
// for prefix, opts := range reg.Verification {
|
||||
// // Check if verification is disable for this prefix.
|
||||
// if opts == nil {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// If enabled, a trust store is required.
|
||||
if opts.TrustStore == nil {
|
||||
return fmt.Errorf("verification enabled for prefix %q, but no trust store configured", prefix)
|
||||
}
|
||||
// // If enabled, a trust store is required.
|
||||
// if opts.TrustStore == nil {
|
||||
// return fmt.Errorf("verification enabled for prefix %q, but no trust store configured", prefix)
|
||||
// }
|
||||
|
||||
// DownloadPolicy must be equal or stricter than DiskLoadPolicy.
|
||||
if opts.DiskLoadPolicy < opts.DownloadPolicy {
|
||||
return errors.New("verification download policy must be equal or stricter than the disk load policy")
|
||||
}
|
||||
// // DownloadPolicy must be equal or stricter than DiskLoadPolicy.
|
||||
// if opts.DiskLoadPolicy < opts.DownloadPolicy {
|
||||
// return errors.New("verification download policy must be equal or stricter than the disk load policy")
|
||||
// }
|
||||
|
||||
// Warn if all policies are disabled.
|
||||
if opts.DownloadPolicy == SignaturePolicyDisable &&
|
||||
opts.DiskLoadPolicy == SignaturePolicyDisable {
|
||||
log.Warningf("%s: verification enabled for prefix %q, but all policies set to disable", reg.Name, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Warn if all policies are disabled.
|
||||
// if opts.DownloadPolicy == SignaturePolicyDisable &&
|
||||
// opts.DiskLoadPolicy == SignaturePolicyDisable {
|
||||
// log.Warningf("%s: verification enabled for prefix %q, but all policies set to disable", reg.Name, prefix)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// StorageDir returns the main storage dir of the resource registry.
|
||||
func (reg *ResourceRegistry) StorageDir() *utils.DirStructure {
|
||||
return reg.storageDir
|
||||
}
|
||||
// // StorageDir returns the main storage dir of the resource registry.
|
||||
// func (reg *ResourceRegistry) StorageDir() *utils.DirStructure {
|
||||
// return reg.storageDir
|
||||
// }
|
||||
|
||||
// TmpDir returns the temporary working dir of the resource registry.
|
||||
func (reg *ResourceRegistry) TmpDir() *utils.DirStructure {
|
||||
return reg.tmpDir
|
||||
}
|
||||
// // TmpDir returns the temporary working dir of the resource registry.
|
||||
// func (reg *ResourceRegistry) TmpDir() *utils.DirStructure {
|
||||
// return reg.tmpDir
|
||||
// }
|
||||
|
||||
// SetDevMode sets the development mode flag.
|
||||
func (reg *ResourceRegistry) SetDevMode(on bool) {
|
||||
reg.Lock()
|
||||
defer reg.Unlock()
|
||||
// // SetDevMode sets the development mode flag.
|
||||
// func (reg *ResourceRegistry) SetDevMode(on bool) {
|
||||
// reg.Lock()
|
||||
// defer reg.Unlock()
|
||||
|
||||
reg.DevMode = on
|
||||
}
|
||||
// reg.DevMode = on
|
||||
// }
|
||||
|
||||
// SetUsePreReleases sets the UsePreReleases flag.
|
||||
func (reg *ResourceRegistry) SetUsePreReleases(yes bool) {
|
||||
reg.Lock()
|
||||
defer reg.Unlock()
|
||||
// // SetUsePreReleases sets the UsePreReleases flag.
|
||||
// func (reg *ResourceRegistry) SetUsePreReleases(yes bool) {
|
||||
// reg.Lock()
|
||||
// defer reg.Unlock()
|
||||
|
||||
reg.UsePreReleases = yes
|
||||
}
|
||||
// reg.UsePreReleases = yes
|
||||
// }
|
||||
|
||||
// AddResource adds a resource to the registry. Does _not_ select new version.
|
||||
func (reg *ResourceRegistry) AddResource(identifier, version string, index *Index, available, currentRelease, preRelease bool) error {
|
||||
reg.Lock()
|
||||
defer reg.Unlock()
|
||||
// // AddResource adds a resource to the registry. Does _not_ select new version.
|
||||
// func (reg *ResourceRegistry) AddResource(identifier, version string, index *Index, available, currentRelease, preRelease bool) error {
|
||||
// reg.Lock()
|
||||
// defer reg.Unlock()
|
||||
|
||||
err := reg.addResource(identifier, version, index, available, currentRelease, preRelease)
|
||||
return err
|
||||
}
|
||||
// err := reg.addResource(identifier, version, index, available, currentRelease, preRelease)
|
||||
// return err
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) addResource(identifier, version string, index *Index, available, currentRelease, preRelease bool) error {
|
||||
res, ok := reg.resources[identifier]
|
||||
if !ok {
|
||||
res = reg.newResource(identifier)
|
||||
reg.resources[identifier] = res
|
||||
}
|
||||
res.Index = index
|
||||
// func (reg *ResourceRegistry) addResource(identifier, version string, index *Index, available, currentRelease, preRelease bool) error {
|
||||
// res, ok := reg.resources[identifier]
|
||||
// if !ok {
|
||||
// res = reg.newResource(identifier)
|
||||
// reg.resources[identifier] = res
|
||||
// }
|
||||
// res.Index = index
|
||||
|
||||
return res.AddVersion(version, available, currentRelease, preRelease)
|
||||
}
|
||||
// return res.AddVersion(version, available, currentRelease, preRelease)
|
||||
// }
|
||||
|
||||
// AddResources adds resources to the registry. Errors are logged, the last one is returned. Despite errors, non-failing resources are still added. Does _not_ select new versions.
|
||||
func (reg *ResourceRegistry) AddResources(versions map[string]string, index *Index, available, currentRelease, preRelease bool) error {
|
||||
reg.Lock()
|
||||
defer reg.Unlock()
|
||||
// // AddResources adds resources to the registry. Errors are logged, the last one is returned. Despite errors, non-failing resources are still added. Does _not_ select new versions.
|
||||
// func (reg *ResourceRegistry) AddResources(versions map[string]string, index *Index, available, currentRelease, preRelease bool) error {
|
||||
// reg.Lock()
|
||||
// defer reg.Unlock()
|
||||
|
||||
// add versions and their flags to registry
|
||||
var lastError error
|
||||
for identifier, version := range versions {
|
||||
lastError = reg.addResource(identifier, version, index, available, currentRelease, preRelease)
|
||||
if lastError != nil {
|
||||
log.Warningf("%s: failed to add resource %s: %s", reg.Name, identifier, lastError)
|
||||
}
|
||||
}
|
||||
// // add versions and their flags to registry
|
||||
// var lastError error
|
||||
// for identifier, version := range versions {
|
||||
// lastError = reg.addResource(identifier, version, index, available, currentRelease, preRelease)
|
||||
// if lastError != nil {
|
||||
// log.Warningf("%s: failed to add resource %s: %s", reg.Name, identifier, lastError)
|
||||
// }
|
||||
// }
|
||||
|
||||
return lastError
|
||||
}
|
||||
// return lastError
|
||||
// }
|
||||
|
||||
// SelectVersions selects new resource versions depending on the current registry state.
|
||||
func (reg *ResourceRegistry) SelectVersions() {
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// // SelectVersions selects new resource versions depending on the current registry state.
|
||||
// func (reg *ResourceRegistry) SelectVersions() {
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
for _, res := range reg.resources {
|
||||
res.Lock()
|
||||
res.selectVersion()
|
||||
res.Unlock()
|
||||
}
|
||||
}
|
||||
// for _, res := range reg.resources {
|
||||
// res.Lock()
|
||||
// res.selectVersion()
|
||||
// res.Unlock()
|
||||
// }
|
||||
// }
|
||||
|
||||
// GetSelectedVersions returns a list of the currently selected versions.
|
||||
func (reg *ResourceRegistry) GetSelectedVersions() (versions map[string]string) {
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// // GetSelectedVersions returns a list of the currently selected versions.
|
||||
// func (reg *ResourceRegistry) GetSelectedVersions() (versions map[string]string) {
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
for _, res := range reg.resources {
|
||||
res.Lock()
|
||||
versions[res.Identifier] = res.SelectedVersion.VersionNumber
|
||||
res.Unlock()
|
||||
}
|
||||
// for _, res := range reg.resources {
|
||||
// res.Lock()
|
||||
// versions[res.Identifier] = res.SelectedVersion.VersionNumber
|
||||
// res.Unlock()
|
||||
// }
|
||||
|
||||
return
|
||||
}
|
||||
// return
|
||||
// }
|
||||
|
||||
// Purge deletes old updates, retaining a certain amount, specified by the keep
|
||||
// parameter. Will at least keep 2 updates per resource.
|
||||
func (reg *ResourceRegistry) Purge(keep int) {
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// // Purge deletes old updates, retaining a certain amount, specified by the keep
|
||||
// // parameter. Will at least keep 2 updates per resource.
|
||||
// func (reg *ResourceRegistry) Purge(keep int) {
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
for _, res := range reg.resources {
|
||||
res.Purge(keep)
|
||||
}
|
||||
}
|
||||
// for _, res := range reg.resources {
|
||||
// res.Purge(keep)
|
||||
// }
|
||||
// }
|
||||
|
||||
// ResetResources removes all resources from the registry.
|
||||
func (reg *ResourceRegistry) ResetResources() {
|
||||
reg.Lock()
|
||||
defer reg.Unlock()
|
||||
// // ResetResources removes all resources from the registry.
|
||||
// func (reg *ResourceRegistry) ResetResources() {
|
||||
// reg.Lock()
|
||||
// defer reg.Unlock()
|
||||
|
||||
reg.resources = make(map[string]*Resource)
|
||||
}
|
||||
// reg.resources = make(map[string]*Resource)
|
||||
// }
|
||||
|
||||
// ResetIndexes removes all indexes from the registry.
|
||||
func (reg *ResourceRegistry) ResetIndexes() {
|
||||
reg.Lock()
|
||||
defer reg.Unlock()
|
||||
// // ResetIndexes removes all indexes from the registry.
|
||||
// func (reg *ResourceRegistry) ResetIndexes() {
|
||||
// reg.Lock()
|
||||
// defer reg.Unlock()
|
||||
|
||||
reg.indexes = make([]*Index, 0, len(reg.indexes))
|
||||
}
|
||||
// reg.indexes = make([]*Index, 0, len(reg.indexes))
|
||||
// }
|
||||
|
||||
// Cleanup removes temporary files.
|
||||
func (reg *ResourceRegistry) Cleanup() error {
|
||||
// delete download tmp dir
|
||||
return os.RemoveAll(reg.tmpDir.Path)
|
||||
}
|
||||
// // Cleanup removes temporary files.
|
||||
// func (reg *ResourceRegistry) Cleanup() error {
|
||||
// // delete download tmp dir
|
||||
// return os.RemoveAll(reg.tmpDir.Path)
|
||||
// }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,49 +1,49 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"strings"
|
||||
// import (
|
||||
// "strings"
|
||||
|
||||
"github.com/safing/jess"
|
||||
)
|
||||
// "github.com/safing/jess"
|
||||
// )
|
||||
|
||||
// VerificationOptions holds options for verification of files.
|
||||
type VerificationOptions struct {
|
||||
TrustStore jess.TrustStore
|
||||
DownloadPolicy SignaturePolicy
|
||||
DiskLoadPolicy SignaturePolicy
|
||||
}
|
||||
// // VerificationOptions holds options for verification of files.
|
||||
// type VerificationOptions struct {
|
||||
// TrustStore jess.TrustStore
|
||||
// DownloadPolicy SignaturePolicy
|
||||
// DiskLoadPolicy SignaturePolicy
|
||||
// }
|
||||
|
||||
// GetVerificationOptions returns the verification options for the given identifier.
|
||||
func (reg *ResourceRegistry) GetVerificationOptions(identifier string) *VerificationOptions {
|
||||
if reg.Verification == nil {
|
||||
return nil
|
||||
}
|
||||
// // GetVerificationOptions returns the verification options for the given identifier.
|
||||
// func (reg *ResourceRegistry) GetVerificationOptions(identifier string) *VerificationOptions {
|
||||
// if reg.Verification == nil {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
var (
|
||||
longestPrefix = -1
|
||||
bestMatch *VerificationOptions
|
||||
)
|
||||
for prefix, opts := range reg.Verification {
|
||||
if len(prefix) > longestPrefix && strings.HasPrefix(identifier, prefix) {
|
||||
longestPrefix = len(prefix)
|
||||
bestMatch = opts
|
||||
}
|
||||
}
|
||||
// var (
|
||||
// longestPrefix = -1
|
||||
// bestMatch *VerificationOptions
|
||||
// )
|
||||
// for prefix, opts := range reg.Verification {
|
||||
// if len(prefix) > longestPrefix && strings.HasPrefix(identifier, prefix) {
|
||||
// longestPrefix = len(prefix)
|
||||
// bestMatch = opts
|
||||
// }
|
||||
// }
|
||||
|
||||
return bestMatch
|
||||
}
|
||||
// return bestMatch
|
||||
// }
|
||||
|
||||
// SignaturePolicy defines behavior in case of errors.
|
||||
type SignaturePolicy uint8
|
||||
// // SignaturePolicy defines behavior in case of errors.
|
||||
// type SignaturePolicy uint8
|
||||
|
||||
// Signature Policies.
|
||||
const (
|
||||
// SignaturePolicyRequire fails on any error.
|
||||
SignaturePolicyRequire = iota
|
||||
// // Signature Policies.
|
||||
// const (
|
||||
// // SignaturePolicyRequire fails on any error.
|
||||
// SignaturePolicyRequire = iota
|
||||
|
||||
// SignaturePolicyWarn only warns on errors.
|
||||
SignaturePolicyWarn
|
||||
// // SignaturePolicyWarn only warns on errors.
|
||||
// SignaturePolicyWarn
|
||||
|
||||
// SignaturePolicyDisable only downloads signatures, but does not verify them.
|
||||
SignaturePolicyDisable
|
||||
)
|
||||
// // SignaturePolicyDisable only downloads signatures, but does not verify them.
|
||||
// SignaturePolicyDisable
|
||||
// )
|
||||
|
||||
@@ -1,180 +1,180 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
// import (
|
||||
// "sort"
|
||||
// "sync"
|
||||
// "time"
|
||||
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
// "github.com/safing/portmaster/base/utils"
|
||||
// )
|
||||
|
||||
// Registry States.
|
||||
const (
|
||||
StateReady = "ready" // Default idle state.
|
||||
StateChecking = "checking" // Downloading indexes.
|
||||
StateDownloading = "downloading" // Downloading updates.
|
||||
StateFetching = "fetching" // Fetching a single file.
|
||||
)
|
||||
// // Registry States.
|
||||
// const (
|
||||
// StateReady = "ready" // Default idle state.
|
||||
// StateChecking = "checking" // Downloading indexes.
|
||||
// StateDownloading = "downloading" // Downloading updates.
|
||||
// StateFetching = "fetching" // Fetching a single file.
|
||||
// )
|
||||
|
||||
// RegistryState describes the registry state.
|
||||
type RegistryState struct {
|
||||
sync.Mutex
|
||||
reg *ResourceRegistry
|
||||
// // RegistryState describes the registry state.
|
||||
// type RegistryState struct {
|
||||
// sync.Mutex
|
||||
// reg *ResourceRegistry
|
||||
|
||||
// ID holds the ID of the state the registry is currently in.
|
||||
ID string
|
||||
// // ID holds the ID of the state the registry is currently in.
|
||||
// ID string
|
||||
|
||||
// Details holds further information about the current state.
|
||||
Details any
|
||||
// // Details holds further information about the current state.
|
||||
// Details any
|
||||
|
||||
// Updates holds generic information about the current status of pending
|
||||
// and recently downloaded updates.
|
||||
Updates UpdateState
|
||||
// // Updates holds generic information about the current status of pending
|
||||
// // and recently downloaded updates.
|
||||
// Updates UpdateState
|
||||
|
||||
// operationLock locks the operation of any state changing operation.
|
||||
// This is separate from the registry lock, which locks access to the
|
||||
// registry struct.
|
||||
operationLock sync.Mutex
|
||||
}
|
||||
// // operationLock locks the operation of any state changing operation.
|
||||
// // This is separate from the registry lock, which locks access to the
|
||||
// // registry struct.
|
||||
// operationLock sync.Mutex
|
||||
// }
|
||||
|
||||
// StateDownloadingDetails holds details of the downloading state.
|
||||
type StateDownloadingDetails struct {
|
||||
// Resources holds the resource IDs that are being downloaded.
|
||||
Resources []string
|
||||
// // StateDownloadingDetails holds details of the downloading state.
|
||||
// type StateDownloadingDetails struct {
|
||||
// // Resources holds the resource IDs that are being downloaded.
|
||||
// Resources []string
|
||||
|
||||
// FinishedUpTo holds the index of Resources that is currently being
|
||||
// downloaded. Previous resources have finished downloading.
|
||||
FinishedUpTo int
|
||||
}
|
||||
// // FinishedUpTo holds the index of Resources that is currently being
|
||||
// // downloaded. Previous resources have finished downloading.
|
||||
// FinishedUpTo int
|
||||
// }
|
||||
|
||||
// UpdateState holds generic information about the current status of pending
|
||||
// and recently downloaded updates.
|
||||
type UpdateState struct {
|
||||
// LastCheckAt holds the time of the last update check.
|
||||
LastCheckAt *time.Time
|
||||
// LastCheckError holds the error of the last check.
|
||||
LastCheckError error
|
||||
// PendingDownload holds the resources that are pending download.
|
||||
PendingDownload []string
|
||||
// // UpdateState holds generic information about the current status of pending
|
||||
// // and recently downloaded updates.
|
||||
// type UpdateState struct {
|
||||
// // LastCheckAt holds the time of the last update check.
|
||||
// LastCheckAt *time.Time
|
||||
// // LastCheckError holds the error of the last check.
|
||||
// LastCheckError error
|
||||
// // PendingDownload holds the resources that are pending download.
|
||||
// PendingDownload []string
|
||||
|
||||
// LastDownloadAt holds the time when resources were downloaded the last time.
|
||||
LastDownloadAt *time.Time
|
||||
// LastDownloadError holds the error of the last download.
|
||||
LastDownloadError error
|
||||
// LastDownload holds the resources that we downloaded the last time updates
|
||||
// were downloaded.
|
||||
LastDownload []string
|
||||
// // LastDownloadAt holds the time when resources were downloaded the last time.
|
||||
// LastDownloadAt *time.Time
|
||||
// // LastDownloadError holds the error of the last download.
|
||||
// LastDownloadError error
|
||||
// // LastDownload holds the resources that we downloaded the last time updates
|
||||
// // were downloaded.
|
||||
// LastDownload []string
|
||||
|
||||
// LastSuccessAt holds the time of the last successful update (check).
|
||||
LastSuccessAt *time.Time
|
||||
}
|
||||
// // LastSuccessAt holds the time of the last successful update (check).
|
||||
// LastSuccessAt *time.Time
|
||||
// }
|
||||
|
||||
// GetState returns the current registry state.
|
||||
// The returned data must not be modified.
|
||||
func (reg *ResourceRegistry) GetState() RegistryState {
|
||||
reg.state.Lock()
|
||||
defer reg.state.Unlock()
|
||||
// // GetState returns the current registry state.
|
||||
// // The returned data must not be modified.
|
||||
// func (reg *ResourceRegistry) GetState() RegistryState {
|
||||
// reg.state.Lock()
|
||||
// defer reg.state.Unlock()
|
||||
|
||||
return RegistryState{
|
||||
ID: reg.state.ID,
|
||||
Details: reg.state.Details,
|
||||
Updates: reg.state.Updates,
|
||||
}
|
||||
}
|
||||
// return RegistryState{
|
||||
// ID: reg.state.ID,
|
||||
// Details: reg.state.Details,
|
||||
// Updates: reg.state.Updates,
|
||||
// }
|
||||
// }
|
||||
|
||||
// StartOperation starts an operation.
|
||||
func (s *RegistryState) StartOperation(id string) bool {
|
||||
defer s.notify()
|
||||
// // StartOperation starts an operation.
|
||||
// func (s *RegistryState) StartOperation(id string) bool {
|
||||
// defer s.notify()
|
||||
|
||||
s.operationLock.Lock()
|
||||
// s.operationLock.Lock()
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
// s.Lock()
|
||||
// defer s.Unlock()
|
||||
|
||||
s.ID = id
|
||||
return true
|
||||
}
|
||||
// s.ID = id
|
||||
// return true
|
||||
// }
|
||||
|
||||
// UpdateOperationDetails updates the details of an operation.
|
||||
// The supplied struct should be a copy and must not be changed after calling
|
||||
// this function.
|
||||
func (s *RegistryState) UpdateOperationDetails(details any) {
|
||||
defer s.notify()
|
||||
// // UpdateOperationDetails updates the details of an operation.
|
||||
// // The supplied struct should be a copy and must not be changed after calling
|
||||
// // this function.
|
||||
// func (s *RegistryState) UpdateOperationDetails(details any) {
|
||||
// defer s.notify()
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
// s.Lock()
|
||||
// defer s.Unlock()
|
||||
|
||||
s.Details = details
|
||||
}
|
||||
// s.Details = details
|
||||
// }
|
||||
|
||||
// EndOperation ends an operation.
|
||||
func (s *RegistryState) EndOperation() {
|
||||
defer s.notify()
|
||||
defer s.operationLock.Unlock()
|
||||
// // EndOperation ends an operation.
|
||||
// func (s *RegistryState) EndOperation() {
|
||||
// defer s.notify()
|
||||
// defer s.operationLock.Unlock()
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
// s.Lock()
|
||||
// defer s.Unlock()
|
||||
|
||||
s.ID = StateReady
|
||||
s.Details = nil
|
||||
}
|
||||
// s.ID = StateReady
|
||||
// s.Details = nil
|
||||
// }
|
||||
|
||||
// ReportUpdateCheck reports an update check to the registry state.
|
||||
func (s *RegistryState) ReportUpdateCheck(pendingDownload []string, failed error) {
|
||||
defer s.notify()
|
||||
// // ReportUpdateCheck reports an update check to the registry state.
|
||||
// func (s *RegistryState) ReportUpdateCheck(pendingDownload []string, failed error) {
|
||||
// defer s.notify()
|
||||
|
||||
sort.Strings(pendingDownload)
|
||||
// sort.Strings(pendingDownload)
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
// s.Lock()
|
||||
// defer s.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
s.Updates.LastCheckAt = &now
|
||||
s.Updates.LastCheckError = failed
|
||||
s.Updates.PendingDownload = pendingDownload
|
||||
// now := time.Now()
|
||||
// s.Updates.LastCheckAt = &now
|
||||
// s.Updates.LastCheckError = failed
|
||||
// s.Updates.PendingDownload = pendingDownload
|
||||
|
||||
if failed == nil {
|
||||
s.Updates.LastSuccessAt = &now
|
||||
}
|
||||
}
|
||||
// if failed == nil {
|
||||
// s.Updates.LastSuccessAt = &now
|
||||
// }
|
||||
// }
|
||||
|
||||
// ReportDownloads reports downloaded updates to the registry state.
|
||||
func (s *RegistryState) ReportDownloads(downloaded []string, failed error) {
|
||||
defer s.notify()
|
||||
// // ReportDownloads reports downloaded updates to the registry state.
|
||||
// func (s *RegistryState) ReportDownloads(downloaded []string, failed error) {
|
||||
// defer s.notify()
|
||||
|
||||
sort.Strings(downloaded)
|
||||
// sort.Strings(downloaded)
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
// s.Lock()
|
||||
// defer s.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
s.Updates.LastDownloadAt = &now
|
||||
s.Updates.LastDownloadError = failed
|
||||
s.Updates.LastDownload = downloaded
|
||||
// now := time.Now()
|
||||
// s.Updates.LastDownloadAt = &now
|
||||
// s.Updates.LastDownloadError = failed
|
||||
// s.Updates.LastDownload = downloaded
|
||||
|
||||
// Remove downloaded resources from the pending list.
|
||||
if len(s.Updates.PendingDownload) > 0 {
|
||||
newPendingDownload := make([]string, 0, len(s.Updates.PendingDownload))
|
||||
for _, pending := range s.Updates.PendingDownload {
|
||||
if !utils.StringInSlice(downloaded, pending) {
|
||||
newPendingDownload = append(newPendingDownload, pending)
|
||||
}
|
||||
}
|
||||
s.Updates.PendingDownload = newPendingDownload
|
||||
}
|
||||
// // Remove downloaded resources from the pending list.
|
||||
// if len(s.Updates.PendingDownload) > 0 {
|
||||
// newPendingDownload := make([]string, 0, len(s.Updates.PendingDownload))
|
||||
// for _, pending := range s.Updates.PendingDownload {
|
||||
// if !utils.StringInSlice(downloaded, pending) {
|
||||
// newPendingDownload = append(newPendingDownload, pending)
|
||||
// }
|
||||
// }
|
||||
// s.Updates.PendingDownload = newPendingDownload
|
||||
// }
|
||||
|
||||
if failed == nil {
|
||||
s.Updates.LastSuccessAt = &now
|
||||
}
|
||||
}
|
||||
// if failed == nil {
|
||||
// s.Updates.LastSuccessAt = &now
|
||||
// }
|
||||
// }
|
||||
|
||||
func (s *RegistryState) notify() {
|
||||
switch {
|
||||
case s.reg == nil:
|
||||
return
|
||||
case s.reg.StateNotifyFunc == nil:
|
||||
return
|
||||
}
|
||||
// func (s *RegistryState) notify() {
|
||||
// switch {
|
||||
// case s.reg == nil:
|
||||
// return
|
||||
// case s.reg.StateNotifyFunc == nil:
|
||||
// return
|
||||
// }
|
||||
|
||||
s.reg.StateNotifyFunc(s)
|
||||
}
|
||||
// s.reg.StateNotifyFunc(s)
|
||||
// }
|
||||
|
||||
@@ -1,272 +1,272 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
// import (
|
||||
// "context"
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "io/fs"
|
||||
// "net/http"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
|
||||
"github.com/safing/jess/filesig"
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
// "github.com/safing/jess/filesig"
|
||||
// "github.com/safing/jess/lhash"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/utils"
|
||||
// )
|
||||
|
||||
// ScanStorage scans root within the storage dir and adds found
|
||||
// resources to the registry. If an error occurred, it is logged
|
||||
// and the last error is returned. Everything that was found
|
||||
// despite errors is added to the registry anyway. Leave root
|
||||
// empty to scan the full storage dir.
|
||||
func (reg *ResourceRegistry) ScanStorage(root string) error {
|
||||
var lastError error
|
||||
// // ScanStorage scans root within the storage dir and adds found
|
||||
// // resources to the registry. If an error occurred, it is logged
|
||||
// // and the last error is returned. Everything that was found
|
||||
// // despite errors is added to the registry anyway. Leave root
|
||||
// // empty to scan the full storage dir.
|
||||
// func (reg *ResourceRegistry) ScanStorage(root string) error {
|
||||
// var lastError error
|
||||
|
||||
// prep root
|
||||
if root == "" {
|
||||
root = reg.storageDir.Path
|
||||
} else {
|
||||
var err error
|
||||
root, err = filepath.Abs(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.HasPrefix(root, reg.storageDir.Path) {
|
||||
return errors.New("supplied scan root path not within storage")
|
||||
}
|
||||
}
|
||||
// // prep root
|
||||
// if root == "" {
|
||||
// root = reg.storageDir.Path
|
||||
// } else {
|
||||
// var err error
|
||||
// root, err = filepath.Abs(root)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if !strings.HasPrefix(root, reg.storageDir.Path) {
|
||||
// return errors.New("supplied scan root path not within storage")
|
||||
// }
|
||||
// }
|
||||
|
||||
// walk fs
|
||||
_ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
// skip tmp dir (including errors trying to read it)
|
||||
if strings.HasPrefix(path, reg.tmpDir.Path) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// // walk fs
|
||||
// _ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
// // skip tmp dir (including errors trying to read it)
|
||||
// if strings.HasPrefix(path, reg.tmpDir.Path) {
|
||||
// return filepath.SkipDir
|
||||
// }
|
||||
|
||||
// handle walker error
|
||||
if err != nil {
|
||||
lastError = fmt.Errorf("%s: could not read %s: %w", reg.Name, path, err)
|
||||
log.Warning(lastError.Error())
|
||||
return nil
|
||||
}
|
||||
// // handle walker error
|
||||
// if err != nil {
|
||||
// lastError = fmt.Errorf("%s: could not read %s: %w", reg.Name, path, err)
|
||||
// log.Warning(lastError.Error())
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Ignore file signatures.
|
||||
if strings.HasSuffix(path, filesig.Extension) {
|
||||
return nil
|
||||
}
|
||||
// // Ignore file signatures.
|
||||
// if strings.HasSuffix(path, filesig.Extension) {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// get relative path to storage
|
||||
relativePath, err := filepath.Rel(reg.storageDir.Path, path)
|
||||
if err != nil {
|
||||
lastError = fmt.Errorf("%s: could not get relative path of %s: %w", reg.Name, path, err)
|
||||
log.Warning(lastError.Error())
|
||||
return nil
|
||||
}
|
||||
// // get relative path to storage
|
||||
// relativePath, err := filepath.Rel(reg.storageDir.Path, path)
|
||||
// if err != nil {
|
||||
// lastError = fmt.Errorf("%s: could not get relative path of %s: %w", reg.Name, path, err)
|
||||
// log.Warning(lastError.Error())
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// convert to identifier and version
|
||||
relativePath = filepath.ToSlash(relativePath)
|
||||
identifier, version, ok := GetIdentifierAndVersion(relativePath)
|
||||
if !ok {
|
||||
// file does not conform to format
|
||||
return nil
|
||||
}
|
||||
// // convert to identifier and version
|
||||
// relativePath = filepath.ToSlash(relativePath)
|
||||
// identifier, version, ok := GetIdentifierAndVersion(relativePath)
|
||||
// if !ok {
|
||||
// // file does not conform to format
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// fully ignore directories that also have an identifier - these will be unpacked resources
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// // fully ignore directories that also have an identifier - these will be unpacked resources
|
||||
// if info.IsDir() {
|
||||
// return filepath.SkipDir
|
||||
// }
|
||||
|
||||
// save
|
||||
err = reg.AddResource(identifier, version, nil, true, false, false)
|
||||
if err != nil {
|
||||
lastError = fmt.Errorf("%s: could not get add resource %s v%s: %w", reg.Name, identifier, version, err)
|
||||
log.Warning(lastError.Error())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// // save
|
||||
// err = reg.AddResource(identifier, version, nil, true, false, false)
|
||||
// if err != nil {
|
||||
// lastError = fmt.Errorf("%s: could not get add resource %s v%s: %w", reg.Name, identifier, version, err)
|
||||
// log.Warning(lastError.Error())
|
||||
// }
|
||||
// return nil
|
||||
// })
|
||||
|
||||
return lastError
|
||||
}
|
||||
// return lastError
|
||||
// }
|
||||
|
||||
// LoadIndexes loads the current release indexes from disk
|
||||
// or will fetch a new version if not available and the
|
||||
// registry is marked as online.
|
||||
func (reg *ResourceRegistry) LoadIndexes(ctx context.Context) error {
|
||||
var firstErr error
|
||||
client := &http.Client{}
|
||||
for _, idx := range reg.getIndexes() {
|
||||
err := reg.loadIndexFile(idx)
|
||||
if err == nil {
|
||||
log.Debugf("%s: loaded index %s", reg.Name, idx.Path)
|
||||
} else if reg.Online {
|
||||
// try to download the index file if a local disk version
|
||||
// does not exist or we don't have permission to read it.
|
||||
if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) {
|
||||
err = reg.downloadIndex(ctx, client, idx)
|
||||
}
|
||||
}
|
||||
// // LoadIndexes loads the current release indexes from disk
|
||||
// // or will fetch a new version if not available and the
|
||||
// // registry is marked as online.
|
||||
// func (reg *ResourceRegistry) LoadIndexes(ctx context.Context) error {
|
||||
// var firstErr error
|
||||
// client := &http.Client{}
|
||||
// for _, idx := range reg.getIndexes() {
|
||||
// err := reg.loadIndexFile(idx)
|
||||
// if err == nil {
|
||||
// log.Debugf("%s: loaded index %s", reg.Name, idx.Path)
|
||||
// } else if reg.Online {
|
||||
// // try to download the index file if a local disk version
|
||||
// // does not exist or we don't have permission to read it.
|
||||
// if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) {
|
||||
// err = reg.downloadIndex(ctx, client, idx)
|
||||
// }
|
||||
// }
|
||||
|
||||
if err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
// if err != nil && firstErr == nil {
|
||||
// firstErr = err
|
||||
// }
|
||||
// }
|
||||
|
||||
return firstErr
|
||||
}
|
||||
// return firstErr
|
||||
// }
|
||||
|
||||
// getIndexes returns a copy of the index.
|
||||
// The indexes itself are references.
|
||||
func (reg *ResourceRegistry) getIndexes() []*Index {
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// // getIndexes returns a copy of the index.
|
||||
// // The indexes itself are references.
|
||||
// func (reg *ResourceRegistry) getIndexes() []*Index {
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
indexes := make([]*Index, len(reg.indexes))
|
||||
copy(indexes, reg.indexes)
|
||||
return indexes
|
||||
}
|
||||
// indexes := make([]*Index, len(reg.indexes))
|
||||
// copy(indexes, reg.indexes)
|
||||
// return indexes
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) loadIndexFile(idx *Index) error {
|
||||
indexPath := filepath.Join(reg.storageDir.Path, filepath.FromSlash(idx.Path))
|
||||
indexData, err := os.ReadFile(indexPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read index file %s: %w", idx.Path, err)
|
||||
}
|
||||
// func (reg *ResourceRegistry) loadIndexFile(idx *Index) error {
|
||||
// indexPath := filepath.Join(reg.storageDir.Path, filepath.FromSlash(idx.Path))
|
||||
// indexData, err := os.ReadFile(indexPath)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to read index file %s: %w", idx.Path, err)
|
||||
// }
|
||||
|
||||
// Verify signature, if enabled.
|
||||
if verifOpts := reg.GetVerificationOptions(idx.Path); verifOpts != nil {
|
||||
// Load and check signature.
|
||||
verifiedHash, _, err := reg.loadAndVerifySigFile(verifOpts, indexPath+filesig.Extension)
|
||||
if err != nil {
|
||||
switch verifOpts.DiskLoadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("failed to verify signature of index %s: %w", idx.Path, err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: failed to verify signature of index %s: %s", reg.Name, idx.Path, err)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: failed to verify signature of index %s: %s", reg.Name, idx.Path, err)
|
||||
}
|
||||
}
|
||||
// // Verify signature, if enabled.
|
||||
// if verifOpts := reg.GetVerificationOptions(idx.Path); verifOpts != nil {
|
||||
// // Load and check signature.
|
||||
// verifiedHash, _, err := reg.loadAndVerifySigFile(verifOpts, indexPath+filesig.Extension)
|
||||
// if err != nil {
|
||||
// switch verifOpts.DiskLoadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return fmt.Errorf("failed to verify signature of index %s: %w", idx.Path, err)
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: failed to verify signature of index %s: %s", reg.Name, idx.Path, err)
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: failed to verify signature of index %s: %s", reg.Name, idx.Path, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// Check if signature checksum matches the index data.
|
||||
if err == nil && !verifiedHash.Matches(indexData) {
|
||||
switch verifOpts.DiskLoadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("index file %s does not match signature", idx.Path)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: index file %s does not match signature", reg.Name, idx.Path)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: index file %s does not match signature", reg.Name, idx.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Check if signature checksum matches the index data.
|
||||
// if err == nil && !verifiedHash.Matches(indexData) {
|
||||
// switch verifOpts.DiskLoadPolicy {
|
||||
// case SignaturePolicyRequire:
|
||||
// return fmt.Errorf("index file %s does not match signature", idx.Path)
|
||||
// case SignaturePolicyWarn:
|
||||
// log.Warningf("%s: index file %s does not match signature", reg.Name, idx.Path)
|
||||
// case SignaturePolicyDisable:
|
||||
// log.Debugf("%s: index file %s does not match signature", reg.Name, idx.Path)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Parse the index file.
|
||||
indexFile, err := ParseIndexFile(indexData, idx.Channel, idx.LastRelease)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse index file %s: %w", idx.Path, err)
|
||||
}
|
||||
// // Parse the index file.
|
||||
// indexFile, err := ParseIndexFile(indexData, idx.Channel, idx.LastRelease)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to parse index file %s: %w", idx.Path, err)
|
||||
// }
|
||||
|
||||
// Update last seen release.
|
||||
idx.LastRelease = indexFile.Published
|
||||
// // Update last seen release.
|
||||
// idx.LastRelease = indexFile.Published
|
||||
|
||||
// Warn if there aren't any releases in the index.
|
||||
if len(indexFile.Releases) == 0 {
|
||||
log.Debugf("%s: index %s has no releases", reg.Name, idx.Path)
|
||||
return nil
|
||||
}
|
||||
// // Warn if there aren't any releases in the index.
|
||||
// if len(indexFile.Releases) == 0 {
|
||||
// log.Debugf("%s: index %s has no releases", reg.Name, idx.Path)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Add index releases to available resources.
|
||||
err = reg.AddResources(indexFile.Releases, idx, false, true, idx.PreRelease)
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to add resource: %s", reg.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// // Add index releases to available resources.
|
||||
// err = reg.AddResources(indexFile.Releases, idx, false, true, idx.PreRelease)
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to add resource: %s", reg.Name, err)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) loadAndVerifySigFile(verifOpts *VerificationOptions, sigFilePath string) (*lhash.LabeledHash, []byte, error) {
|
||||
// Load signature file.
|
||||
sigFileData, err := os.ReadFile(sigFilePath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read signature file: %w", err)
|
||||
}
|
||||
// func (reg *ResourceRegistry) loadAndVerifySigFile(verifOpts *VerificationOptions, sigFilePath string) (*lhash.LabeledHash, []byte, error) {
|
||||
// // Load signature file.
|
||||
// sigFileData, err := os.ReadFile(sigFilePath)
|
||||
// if err != nil {
|
||||
// return nil, nil, fmt.Errorf("failed to read signature file: %w", err)
|
||||
// }
|
||||
|
||||
// Extract all signatures.
|
||||
sigs, err := filesig.ParseSigFile(sigFileData)
|
||||
switch {
|
||||
case len(sigs) == 0 && err != nil:
|
||||
return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
case len(sigs) == 0:
|
||||
return nil, nil, errors.New("no signatures found in signature file")
|
||||
case err != nil:
|
||||
return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
}
|
||||
// // Extract all signatures.
|
||||
// sigs, err := filesig.ParseSigFile(sigFileData)
|
||||
// switch {
|
||||
// case len(sigs) == 0 && err != nil:
|
||||
// return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
// case len(sigs) == 0:
|
||||
// return nil, nil, errors.New("no signatures found in signature file")
|
||||
// case err != nil:
|
||||
// return nil, nil, fmt.Errorf("failed to parse signature file: %w", err)
|
||||
// }
|
||||
|
||||
// Verify all signatures.
|
||||
var verifiedHash *lhash.LabeledHash
|
||||
for _, sig := range sigs {
|
||||
fd, err := filesig.VerifyFileData(
|
||||
sig,
|
||||
nil,
|
||||
verifOpts.TrustStore,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, sigFileData, err
|
||||
}
|
||||
// // Verify all signatures.
|
||||
// var verifiedHash *lhash.LabeledHash
|
||||
// for _, sig := range sigs {
|
||||
// fd, err := filesig.VerifyFileData(
|
||||
// sig,
|
||||
// nil,
|
||||
// verifOpts.TrustStore,
|
||||
// )
|
||||
// if err != nil {
|
||||
// return nil, sigFileData, err
|
||||
// }
|
||||
|
||||
// Save or check verified hash.
|
||||
if verifiedHash == nil {
|
||||
verifiedHash = fd.FileHash()
|
||||
} else if !fd.FileHash().Equal(verifiedHash) {
|
||||
// Return an error if two valid hashes mismatch.
|
||||
// For simplicity, all hash algorithms must be the same for now.
|
||||
return nil, sigFileData, errors.New("file hashes from different signatures do not match")
|
||||
}
|
||||
}
|
||||
// // Save or check verified hash.
|
||||
// if verifiedHash == nil {
|
||||
// verifiedHash = fd.FileHash()
|
||||
// } else if !fd.FileHash().Equal(verifiedHash) {
|
||||
// // Return an error if two valid hashes mismatch.
|
||||
// // For simplicity, all hash algorithms must be the same for now.
|
||||
// return nil, sigFileData, errors.New("file hashes from different signatures do not match")
|
||||
// }
|
||||
// }
|
||||
|
||||
return verifiedHash, sigFileData, nil
|
||||
}
|
||||
// return verifiedHash, sigFileData, nil
|
||||
// }
|
||||
|
||||
// CreateSymlinks creates a directory structure with unversioned symlinks to the given updates list.
|
||||
func (reg *ResourceRegistry) CreateSymlinks(symlinkRoot *utils.DirStructure) error {
|
||||
err := os.RemoveAll(symlinkRoot.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wipe symlink root: %w", err)
|
||||
}
|
||||
// // CreateSymlinks creates a directory structure with unversioned symlinks to the given updates list.
|
||||
// func (reg *ResourceRegistry) CreateSymlinks(symlinkRoot *utils.DirStructure) error {
|
||||
// err := os.RemoveAll(symlinkRoot.Path)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to wipe symlink root: %w", err)
|
||||
// }
|
||||
|
||||
err = symlinkRoot.Ensure()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create symlink root: %w", err)
|
||||
}
|
||||
// err = symlinkRoot.Ensure()
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to create symlink root: %w", err)
|
||||
// }
|
||||
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
for _, res := range reg.resources {
|
||||
if res.SelectedVersion == nil {
|
||||
return fmt.Errorf("no selected version available for %s", res.Identifier)
|
||||
}
|
||||
// for _, res := range reg.resources {
|
||||
// if res.SelectedVersion == nil {
|
||||
// return fmt.Errorf("no selected version available for %s", res.Identifier)
|
||||
// }
|
||||
|
||||
targetPath := res.SelectedVersion.storagePath()
|
||||
linkPath := filepath.Join(symlinkRoot.Path, filepath.FromSlash(res.Identifier))
|
||||
linkPathDir := filepath.Dir(linkPath)
|
||||
// targetPath := res.SelectedVersion.storagePath()
|
||||
// linkPath := filepath.Join(symlinkRoot.Path, filepath.FromSlash(res.Identifier))
|
||||
// linkPathDir := filepath.Dir(linkPath)
|
||||
|
||||
err = symlinkRoot.EnsureAbsPath(linkPathDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create dir for link: %w", err)
|
||||
}
|
||||
// err = symlinkRoot.EnsureAbsPath(linkPathDir)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to create dir for link: %w", err)
|
||||
// }
|
||||
|
||||
relativeTargetPath, err := filepath.Rel(linkPathDir, targetPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get relative target path: %w", err)
|
||||
}
|
||||
// relativeTargetPath, err := filepath.Rel(linkPathDir, targetPath)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to get relative target path: %w", err)
|
||||
// }
|
||||
|
||||
err = os.Symlink(relativeTargetPath, linkPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link %s: %w", res.Identifier, err)
|
||||
}
|
||||
}
|
||||
// err = os.Symlink(relativeTargetPath, linkPath)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to link %s: %w", res.Identifier, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
@@ -1,195 +1,195 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
// import (
|
||||
// "archive/zip"
|
||||
// "compress/gzip"
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "io"
|
||||
// "io/fs"
|
||||
// "os"
|
||||
// "path"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
// "github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/utils"
|
||||
// )
|
||||
|
||||
// MaxUnpackSize specifies the maximum size that will be unpacked.
|
||||
const MaxUnpackSize = 1000000000 // 1GB
|
||||
// // MaxUnpackSize specifies the maximum size that will be unpacked.
|
||||
// const MaxUnpackSize = 1000000000 // 1GB
|
||||
|
||||
// UnpackGZIP unpacks a GZIP compressed reader r
|
||||
// and returns a new reader. It's suitable to be
|
||||
// used with registry.GetPackedFile.
|
||||
func UnpackGZIP(r io.Reader) (io.Reader, error) {
|
||||
return gzip.NewReader(r)
|
||||
}
|
||||
// // UnpackGZIP unpacks a GZIP compressed reader r
|
||||
// // and returns a new reader. It's suitable to be
|
||||
// // used with registry.GetPackedFile.
|
||||
// func UnpackGZIP(r io.Reader) (io.Reader, error) {
|
||||
// return gzip.NewReader(r)
|
||||
// }
|
||||
|
||||
// UnpackResources unpacks all resources defined in the AutoUnpack list.
|
||||
func (reg *ResourceRegistry) UnpackResources() error {
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// // UnpackResources unpacks all resources defined in the AutoUnpack list.
|
||||
// func (reg *ResourceRegistry) UnpackResources() error {
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
var multierr *multierror.Error
|
||||
for _, res := range reg.resources {
|
||||
if utils.StringInSlice(reg.AutoUnpack, res.Identifier) {
|
||||
err := res.UnpackArchive()
|
||||
if err != nil {
|
||||
multierr = multierror.Append(
|
||||
multierr,
|
||||
fmt.Errorf("%s: %w", res.Identifier, err),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// var multierr *multierror.Error
|
||||
// for _, res := range reg.resources {
|
||||
// if utils.StringInSlice(reg.AutoUnpack, res.Identifier) {
|
||||
// err := res.UnpackArchive()
|
||||
// if err != nil {
|
||||
// multierr = multierror.Append(
|
||||
// multierr,
|
||||
// fmt.Errorf("%s: %w", res.Identifier, err),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return multierr.ErrorOrNil()
|
||||
}
|
||||
// return multierr.ErrorOrNil()
|
||||
// }
|
||||
|
||||
const (
|
||||
zipSuffix = ".zip"
|
||||
)
|
||||
// const (
|
||||
// zipSuffix = ".zip"
|
||||
// )
|
||||
|
||||
// UnpackArchive unpacks the archive the resource refers to. The contents are
|
||||
// unpacked into a directory with the same name as the file, excluding the
|
||||
// suffix. If the destination folder already exists, it is assumed that the
|
||||
// contents have already been correctly unpacked.
|
||||
func (res *Resource) UnpackArchive() error {
|
||||
res.Lock()
|
||||
defer res.Unlock()
|
||||
// // UnpackArchive unpacks the archive the resource refers to. The contents are
|
||||
// // unpacked into a directory with the same name as the file, excluding the
|
||||
// // suffix. If the destination folder already exists, it is assumed that the
|
||||
// // contents have already been correctly unpacked.
|
||||
// func (res *Resource) UnpackArchive() error {
|
||||
// res.Lock()
|
||||
// defer res.Unlock()
|
||||
|
||||
// Only unpack selected versions.
|
||||
if res.SelectedVersion == nil {
|
||||
return nil
|
||||
}
|
||||
// // Only unpack selected versions.
|
||||
// if res.SelectedVersion == nil {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(res.Identifier, zipSuffix):
|
||||
return res.unpackZipArchive()
|
||||
default:
|
||||
return fmt.Errorf("unsupported file type for unpacking")
|
||||
}
|
||||
}
|
||||
// switch {
|
||||
// case strings.HasSuffix(res.Identifier, zipSuffix):
|
||||
// return res.unpackZipArchive()
|
||||
// default:
|
||||
// return fmt.Errorf("unsupported file type for unpacking")
|
||||
// }
|
||||
// }
|
||||
|
||||
func (res *Resource) unpackZipArchive() error {
|
||||
// Get file and directory paths.
|
||||
archiveFile := res.SelectedVersion.storagePath()
|
||||
destDir := strings.TrimSuffix(archiveFile, zipSuffix)
|
||||
tmpDir := filepath.Join(
|
||||
res.registry.tmpDir.Path,
|
||||
filepath.FromSlash(strings.TrimSuffix(
|
||||
path.Base(res.SelectedVersion.versionedPath()),
|
||||
zipSuffix,
|
||||
)),
|
||||
)
|
||||
// func (res *Resource) unpackZipArchive() error {
|
||||
// // Get file and directory paths.
|
||||
// archiveFile := res.SelectedVersion.storagePath()
|
||||
// destDir := strings.TrimSuffix(archiveFile, zipSuffix)
|
||||
// tmpDir := filepath.Join(
|
||||
// res.registry.tmpDir.Path,
|
||||
// filepath.FromSlash(strings.TrimSuffix(
|
||||
// path.Base(res.SelectedVersion.versionedPath()),
|
||||
// zipSuffix,
|
||||
// )),
|
||||
// )
|
||||
|
||||
// Check status of destination.
|
||||
dstStat, err := os.Stat(destDir)
|
||||
switch {
|
||||
case errors.Is(err, fs.ErrNotExist):
|
||||
// The destination does not exist, continue with unpacking.
|
||||
case err != nil:
|
||||
return fmt.Errorf("cannot access destination for unpacking: %w", err)
|
||||
case !dstStat.IsDir():
|
||||
return fmt.Errorf("destination for unpacking is blocked by file: %s", dstStat.Name())
|
||||
default:
|
||||
// Archive already seems to be unpacked.
|
||||
return nil
|
||||
}
|
||||
// // Check status of destination.
|
||||
// dstStat, err := os.Stat(destDir)
|
||||
// switch {
|
||||
// case errors.Is(err, fs.ErrNotExist):
|
||||
// // The destination does not exist, continue with unpacking.
|
||||
// case err != nil:
|
||||
// return fmt.Errorf("cannot access destination for unpacking: %w", err)
|
||||
// case !dstStat.IsDir():
|
||||
// return fmt.Errorf("destination for unpacking is blocked by file: %s", dstStat.Name())
|
||||
// default:
|
||||
// // Archive already seems to be unpacked.
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Create the tmp directory for unpacking.
|
||||
err = res.registry.tmpDir.EnsureAbsPath(tmpDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tmp dir for unpacking: %w", err)
|
||||
}
|
||||
// // Create the tmp directory for unpacking.
|
||||
// err = res.registry.tmpDir.EnsureAbsPath(tmpDir)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to create tmp dir for unpacking: %w", err)
|
||||
// }
|
||||
|
||||
// Defer clean up of directories.
|
||||
defer func() {
|
||||
// Always clean up the tmp dir.
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
// Cleanup the destination in case of an error.
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(destDir)
|
||||
}
|
||||
}()
|
||||
// // Defer clean up of directories.
|
||||
// defer func() {
|
||||
// // Always clean up the tmp dir.
|
||||
// _ = os.RemoveAll(tmpDir)
|
||||
// // Cleanup the destination in case of an error.
|
||||
// if err != nil {
|
||||
// _ = os.RemoveAll(destDir)
|
||||
// }
|
||||
// }()
|
||||
|
||||
// Open the archive for reading.
|
||||
var archiveReader *zip.ReadCloser
|
||||
archiveReader, err = zip.OpenReader(archiveFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open zip reader: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = archiveReader.Close()
|
||||
}()
|
||||
// // Open the archive for reading.
|
||||
// var archiveReader *zip.ReadCloser
|
||||
// archiveReader, err = zip.OpenReader(archiveFile)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to open zip reader: %w", err)
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = archiveReader.Close()
|
||||
// }()
|
||||
|
||||
// Save all files to the tmp dir.
|
||||
for _, file := range archiveReader.File {
|
||||
err = copyFromZipArchive(
|
||||
file,
|
||||
filepath.Join(tmpDir, filepath.FromSlash(file.Name)),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract archive file %s: %w", file.Name, err)
|
||||
}
|
||||
}
|
||||
// // Save all files to the tmp dir.
|
||||
// for _, file := range archiveReader.File {
|
||||
// err = copyFromZipArchive(
|
||||
// file,
|
||||
// filepath.Join(tmpDir, filepath.FromSlash(file.Name)),
|
||||
// )
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to extract archive file %s: %w", file.Name, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// Make the final move.
|
||||
err = os.Rename(tmpDir, destDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to move the extracted archive from %s to %s: %w", tmpDir, destDir, err)
|
||||
}
|
||||
// // Make the final move.
|
||||
// err = os.Rename(tmpDir, destDir)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to move the extracted archive from %s to %s: %w", tmpDir, destDir, err)
|
||||
// }
|
||||
|
||||
// Fix permissions on the destination dir.
|
||||
err = res.registry.storageDir.EnsureAbsPath(destDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply directory permissions on %s: %w", destDir, err)
|
||||
}
|
||||
// // Fix permissions on the destination dir.
|
||||
// err = res.registry.storageDir.EnsureAbsPath(destDir)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to apply directory permissions on %s: %w", destDir, err)
|
||||
// }
|
||||
|
||||
log.Infof("%s: unpacked %s", res.registry.Name, res.SelectedVersion.versionedPath())
|
||||
return nil
|
||||
}
|
||||
// log.Infof("%s: unpacked %s", res.registry.Name, res.SelectedVersion.versionedPath())
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func copyFromZipArchive(archiveFile *zip.File, dstPath string) error {
|
||||
// If file is a directory, create it and continue.
|
||||
if archiveFile.FileInfo().IsDir() {
|
||||
err := os.Mkdir(dstPath, archiveFile.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", dstPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// func copyFromZipArchive(archiveFile *zip.File, dstPath string) error {
|
||||
// // If file is a directory, create it and continue.
|
||||
// if archiveFile.FileInfo().IsDir() {
|
||||
// err := os.Mkdir(dstPath, archiveFile.Mode())
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to create directory %s: %w", dstPath, err)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Open archived file for reading.
|
||||
fileReader, err := archiveFile.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file in archive: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = fileReader.Close()
|
||||
}()
|
||||
// // Open archived file for reading.
|
||||
// fileReader, err := archiveFile.Open()
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to open file in archive: %w", err)
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = fileReader.Close()
|
||||
// }()
|
||||
|
||||
// Open destination file for writing.
|
||||
dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, archiveFile.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open destination file %s: %w", dstPath, err)
|
||||
}
|
||||
defer func() {
|
||||
_ = dstFile.Close()
|
||||
}()
|
||||
// // Open destination file for writing.
|
||||
// dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, archiveFile.Mode())
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to open destination file %s: %w", dstPath, err)
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = dstFile.Close()
|
||||
// }()
|
||||
|
||||
// Copy full file from archive to dst.
|
||||
if _, err := io.CopyN(dstFile, fileReader, MaxUnpackSize); err != nil {
|
||||
// EOF is expected here as the archive is likely smaller
|
||||
// thane MaxUnpackSize
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// // Copy full file from archive to dst.
|
||||
// if _, err := io.CopyN(dstFile, fileReader, MaxUnpackSize); err != nil {
|
||||
// // EOF is expected here as the archive is likely smaller
|
||||
// // thane MaxUnpackSize
|
||||
// if errors.Is(err, io.EOF) {
|
||||
// return nil
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
@@ -1,359 +1,359 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
// import (
|
||||
// "context"
|
||||
// "fmt"
|
||||
// "net/http"
|
||||
// "os"
|
||||
// "path"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
// "golang.org/x/exp/slices"
|
||||
|
||||
"github.com/safing/jess/filesig"
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
// "github.com/safing/jess/filesig"
|
||||
// "github.com/safing/jess/lhash"
|
||||
// "github.com/safing/portmaster/base/log"
|
||||
// "github.com/safing/portmaster/base/utils"
|
||||
// )
|
||||
|
||||
// UpdateIndexes downloads all indexes. An error is only returned when all
|
||||
// indexes fail to update.
|
||||
func (reg *ResourceRegistry) UpdateIndexes(ctx context.Context) error {
|
||||
var lastErr error
|
||||
var anySuccess bool
|
||||
// // UpdateIndexes downloads all indexes. An error is only returned when all
|
||||
// // indexes fail to update.
|
||||
// func (reg *ResourceRegistry) UpdateIndexes(ctx context.Context) error {
|
||||
// var lastErr error
|
||||
// var anySuccess bool
|
||||
|
||||
// Start registry operation.
|
||||
reg.state.StartOperation(StateChecking)
|
||||
defer reg.state.EndOperation()
|
||||
// // Start registry operation.
|
||||
// reg.state.StartOperation(StateChecking)
|
||||
// defer reg.state.EndOperation()
|
||||
|
||||
client := &http.Client{}
|
||||
for _, idx := range reg.getIndexes() {
|
||||
if err := reg.downloadIndex(ctx, client, idx); err != nil {
|
||||
lastErr = err
|
||||
log.Warningf("%s: failed to update index %s: %s", reg.Name, idx.Path, err)
|
||||
} else {
|
||||
anySuccess = true
|
||||
}
|
||||
}
|
||||
// client := &http.Client{}
|
||||
// for _, idx := range reg.getIndexes() {
|
||||
// if err := reg.downloadIndex(ctx, client, idx); err != nil {
|
||||
// lastErr = err
|
||||
// log.Warningf("%s: failed to update index %s: %s", reg.Name, idx.Path, err)
|
||||
// } else {
|
||||
// anySuccess = true
|
||||
// }
|
||||
// }
|
||||
|
||||
// If all indexes failed to update, fail.
|
||||
if !anySuccess {
|
||||
err := fmt.Errorf("failed to update all indexes, last error was: %w", lastErr)
|
||||
reg.state.ReportUpdateCheck(nil, err)
|
||||
return err
|
||||
}
|
||||
// // If all indexes failed to update, fail.
|
||||
// if !anySuccess {
|
||||
// err := fmt.Errorf("failed to update all indexes, last error was: %w", lastErr)
|
||||
// reg.state.ReportUpdateCheck(nil, err)
|
||||
// return err
|
||||
// }
|
||||
|
||||
// Get pending resources and update status.
|
||||
pendingResourceVersions, _ := reg.GetPendingDownloads(true, false)
|
||||
reg.state.ReportUpdateCheck(
|
||||
humanInfoFromResourceVersions(pendingResourceVersions),
|
||||
nil,
|
||||
)
|
||||
// // Get pending resources and update status.
|
||||
// pendingResourceVersions, _ := reg.GetPendingDownloads(true, false)
|
||||
// reg.state.ReportUpdateCheck(
|
||||
// humanInfoFromResourceVersions(pendingResourceVersions),
|
||||
// nil,
|
||||
// )
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (reg *ResourceRegistry) downloadIndex(ctx context.Context, client *http.Client, idx *Index) error {
|
||||
var (
|
||||
// Index.
|
||||
indexErr error
|
||||
indexData []byte
|
||||
downloadURL string
|
||||
// func (reg *ResourceRegistry) downloadIndex(ctx context.Context, client *http.Client, idx *Index) error {
|
||||
// var (
|
||||
// // Index.
|
||||
// indexErr error
|
||||
// indexData []byte
|
||||
// downloadURL string
|
||||
|
||||
// Signature.
|
||||
sigErr error
|
||||
verifiedHash *lhash.LabeledHash
|
||||
sigFileData []byte
|
||||
verifOpts = reg.GetVerificationOptions(idx.Path)
|
||||
)
|
||||
// // Signature.
|
||||
// sigErr error
|
||||
// verifiedHash *lhash.LabeledHash
|
||||
// sigFileData []byte
|
||||
// verifOpts = reg.GetVerificationOptions(idx.Path)
|
||||
// )
|
||||
|
||||
// Upgrade to v2 index if verification is enabled.
|
||||
downloadIndexPath := idx.Path
|
||||
if verifOpts != nil {
|
||||
downloadIndexPath = strings.TrimSuffix(downloadIndexPath, baseIndexExtension) + v2IndexExtension
|
||||
}
|
||||
// // Upgrade to v2 index if verification is enabled.
|
||||
// downloadIndexPath := idx.Path
|
||||
// if verifOpts != nil {
|
||||
// downloadIndexPath = strings.TrimSuffix(downloadIndexPath, baseIndexExtension) + v2IndexExtension
|
||||
// }
|
||||
|
||||
// Download new index and signature.
|
||||
for tries := range 3 {
|
||||
// Index and signature need to be fetched together, so that they are
|
||||
// fetched from the same source. One source should always have a matching
|
||||
// index and signature. Backup sources may be behind a little.
|
||||
// If the signature verification fails, another source should be tried.
|
||||
// // Download new index and signature.
|
||||
// for tries := range 3 {
|
||||
// // Index and signature need to be fetched together, so that they are
|
||||
// // fetched from the same source. One source should always have a matching
|
||||
// // index and signature. Backup sources may be behind a little.
|
||||
// // If the signature verification fails, another source should be tried.
|
||||
|
||||
// Get index data.
|
||||
indexData, downloadURL, indexErr = reg.fetchData(ctx, client, downloadIndexPath, tries)
|
||||
if indexErr != nil {
|
||||
log.Debugf("%s: failed to fetch index %s: %s", reg.Name, downloadURL, indexErr)
|
||||
continue
|
||||
}
|
||||
// // Get index data.
|
||||
// indexData, downloadURL, indexErr = reg.fetchData(ctx, client, downloadIndexPath, tries)
|
||||
// if indexErr != nil {
|
||||
// log.Debugf("%s: failed to fetch index %s: %s", reg.Name, downloadURL, indexErr)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// Get signature and verify it.
|
||||
if verifOpts != nil {
|
||||
verifiedHash, sigFileData, sigErr = reg.fetchAndVerifySigFile(
|
||||
ctx, client,
|
||||
verifOpts, downloadIndexPath+filesig.Extension, nil,
|
||||
tries,
|
||||
)
|
||||
if sigErr != nil {
|
||||
log.Debugf("%s: failed to verify signature of %s: %s", reg.Name, downloadURL, sigErr)
|
||||
continue
|
||||
}
|
||||
// // Get signature and verify it.
|
||||
// if verifOpts != nil {
|
||||
// verifiedHash, sigFileData, sigErr = reg.fetchAndVerifySigFile(
|
||||
// ctx, client,
|
||||
// verifOpts, downloadIndexPath+filesig.Extension, nil,
|
||||
// tries,
|
||||
// )
|
||||
// if sigErr != nil {
|
||||
// log.Debugf("%s: failed to verify signature of %s: %s", reg.Name, downloadURL, sigErr)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// Check if the index matches the verified hash.
|
||||
if verifiedHash.Matches(indexData) {
|
||||
log.Infof("%s: verified signature of %s", reg.Name, downloadURL)
|
||||
} else {
|
||||
sigErr = ErrIndexChecksumMismatch
|
||||
log.Debugf("%s: checksum does not match file from %s", reg.Name, downloadURL)
|
||||
continue
|
||||
}
|
||||
}
|
||||
// // Check if the index matches the verified hash.
|
||||
// if verifiedHash.Matches(indexData) {
|
||||
// log.Infof("%s: verified signature of %s", reg.Name, downloadURL)
|
||||
// } else {
|
||||
// sigErr = ErrIndexChecksumMismatch
|
||||
// log.Debugf("%s: checksum does not match file from %s", reg.Name, downloadURL)
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
|
||||
break
|
||||
}
|
||||
if indexErr != nil {
|
||||
return fmt.Errorf("failed to fetch index %s: %w", downloadIndexPath, indexErr)
|
||||
}
|
||||
if sigErr != nil {
|
||||
return fmt.Errorf("failed to fetch or verify index %s signature: %w", downloadIndexPath, sigErr)
|
||||
}
|
||||
// break
|
||||
// }
|
||||
// if indexErr != nil {
|
||||
// return fmt.Errorf("failed to fetch index %s: %w", downloadIndexPath, indexErr)
|
||||
// }
|
||||
// if sigErr != nil {
|
||||
// return fmt.Errorf("failed to fetch or verify index %s signature: %w", downloadIndexPath, sigErr)
|
||||
// }
|
||||
|
||||
// Parse the index file.
|
||||
indexFile, err := ParseIndexFile(indexData, idx.Channel, idx.LastRelease)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse index %s: %w", idx.Path, err)
|
||||
}
|
||||
// // Parse the index file.
|
||||
// indexFile, err := ParseIndexFile(indexData, idx.Channel, idx.LastRelease)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("failed to parse index %s: %w", idx.Path, err)
|
||||
// }
|
||||
|
||||
// Add index data to registry.
|
||||
if len(indexFile.Releases) > 0 {
|
||||
// Check if all resources are within the indexes' authority.
|
||||
authoritativePath := path.Dir(idx.Path) + "/"
|
||||
if authoritativePath == "./" {
|
||||
// Fix path for indexes at the storage root.
|
||||
authoritativePath = ""
|
||||
}
|
||||
cleanedData := make(map[string]string, len(indexFile.Releases))
|
||||
for key, version := range indexFile.Releases {
|
||||
if strings.HasPrefix(key, authoritativePath) {
|
||||
cleanedData[key] = version
|
||||
} else {
|
||||
log.Warningf("%s: index %s oversteps it's authority by defining version for %s", reg.Name, idx.Path, key)
|
||||
}
|
||||
}
|
||||
// // Add index data to registry.
|
||||
// if len(indexFile.Releases) > 0 {
|
||||
// // Check if all resources are within the indexes' authority.
|
||||
// authoritativePath := path.Dir(idx.Path) + "/"
|
||||
// if authoritativePath == "./" {
|
||||
// // Fix path for indexes at the storage root.
|
||||
// authoritativePath = ""
|
||||
// }
|
||||
// cleanedData := make(map[string]string, len(indexFile.Releases))
|
||||
// for key, version := range indexFile.Releases {
|
||||
// if strings.HasPrefix(key, authoritativePath) {
|
||||
// cleanedData[key] = version
|
||||
// } else {
|
||||
// log.Warningf("%s: index %s oversteps it's authority by defining version for %s", reg.Name, idx.Path, key)
|
||||
// }
|
||||
// }
|
||||
|
||||
// add resources to registry
|
||||
err = reg.AddResources(cleanedData, idx, false, true, idx.PreRelease)
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to add resources: %s", reg.Name, err)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("%s: index %s is empty", reg.Name, idx.Path)
|
||||
}
|
||||
// // add resources to registry
|
||||
// err = reg.AddResources(cleanedData, idx, false, true, idx.PreRelease)
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to add resources: %s", reg.Name, err)
|
||||
// }
|
||||
// } else {
|
||||
// log.Debugf("%s: index %s is empty", reg.Name, idx.Path)
|
||||
// }
|
||||
|
||||
// Check if dest dir exists.
|
||||
indexDir := filepath.FromSlash(path.Dir(idx.Path))
|
||||
err = reg.storageDir.EnsureRelPath(indexDir)
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to ensure directory for updated index %s: %s", reg.Name, idx.Path, err)
|
||||
}
|
||||
// // Check if dest dir exists.
|
||||
// indexDir := filepath.FromSlash(path.Dir(idx.Path))
|
||||
// err = reg.storageDir.EnsureRelPath(indexDir)
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to ensure directory for updated index %s: %s", reg.Name, idx.Path, err)
|
||||
// }
|
||||
|
||||
// Index files must be readable by portmaster-staert with user permissions in order to load the index.
|
||||
err = os.WriteFile( //nolint:gosec
|
||||
filepath.Join(reg.storageDir.Path, filepath.FromSlash(idx.Path)),
|
||||
indexData, 0o0644,
|
||||
)
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to save updated index %s: %s", reg.Name, idx.Path, err)
|
||||
}
|
||||
// // Index files must be readable by portmaster-staert with user permissions in order to load the index.
|
||||
// err = os.WriteFile( //nolint:gosec
|
||||
// filepath.Join(reg.storageDir.Path, filepath.FromSlash(idx.Path)),
|
||||
// indexData, 0o0644,
|
||||
// )
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to save updated index %s: %s", reg.Name, idx.Path, err)
|
||||
// }
|
||||
|
||||
// Write signature file, if we have one.
|
||||
if len(sigFileData) > 0 {
|
||||
err = os.WriteFile( //nolint:gosec
|
||||
filepath.Join(reg.storageDir.Path, filepath.FromSlash(idx.Path)+filesig.Extension),
|
||||
sigFileData, 0o0644,
|
||||
)
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to save updated index signature %s: %s", reg.Name, idx.Path+filesig.Extension, err)
|
||||
}
|
||||
}
|
||||
// // Write signature file, if we have one.
|
||||
// if len(sigFileData) > 0 {
|
||||
// err = os.WriteFile( //nolint:gosec
|
||||
// filepath.Join(reg.storageDir.Path, filepath.FromSlash(idx.Path)+filesig.Extension),
|
||||
// sigFileData, 0o0644,
|
||||
// )
|
||||
// if err != nil {
|
||||
// log.Warningf("%s: failed to save updated index signature %s: %s", reg.Name, idx.Path+filesig.Extension, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
log.Infof("%s: updated index %s with %d entries", reg.Name, idx.Path, len(indexFile.Releases))
|
||||
return nil
|
||||
}
|
||||
// log.Infof("%s: updated index %s with %d entries", reg.Name, idx.Path, len(indexFile.Releases))
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// DownloadUpdates checks if updates are available and downloads updates of used components.
|
||||
func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context, includeManual bool) error {
|
||||
// Start registry operation.
|
||||
reg.state.StartOperation(StateDownloading)
|
||||
defer reg.state.EndOperation()
|
||||
// // DownloadUpdates checks if updates are available and downloads updates of used components.
|
||||
// func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context, includeManual bool) error {
|
||||
// // Start registry operation.
|
||||
// reg.state.StartOperation(StateDownloading)
|
||||
// defer reg.state.EndOperation()
|
||||
|
||||
// Get pending updates.
|
||||
toUpdate, missingSigs := reg.GetPendingDownloads(includeManual, true)
|
||||
downloadDetailsResources := humanInfoFromResourceVersions(toUpdate)
|
||||
reg.state.UpdateOperationDetails(&StateDownloadingDetails{
|
||||
Resources: downloadDetailsResources,
|
||||
})
|
||||
// // Get pending updates.
|
||||
// toUpdate, missingSigs := reg.GetPendingDownloads(includeManual, true)
|
||||
// downloadDetailsResources := humanInfoFromResourceVersions(toUpdate)
|
||||
// reg.state.UpdateOperationDetails(&StateDownloadingDetails{
|
||||
// Resources: downloadDetailsResources,
|
||||
// })
|
||||
|
||||
// nothing to update
|
||||
if len(toUpdate) == 0 && len(missingSigs) == 0 {
|
||||
log.Infof("%s: everything up to date", reg.Name)
|
||||
return nil
|
||||
}
|
||||
// // nothing to update
|
||||
// if len(toUpdate) == 0 && len(missingSigs) == 0 {
|
||||
// log.Infof("%s: everything up to date", reg.Name)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// check download dir
|
||||
if err := reg.tmpDir.Ensure(); err != nil {
|
||||
return fmt.Errorf("could not prepare tmp directory for download: %w", err)
|
||||
}
|
||||
// // check download dir
|
||||
// if err := reg.tmpDir.Ensure(); err != nil {
|
||||
// return fmt.Errorf("could not prepare tmp directory for download: %w", err)
|
||||
// }
|
||||
|
||||
// download updates
|
||||
log.Infof("%s: starting to download %d updates", reg.Name, len(toUpdate))
|
||||
client := &http.Client{}
|
||||
var reportError error
|
||||
// // download updates
|
||||
// log.Infof("%s: starting to download %d updates", reg.Name, len(toUpdate))
|
||||
// client := &http.Client{}
|
||||
// var reportError error
|
||||
|
||||
for i, rv := range toUpdate {
|
||||
log.Infof(
|
||||
"%s: downloading update [%d/%d]: %s version %s",
|
||||
reg.Name,
|
||||
i+1, len(toUpdate),
|
||||
rv.resource.Identifier, rv.VersionNumber,
|
||||
)
|
||||
var err error
|
||||
for tries := range 3 {
|
||||
err = reg.fetchFile(ctx, client, rv, tries)
|
||||
if err == nil {
|
||||
// Update resource version state.
|
||||
rv.resource.Lock()
|
||||
rv.Available = true
|
||||
if rv.resource.VerificationOptions != nil {
|
||||
rv.SigAvailable = true
|
||||
}
|
||||
rv.resource.Unlock()
|
||||
// for i, rv := range toUpdate {
|
||||
// log.Infof(
|
||||
// "%s: downloading update [%d/%d]: %s version %s",
|
||||
// reg.Name,
|
||||
// i+1, len(toUpdate),
|
||||
// rv.resource.Identifier, rv.VersionNumber,
|
||||
// )
|
||||
// var err error
|
||||
// for tries := range 3 {
|
||||
// err = reg.fetchFile(ctx, client, rv, tries)
|
||||
// if err == nil {
|
||||
// // Update resource version state.
|
||||
// rv.resource.Lock()
|
||||
// rv.Available = true
|
||||
// if rv.resource.VerificationOptions != nil {
|
||||
// rv.SigAvailable = true
|
||||
// }
|
||||
// rv.resource.Unlock()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
reportError := fmt.Errorf("failed to download %s version %s: %w", rv.resource.Identifier, rv.VersionNumber, err)
|
||||
log.Warningf("%s: %s", reg.Name, reportError)
|
||||
}
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if err != nil {
|
||||
// reportError := fmt.Errorf("failed to download %s version %s: %w", rv.resource.Identifier, rv.VersionNumber, err)
|
||||
// log.Warningf("%s: %s", reg.Name, reportError)
|
||||
// }
|
||||
|
||||
reg.state.UpdateOperationDetails(&StateDownloadingDetails{
|
||||
Resources: downloadDetailsResources,
|
||||
FinishedUpTo: i + 1,
|
||||
})
|
||||
}
|
||||
// reg.state.UpdateOperationDetails(&StateDownloadingDetails{
|
||||
// Resources: downloadDetailsResources,
|
||||
// FinishedUpTo: i + 1,
|
||||
// })
|
||||
// }
|
||||
|
||||
if len(missingSigs) > 0 {
|
||||
log.Infof("%s: downloading %d missing signatures", reg.Name, len(missingSigs))
|
||||
// if len(missingSigs) > 0 {
|
||||
// log.Infof("%s: downloading %d missing signatures", reg.Name, len(missingSigs))
|
||||
|
||||
for _, rv := range missingSigs {
|
||||
var err error
|
||||
for tries := range 3 {
|
||||
err = reg.fetchMissingSig(ctx, client, rv, tries)
|
||||
if err == nil {
|
||||
// Update resource version state.
|
||||
rv.resource.Lock()
|
||||
rv.SigAvailable = true
|
||||
rv.resource.Unlock()
|
||||
// for _, rv := range missingSigs {
|
||||
// var err error
|
||||
// for tries := range 3 {
|
||||
// err = reg.fetchMissingSig(ctx, client, rv, tries)
|
||||
// if err == nil {
|
||||
// // Update resource version state.
|
||||
// rv.resource.Lock()
|
||||
// rv.SigAvailable = true
|
||||
// rv.resource.Unlock()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
reportError := fmt.Errorf("failed to download missing sig of %s version %s: %w", rv.resource.Identifier, rv.VersionNumber, err)
|
||||
log.Warningf("%s: %s", reg.Name, reportError)
|
||||
}
|
||||
}
|
||||
}
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if err != nil {
|
||||
// reportError := fmt.Errorf("failed to download missing sig of %s version %s: %w", rv.resource.Identifier, rv.VersionNumber, err)
|
||||
// log.Warningf("%s: %s", reg.Name, reportError)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
reg.state.ReportDownloads(
|
||||
downloadDetailsResources,
|
||||
reportError,
|
||||
)
|
||||
log.Infof("%s: finished downloading updates", reg.Name)
|
||||
// reg.state.ReportDownloads(
|
||||
// downloadDetailsResources,
|
||||
// reportError,
|
||||
// )
|
||||
// log.Infof("%s: finished downloading updates", reg.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// DownloadUpdates checks if updates are available and downloads updates of used components.
|
||||
// // DownloadUpdates checks if updates are available and downloads updates of used components.
|
||||
|
||||
// GetPendingDownloads returns the list of pending downloads.
|
||||
// If manual is set, indexes with AutoDownload=false will be checked.
|
||||
// If auto is set, indexes with AutoDownload=true will be checked.
|
||||
func (reg *ResourceRegistry) GetPendingDownloads(manual, auto bool) (resources, sigs []*ResourceVersion) {
|
||||
reg.RLock()
|
||||
defer reg.RUnlock()
|
||||
// // GetPendingDownloads returns the list of pending downloads.
|
||||
// // If manual is set, indexes with AutoDownload=false will be checked.
|
||||
// // If auto is set, indexes with AutoDownload=true will be checked.
|
||||
// func (reg *ResourceRegistry) GetPendingDownloads(manual, auto bool) (resources, sigs []*ResourceVersion) {
|
||||
// reg.RLock()
|
||||
// defer reg.RUnlock()
|
||||
|
||||
// create list of downloads
|
||||
var toUpdate []*ResourceVersion
|
||||
var missingSigs []*ResourceVersion
|
||||
// // create list of downloads
|
||||
// var toUpdate []*ResourceVersion
|
||||
// var missingSigs []*ResourceVersion
|
||||
|
||||
for _, res := range reg.resources {
|
||||
func() {
|
||||
res.Lock()
|
||||
defer res.Unlock()
|
||||
// for _, res := range reg.resources {
|
||||
// func() {
|
||||
// res.Lock()
|
||||
// defer res.Unlock()
|
||||
|
||||
// Skip resources without index or indexes that should not be reported
|
||||
// according to parameters.
|
||||
switch {
|
||||
case res.Index == nil:
|
||||
// Cannot download if resource is not part of an index.
|
||||
return
|
||||
case manual && !res.Index.AutoDownload:
|
||||
// Manual update report and index is not auto-download.
|
||||
case auto && res.Index.AutoDownload:
|
||||
// Auto update report and index is auto-download.
|
||||
default:
|
||||
// Resource should not be reported.
|
||||
return
|
||||
}
|
||||
// // Skip resources without index or indexes that should not be reported
|
||||
// // according to parameters.
|
||||
// switch {
|
||||
// case res.Index == nil:
|
||||
// // Cannot download if resource is not part of an index.
|
||||
// return
|
||||
// case manual && !res.Index.AutoDownload:
|
||||
// // Manual update report and index is not auto-download.
|
||||
// case auto && res.Index.AutoDownload:
|
||||
// // Auto update report and index is auto-download.
|
||||
// default:
|
||||
// // Resource should not be reported.
|
||||
// return
|
||||
// }
|
||||
|
||||
// Skip resources we don't need.
|
||||
switch {
|
||||
case res.inUse():
|
||||
// Update if resource is in use.
|
||||
case res.available():
|
||||
// Update if resource is available locally, ie. was used in the past.
|
||||
case utils.StringInSlice(reg.MandatoryUpdates, res.Identifier):
|
||||
// Update is set as mandatory.
|
||||
default:
|
||||
// Resource does not need to be updated.
|
||||
return
|
||||
}
|
||||
// // Skip resources we don't need.
|
||||
// switch {
|
||||
// case res.inUse():
|
||||
// // Update if resource is in use.
|
||||
// case res.available():
|
||||
// // Update if resource is available locally, ie. was used in the past.
|
||||
// case utils.StringInSlice(reg.MandatoryUpdates, res.Identifier):
|
||||
// // Update is set as mandatory.
|
||||
// default:
|
||||
// // Resource does not need to be updated.
|
||||
// return
|
||||
// }
|
||||
|
||||
// Go through all versions until we find versions that need updating.
|
||||
for _, rv := range res.Versions {
|
||||
switch {
|
||||
case !rv.CurrentRelease:
|
||||
// We are not interested in older releases.
|
||||
case !rv.Available:
|
||||
// File not available locally, download!
|
||||
toUpdate = append(toUpdate, rv)
|
||||
case !rv.SigAvailable && res.VerificationOptions != nil:
|
||||
// File signature is not available and verification is enabled, download signature!
|
||||
missingSigs = append(missingSigs, rv)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// // Go through all versions until we find versions that need updating.
|
||||
// for _, rv := range res.Versions {
|
||||
// switch {
|
||||
// case !rv.CurrentRelease:
|
||||
// // We are not interested in older releases.
|
||||
// case !rv.Available:
|
||||
// // File not available locally, download!
|
||||
// toUpdate = append(toUpdate, rv)
|
||||
// case !rv.SigAvailable && res.VerificationOptions != nil:
|
||||
// // File signature is not available and verification is enabled, download signature!
|
||||
// missingSigs = append(missingSigs, rv)
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
|
||||
slices.SortFunc(toUpdate, func(a, b *ResourceVersion) int {
|
||||
return strings.Compare(a.resource.Identifier, b.resource.Identifier)
|
||||
})
|
||||
slices.SortFunc(missingSigs, func(a, b *ResourceVersion) int {
|
||||
return strings.Compare(a.resource.Identifier, b.resource.Identifier)
|
||||
})
|
||||
// slices.SortFunc(toUpdate, func(a, b *ResourceVersion) int {
|
||||
// return strings.Compare(a.resource.Identifier, b.resource.Identifier)
|
||||
// })
|
||||
// slices.SortFunc(missingSigs, func(a, b *ResourceVersion) int {
|
||||
// return strings.Compare(a.resource.Identifier, b.resource.Identifier)
|
||||
// })
|
||||
|
||||
return toUpdate, missingSigs
|
||||
}
|
||||
// return toUpdate, missingSigs
|
||||
// }
|
||||
|
||||
func humanInfoFromResourceVersions(resourceVersions []*ResourceVersion) []string {
|
||||
identifiers := make([]string, len(resourceVersions))
|
||||
// func humanInfoFromResourceVersions(resourceVersions []*ResourceVersion) []string {
|
||||
// identifiers := make([]string, len(resourceVersions))
|
||||
|
||||
for i, rv := range resourceVersions {
|
||||
identifiers[i] = fmt.Sprintf("%s v%s", rv.resource.Identifier, rv.VersionNumber)
|
||||
}
|
||||
// for i, rv := range resourceVersions {
|
||||
// identifiers[i] = fmt.Sprintf("%s v%s", rv.resource.Identifier, rv.VersionNumber)
|
||||
// }
|
||||
|
||||
return identifiers
|
||||
}
|
||||
// return identifiers
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user