[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [sandboxed-tor-browser/master] Bug 20851: If the incremental update fails, fall back to the complete update.
commit 3b5281a4191efd5336328932fbe74e17e9eb7614
Author: Yawning Angel <yawning@xxxxxxxxxxxxxxx>
Date: Fri Dec 23 03:02:16 2016 +0000
Bug 20851: If the incremental update fails, fall back to the complete update.
---
ChangeLog | 2 +
.../internal/ui/config/config.go | 12 ++
.../sandboxed-tor-browser/internal/ui/launch.go | 6 +-
.../sandboxed-tor-browser/internal/ui/update.go | 226 +++++++++++++++------
4 files changed, 175 insertions(+), 71 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index c7d7e4a..5344f14 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,6 @@
Changes in version 0.0.3 - UNRELEASED:
+ * Bug 20851: If the incremental update fails, fall back to the complete
+ update.
* Bug 21055: Fall back gracefully if the Adwaita theme is not present.
* Bug 20791: Fetch install/update metadata using onions.
* Bug 20979: runtime/cgo: pthread_create failed: Resource temporarily
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go b/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
index a1af1cd..9ffbd5c 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
@@ -302,6 +302,9 @@ type Config struct {
// ForceUpdate is set if the installed bundle is known to be obsolete.
ForceUpdate bool `json:"forceUpdate"`
+ // SkipPartialUpdate is set if the partial update has failed to apply.
+ SkipPartialUpdate bool `json:"skipPartialUpdate"`
+
// Tor is the Tor network configuration.
Tor Tor `json:"tor,omitEmpty"`
@@ -381,6 +384,15 @@ func (cfg *Config) SetForceUpdate(b bool) {
}
}
+// SetSkipPartailUpdate sets the bundle as needing a complete update as opposed
+// to a partial one, and marks the config dirty.
+func (cfg *Config) SetSkipPartialUpdate(b bool) {
+ if cfg.SkipPartialUpdate != b {
+ cfg.SkipPartialUpdate = b
+ cfg.isDirty = true
+ }
+}
+
// Sanitize validates the config, and brings it inline with reality.
func (cfg *Config) Sanitize() {
if !utils.DirExists(cfg.Sandbox.DownloadsDir) {
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/launch.go b/src/cmd/sandboxed-tor-browser/internal/ui/launch.go
index 6a86dba..49b7663 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/launch.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/launch.go
@@ -63,15 +63,13 @@ func (c *Common) DoLaunch(async *Async, checkUpdates bool) {
// Start tor if required.
log.Printf("launch: Connecting to the Tor network.")
async.UpdateProgress("Connecting to the Tor network.")
- dialFn, err := c.launchTor(async, false)
- if err != nil {
- async.Err = err
+ if _, async.Err = c.launchTor(async, false); async.Err != nil {
return
}
// If an update check is needed, check for updates.
if checkUpdates {
- c.doUpdate(async, dialFn)
+ c.doUpdate(async)
if async.Err != nil {
return
}
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/update.go b/src/cmd/sandboxed-tor-browser/internal/ui/update.go
index 7c5f812..1994e64 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/update.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/update.go
@@ -25,16 +25,29 @@ import (
"cmd/sandboxed-tor-browser/internal/installer"
"cmd/sandboxed-tor-browser/internal/sandbox"
+ "cmd/sandboxed-tor-browser/internal/tor"
. "cmd/sandboxed-tor-browser/internal/ui/async"
)
-func (c *Common) CheckUpdate(async *Async, dialFn dialFunc) *installer.UpdateEntry {
+// CheckUpdate queries the update server to see if an update for the current
+// bundle is available.
+func (c *Common) CheckUpdate(async *Async) *installer.UpdateEntry {
// Check for updates.
log.Printf("update: Checking for updates.")
async.UpdateProgress("Checking for updates.")
// Create the async HTTP client.
- client := newHPKPGrabClient(dialFn)
+ if c.tor == nil {
+ async.Err = tor.ErrTorNotRunning
+ return nil
+ }
+ dialer, err := c.tor.Dialer()
+ if err != nil {
+ async.Err = err
+ return nil
+ }
+
+ client := newHPKPGrabClient(dialer.Dial)
// Determine where the update metadata should be fetched from.
updateURLs := []string{}
@@ -57,7 +70,9 @@ func (c *Common) CheckUpdate(async *Async, dialFn dialFunc) *installer.UpdateEnt
for _, url := range updateURLs {
log.Printf("update: Metadata URL: %v", url)
async.Err = nil // Clear errors per fetch.
- if b := async.Grab(client, url, nil); async.Err != nil {
+ if b := async.Grab(client, url, nil); async.Err == ErrCanceled {
+ return nil
+ } else if async.Err != nil {
log.Printf("update: Metadata download failed: %v", async.Err)
continue
} else if update, async.Err = installer.GetUpdateEntry(b); async.Err != nil {
@@ -69,13 +84,14 @@ func (c *Common) CheckUpdate(async *Async, dialFn dialFunc) *installer.UpdateEnt
}
if !fetchOk {
- // This should be set from the last update attempt...
- if async.Err == nil {
- async.Err = fmt.Errorf("failed to download update metadata")
- }
+ // The last update attempt likely isn't the only relevant error,
+ // just set this to something that won't terrify users, more detailed
+ // diagnostics are avaialble in the log.
+ async.Err = fmt.Errorf("failed to download update metadata")
return nil
}
+ // If there is an update, tag the installed bundle as stale...
if update == nil {
log.Printf("update: Installed bundle is current.")
c.Cfg.SetForceUpdate(false)
@@ -84,6 +100,7 @@ func (c *Common) CheckUpdate(async *Async, dialFn dialFunc) *installer.UpdateEnt
c.Cfg.SetForceUpdate(true)
}
+ // ... and flush the config.
if async.Err = c.Cfg.Sync(); async.Err != nil {
return nil
}
@@ -91,38 +108,26 @@ func (c *Common) CheckUpdate(async *Async, dialFn dialFunc) *installer.UpdateEnt
return update
}
-func (c *Common) doUpdate(async *Async, dialFn dialFunc) {
- // This attempts to follow the process that Firefox uses to check for
- // updates. https://wiki.mozilla.org/Software_Update:Checking_For_Updates
+// FetchUpdate downloads the update specified by the patch over tor, and
+// validates it with the hash in the patch datastructure, and the known MAR
+// signing keys.
+func (c *Common) FetchUpdate(async *Async, patch *installer.Patch) []byte {
+ var dialFn dialFunc
- // Check for updates.
- update := c.CheckUpdate(async, dialFn)
- if async.Err != nil || update == nil {
- return
- }
-
- // Ensure that the update entry version is actually neweer.
- if !c.Manif.BundleUpdateVersionValid(update.AppVersion) {
- log.Printf("update: Update server provided a downgrade: '%v'", update.AppVersion)
- async.Err = fmt.Errorf("update server provided a downgrade: '%v'", update.AppVersion)
- return
- }
-
- // Figure out the best MAR to download.
- patches := make(map[string]*installer.Patch)
- for _, v := range update.Patch {
- if patches[v.Type] != nil {
- async.Err = fmt.Errorf("duplicate patch entry for kind: '%v'", v.Type)
- return
+ // Launch the tor daemon if needed.
+ if c.tor == nil {
+ dialFn, async.Err = c.launchTor(async, false)
+ if async.Err != nil {
+ return nil
}
- patches[v.Type] = &v
- }
- patch := patches["partial"] // Favor the delta update mechanism.
- if patch == nil {
- if patch = patches["complete"]; patch == nil {
- async.Err = fmt.Errorf("no suitable MAR file found")
- return
+ } else {
+ // Otherwise, retreive the dialer.
+ dialer, err := c.tor.Dialer()
+ if err != nil {
+ async.Err = err
+ return nil
}
+ dialFn = dialer.Dial
}
// Download the MAR file.
@@ -132,7 +137,7 @@ func (c *Common) doUpdate(async *Async, dialFn dialFunc) {
var mar []byte
client := newHPKPGrabClient(dialFn)
if mar = async.Grab(client, patch.Url, func(s string) { async.UpdateProgress(fmt.Sprintf("Downloading Tor Browser Update: %s", s)) }); async.Err != nil {
- return
+ return nil
}
log.Printf("update: Validating Tor Browser Update.")
@@ -142,64 +147,151 @@ func (c *Common) doUpdate(async *Async, dialFn dialFunc) {
expectedHash, err := hex.DecodeString(patch.HashValue)
if err != nil {
async.Err = fmt.Errorf("failed to decode HashValue: %v", err)
- return
+ return nil
}
switch patch.HashFunction {
case "SHA512":
derivedHash := sha512.Sum512(mar)
if !bytes.Equal(expectedHash, derivedHash[:]) {
async.Err = fmt.Errorf("downloaded hash does not match patch metadata")
- return
+ return nil
}
default:
async.Err = fmt.Errorf("unsupported hash function: '%v'", patch.HashFunction)
- return
+ return nil
}
// ... and verify the signature block in the MAR with our copy of the key.
if async.Err = installer.VerifyTorBrowserMAR(mar); async.Err != nil {
- return
+ return nil
}
- // Shutdown the old tor now.
- if c.tor != nil {
- log.Printf("update: Shutting down old tor.")
- c.tor.Shutdown()
- c.tor = nil
- }
+ return mar
+}
- // Apply the update.
- log.Printf("update: Updating Tor Browser.")
- async.UpdateProgress("Updating Tor Browser.")
+func (c *Common) doUpdate(async *Async) {
+ // This attempts to follow the process that Firefox uses to check for
+ // updates. https://wiki.mozilla.org/Software_Update:Checking_For_Updates
- async.ToUI <- false // Lock out canceling.
+ const (
+ patchPartial = "partial"
+ patchComplete = "complete"
+ )
- if async.Err = sandbox.RunUpdate(c.Cfg, mar); async.Err != nil {
+ // Check for updates.
+ update := c.CheckUpdate(async)
+ if async.Err != nil || update == nil {
+ // Something either broke, or the bundle is up to date. The caller
+ // needs to check async.Err, and either way there's nothing more that
+ // can be done.
return
}
- // Reinstall the autoconfig stuff.
- if async.Err = writeAutoconfig(c.Cfg); async.Err != nil {
+ // Ensure that the update entry version is actually neweer.
+ if !c.Manif.BundleUpdateVersionValid(update.AppVersion) {
+ log.Printf("update: Update server provided a downgrade: '%v'", update.AppVersion)
+ async.Err = fmt.Errorf("update server provided a downgrade: '%v'", update.AppVersion)
return
}
- // Update the maniftest and config.
- c.Manif.SetVersion(update.AppVersion)
- if async.Err = c.Manif.Sync(); async.Err != nil {
- return
+ // Figure out the best MAR to download.
+ patches := make(map[string]*installer.Patch)
+ for i := 0; i < len(update.Patch); i++ {
+ v := &update.Patch[i]
+ if patches[v.Type] != nil {
+ async.Err = fmt.Errorf("duplicate patch entry for kind: '%v'", v.Type)
+ return
+ }
+ patches[v.Type] = v
}
- c.Cfg.SetForceUpdate(false)
- if async.Err = c.Cfg.Sync(); async.Err != nil {
- return
+
+ patchTypes := []string{}
+ if !c.Cfg.SkipPartialUpdate {
+ patchTypes = append(patchTypes, patchPartial)
}
+ patchTypes = append(patchTypes, patchComplete)
+
+ // Cycle through the patch types, and apply the "best" one.
+ nrAttempts := 0
+ for _, patchType := range patchTypes {
+ async.Err = nil
+
+ patch := patches[patchType]
+ if patch == nil {
+ continue
+ }
+
+ nrAttempts++
+ mar := c.FetchUpdate(async, patch)
+ if async.Err == ErrCanceled {
+ return
+ } else if async.Err != nil {
+ log.Printf("update: Failed to fetch update: %v", async.Err)
+ continue
+ }
+ if mar == nil {
+ panic("update: no MAR returned from successful fetch")
+ }
+
+ // Shutdown the old tor now.
+ if c.tor != nil {
+ log.Printf("update: Shutting down old tor.")
+ c.tor.Shutdown()
+ c.tor = nil
+ }
+
+ // Apply the update.
+ log.Printf("update: Updating Tor Browser.")
+ async.UpdateProgress("Updating Tor Browser.")
+
+ async.ToUI <- false // Lock out canceling.
+
+ if async.Err = sandbox.RunUpdate(c.Cfg, mar); async.Err != nil {
+ log.Printf("update: Failed to apply update: %v", async.Err)
+ if patchType == patchPartial {
+ c.Cfg.SetSkipPartialUpdate(true)
+ if async.Err = c.Cfg.Sync(); async.Err != nil {
+ return
+ }
+ }
+ async.ToUI <- true // Unlock canceling.
+ continue
+ }
+
+ // Failues past this point are catastrophic in that, the on-disk
+ // bundle is up to date, but the post-update tasks have failed.
+
+ // Reinstall the autoconfig stuff.
+ if async.Err = writeAutoconfig(c.Cfg); async.Err != nil {
+ return
+ }
- async.ToUI <- true // Unlock canceling.
+ // Update the maniftest and config.
+ c.Manif.SetVersion(update.AppVersion)
+ if async.Err = c.Manif.Sync(); async.Err != nil {
+ return
+ }
+ c.Cfg.SetForceUpdate(false)
+ c.Cfg.SetSkipPartialUpdate(false)
+ if async.Err = c.Cfg.Sync(); async.Err != nil {
+ return
+ }
+
+ async.ToUI <- true // Unlock canceling.
+
+ // Restart tor if we launched it.
+ if !c.Cfg.UseSystemTor {
+ log.Printf("launch: Reconnecting to the Tor network.")
+ async.UpdateProgress("Reconnecting to the Tor network.")
+ _, async.Err = c.launchTor(async, false)
+ }
+ return
+ }
- // Restart tor if we launched it.
- if !c.Cfg.UseSystemTor {
- log.Printf("launch: Reconnecting to the Tor network.")
- async.UpdateProgress("Reconnecting to the Tor network.")
- _, async.Err = c.launchTor(async, false)
+ if nrAttempts == 0 {
+ async.Err = fmt.Errorf("no suitable MAR file found")
+ } else if async.Err != ErrCanceled {
+ async.Err = fmt.Errorf("failed to apply all possible MAR files")
}
return
}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits