vendor inconshreveable/go-update and it's deps
Henry committed
Oct 18, 2014 at 13:31 UTC
deb9aebc482969555143f42964170406d3fd20e6
33 files changed
+2676
Godeps/Godeps.json
+13
@@ -9,6 +9,11 @@
9
"ImportPath": "bazil.org/fuse",
10
"Rev": "a04507d54fc3610d38ee951402d8c4acab56c7b1"
11
},
12
+ {
13
+ "ImportPath": "bitbucket.org/kardianos/osext",
14
+ "Comment": "null-15",
15
+ "Rev": "44140c5fc69ecf1102c5ef451d73cd98ef59b178"
16
+ },
17
{
18
"ImportPath": "code.google.com/p/go-uuid/uuid",
19
"Comment": "null-12",
@@ -62,6 +67,10 @@
67
"ImportPath": "github.com/gorilla/mux",
68
"Rev": "4b8fbc56f3b2400a7c7ea3dba9b3539787c486b6"
69
},
70
+ {
71
+ "ImportPath": "github.com/inconshreveable/go-update",
72
+ "Rev": "221d034a558b4c21b0624b2a450c076913854a57"
73
+ },
74
{
75
"ImportPath": "github.com/jbenet/commander",
76
"Rev": "e0cf317891f0ab6f1ac64dfcb754b4fb5e69f7df"
@@ -92,6 +101,10 @@
101
"Comment": "0.1.0-5-g1976046",
102
"Rev": "1976046c2b0db0b668791b3e541d76a38b7c1af7"
103
},
104
+ {
105
+ "ImportPath": "github.com/kr/binarydist",
106
+ "Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd"
107
+ },
108
{
109
"ImportPath": "github.com/mitchellh/go-homedir",
110
"Rev": "7d2d8c8a4e078ce3c58736ab521a40b37a504c52"
Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE
new
+27
@@ -0,0 +1,27 @@
1
+Copyright (c) 2012 The Go Authors. All rights reserved.
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions are
5
+met:
6
+
7
+ * Redistributions of source code must retain the above copyright
8
+notice, this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above
10
+copyright notice, this list of conditions and the following disclaimer
11
+in the documentation and/or other materials provided with the
12
+distribution.
13
+ * Neither the name of Google Inc. nor the names of its
14
+contributors may be used to endorse or promote products derived from
15
+this software without specific prior written permission.
16
+
17
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go
new
+32
@@ -0,0 +1,32 @@
1
+// Copyright 2012 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// Extensions to the standard "os" package.
6
+package osext
7
+
8
+import "path/filepath"
9
+
10
+// Executable returns an absolute path that can be used to
11
+// re-invoke the current program.
12
+// It may not be valid after the current program exits.
13
+func Executable() (string, error) {
14
+ p, err := executable()
15
+ return filepath.Clean(p), err
16
+}
17
+
18
+// Returns same path as Executable, returns just the folder
19
+// path. Excludes the executable name.
20
+func ExecutableFolder() (string, error) {
21
+ p, err := Executable()
22
+ if err != nil {
23
+ return "", err
24
+ }
25
+ folder, _ := filepath.Split(p)
26
+ return folder, nil
27
+}
28
+
29
+// Depricated. Same as Executable().
30
+func GetExePath() (exePath string, err error) {
31
+ return Executable()
32
+}
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go
new
+20
@@ -0,0 +1,20 @@
1
+// Copyright 2012 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package osext
6
+
7
+import (
8
+ "os"
9
+ "strconv"
10
+ "syscall"
11
+)
12
+
13
+func executable() (string, error) {
14
+ f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
15
+ if err != nil {
16
+ return "", err
17
+ }
18
+ defer f.Close()
19
+ return syscall.Fd2path(int(f.Fd()))
20
+}
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go
new
+28
@@ -0,0 +1,28 @@
1
+// Copyright 2012 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// +build linux netbsd openbsd solaris
6
+
7
+package osext
8
+
9
+import (
10
+ "errors"
11
+ "fmt"
12
+ "os"
13
+ "runtime"
14
+)
15
+
16
+func executable() (string, error) {
17
+ switch runtime.GOOS {
18
+ case "linux":
19
+ return os.Readlink("/proc/self/exe")
20
+ case "netbsd":
21
+ return os.Readlink("/proc/curproc/exe")
22
+ case "openbsd":
23
+ return os.Readlink("/proc/curproc/file")
24
+ case "solaris":
25
+ return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
26
+ }
27
+ return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
28
+}
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go
new
+79
@@ -0,0 +1,79 @@
1
+// Copyright 2012 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// +build darwin freebsd
6
+
7
+package osext
8
+
9
+import (
10
+ "os"
11
+ "path/filepath"
12
+ "runtime"
13
+ "syscall"
14
+ "unsafe"
15
+)
16
+
17
+var initCwd, initCwdErr = os.Getwd()
18
+
19
+func executable() (string, error) {
20
+ var mib [4]int32
21
+ switch runtime.GOOS {
22
+ case "freebsd":
23
+ mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
24
+ case "darwin":
25
+ mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
26
+ }
27
+
28
+ n := uintptr(0)
29
+ // Get length.
30
+ _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
31
+ if errNum != 0 {
32
+ return "", errNum
33
+ }
34
+ if n == 0 { // This shouldn't happen.
35
+ return "", nil
36
+ }
37
+ buf := make([]byte, n)
38
+ _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
39
+ if errNum != 0 {
40
+ return "", errNum
41
+ }
42
+ if n == 0 { // This shouldn't happen.
43
+ return "", nil
44
+ }
45
+ for i, v := range buf {
46
+ if v == 0 {
47
+ buf = buf[:i]
48
+ break
49
+ }
50
+ }
51
+ var err error
52
+ execPath := string(buf)
53
+ // execPath will not be empty due to above checks.
54
+ // Try to get the absolute path if the execPath is not rooted.
55
+ if execPath[0] != '/' {
56
+ execPath, err = getAbs(execPath)
57
+ if err != nil {
58
+ return execPath, err
59
+ }
60
+ }
61
+ // For darwin KERN_PROCARGS may return the path to a symlink rather than the
62
+ // actual executable.
63
+ if runtime.GOOS == "darwin" {
64
+ if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
65
+ return execPath, err
66
+ }
67
+ }
68
+ return execPath, nil
69
+}
70
+
71
+func getAbs(execPath string) (string, error) {
72
+ if initCwdErr != nil {
73
+ return execPath, initCwdErr
74
+ }
75
+ // The execPath may begin with a "../" or a "./" so clean it first.
76
+ // Join the two paths, trailing and starting slashes undetermined, so use
77
+ // the generic Join function.
78
+ return filepath.Join(initCwd, filepath.Clean(execPath)), nil
79
+}
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go
new
+79
@@ -0,0 +1,79 @@
1
+// Copyright 2012 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// +build darwin linux freebsd netbsd windows
6
+
7
+package osext
8
+
9
+import (
10
+ "fmt"
11
+ "os"
12
+ oexec "os/exec"
13
+ "path/filepath"
14
+ "runtime"
15
+ "testing"
16
+)
17
+
18
+const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
19
+
20
+func TestExecPath(t *testing.T) {
21
+ ep, err := Executable()
22
+ if err != nil {
23
+ t.Fatalf("ExecPath failed: %v", err)
24
+ }
25
+ // we want fn to be of the form "dir/prog"
26
+ dir := filepath.Dir(filepath.Dir(ep))
27
+ fn, err := filepath.Rel(dir, ep)
28
+ if err != nil {
29
+ t.Fatalf("filepath.Rel: %v", err)
30
+ }
31
+ cmd := &oexec.Cmd{}
32
+ // make child start with a relative program path
33
+ cmd.Dir = dir
34
+ cmd.Path = fn
35
+ // forge argv[0] for child, so that we can verify we could correctly
36
+ // get real path of the executable without influenced by argv[0].
37
+ cmd.Args = []string{"-", "-test.run=XXXX"}
38
+ cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
39
+ out, err := cmd.CombinedOutput()
40
+ if err != nil {
41
+ t.Fatalf("exec(self) failed: %v", err)
42
+ }
43
+ outs := string(out)
44
+ if !filepath.IsAbs(outs) {
45
+ t.Fatalf("Child returned %q, want an absolute path", out)
46
+ }
47
+ if !sameFile(outs, ep) {
48
+ t.Fatalf("Child returned %q, not the same file as %q", out, ep)
49
+ }
50
+}
51
+
52
+func sameFile(fn1, fn2 string) bool {
53
+ fi1, err := os.Stat(fn1)
54
+ if err != nil {
55
+ return false
56
+ }
57
+ fi2, err := os.Stat(fn2)
58
+ if err != nil {
59
+ return false
60
+ }
61
+ return os.SameFile(fi1, fi2)
62
+}
63
+
64
+func init() {
65
+ if e := os.Getenv(execPath_EnvVar); e != "" {
66
+ // first chdir to another path
67
+ dir := "/"
68
+ if runtime.GOOS == "windows" {
69
+ dir = filepath.VolumeName(".")
70
+ }
71
+ os.Chdir(dir)
72
+ if ep, err := Executable(); err != nil {
73
+ fmt.Fprint(os.Stderr, "ERROR: ", err)
74
+ } else {
75
+ fmt.Fprint(os.Stderr, ep)
76
+ }
77
+ os.Exit(0)
78
+ }
79
+}
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go
new
+34
@@ -0,0 +1,34 @@
1
+// Copyright 2012 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package osext
6
+
7
+import (
8
+ "syscall"
9
+ "unicode/utf16"
10
+ "unsafe"
11
+)
12
+
13
+var (
14
+ kernel = syscall.MustLoadDLL("kernel32.dll")
15
+ getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
16
+)
17
+
18
+// GetModuleFileName() with hModule = NULL
19
+func executable() (exePath string, err error) {
20
+ return getModuleFileName()
21
+}
22
+
23
+func getModuleFileName() (string, error) {
24
+ var n uint32
25
+ b := make([]uint16, syscall.MAX_PATH)
26
+ size := uint32(len(b))
27
+
28
+ r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
29
+ n = uint32(r0)
30
+ if n == 0 {
31
+ return "", e1
32
+ }
33
+ return string(utf16.Decode(b[0:n])), nil
34
+}
Godeps/_workspace/src/github.com/inconshreveable/go-update/LICENSE
new
+13
@@ -0,0 +1,13 @@
1
+Copyright 2014 Alan Shreve
2
+
3
+Licensed under the Apache License, Version 2.0 (the "License");
4
+you may not use this file except in compliance with the License.
5
+You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+Unless required by applicable law or agreed to in writing, software
10
+distributed under the License is distributed on an "AS IS" BASIS,
11
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+See the License for the specific language governing permissions and
13
+limitations under the License.
Godeps/_workspace/src/github.com/inconshreveable/go-update/README.md
new
+37
@@ -0,0 +1,37 @@
1
+# go-update: Automatically update Go programs from the internet
2
+
3
+go-update allows a program to update itself by replacing its executable file
4
+with a new version. It provides the flexibility to implement different updating user experiences
5
+like auto-updating, or manual user-initiated updates. It also boasts
6
+advanced features like binary patching and code signing verification.
7
+
8
+Updating your program to a new version is as easy as:
9
+
10
+ err, errRecover := update.New().FromUrl("http://release.example.com/2.0/myprogram")
11
+ if err != nil {
12
+ fmt.Printf("Update failed: %v\n", err)
13
+ }
14
+
15
+## Documentation and API Reference
16
+
17
+Comprehensive API documentation and code examples are available in the code documentation available on godoc.org:
18
+
19
+[](https://godoc.org/github.com/inconshreveable/go-update)
20
+
21
+## Features
22
+
23
+- Cross platform support (Windows too!)
24
+- Binary patch application
25
+- Checksum verification
26
+- Code signing verification
27
+- Support for updating arbitrary files
28
+
29
+## [equinox.io](https://equinox.io)
30
+go-update provides the primitives for building self-updating applications, but there a number of other challenges
31
+involved in a complete updating solution such as hosting, code signing, update channels, gradual rollout,
32
+dynamically computing binary patches, tracking update metrics like versions and failures, plus more.
33
+
34
+I provide this service, a complete solution, free for open source projects, at [equinox.io](https://equinox.io).
35
+
36
+## License
37
+Apache
Godeps/_workspace/src/github.com/inconshreveable/go-update/check/check.go
new
+209
@@ -0,0 +1,209 @@
1
+package check
2
+
3
+import (
4
+ "bytes"
5
+ _ "crypto/sha512" // for tls cipher support
6
+ "encoding/hex"
7
+ "encoding/json"
8
+ "fmt"
9
+ "io/ioutil"
10
+ "net/http"
11
+ "runtime"
12
+
13
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bitbucket.org/kardianos/osext"
14
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/inconshreveable/go-update"
15
+)
16
+
17
+type Initiative string
18
+
19
+const (
20
+ INITIATIVE_NEVER Initiative = "never"
21
+ INITIATIVE_AUTO = "auto"
22
+ INITIATIVE_MANUAL = "manual"
23
+)
24
+
25
+var NoUpdateAvailable error = fmt.Errorf("No update available")
26
+
27
+type Params struct {
28
+ // protocol version
29
+ Version int `json:"version"`
30
+ // identifier of the application to update
31
+ AppId string `json:"app_id"`
32
+ // version of the application updating itself
33
+ AppVersion string `json:"app_version"`
34
+ // operating system of target platform
35
+ OS string `json:"-"`
36
+ // hardware architecture of target platform
37
+ Arch string `json:"-"`
38
+ // application-level user identifier
39
+ UserId string `json:"user_id"`
40
+ // checksum of the binary to replace (used for returning diff patches)
41
+ Checksum string `json:"checksum"`
42
+ // release channel (empty string means 'stable')
43
+ Channel string `json:"-"`
44
+ // tags for custom update channels
45
+ Tags map[string]string `json:"tags"`
46
+}
47
+
48
+type Result struct {
49
+ up *update.Update
50
+
51
+ // should the update be applied automatically/manually
52
+ Initiative Initiative `json:"initiative"`
53
+ // url where to download the updated application
54
+ Url string `json:"url"`
55
+ // a URL to a patch to apply
56
+ PatchUrl string `json:"patch_url"`
57
+ // the patch format (only bsdiff supported at the moment)
58
+ PatchType update.PatchType `json:"patch_type"`
59
+ // version of the new application
60
+ Version string `json:"version"`
61
+ // expected checksum of the new application
62
+ Checksum string `json:"checksum"`
63
+ // signature for verifying update authenticity
64
+ Signature string `json:"signature"`
65
+}
66
+
67
+// CheckForUpdate makes an HTTP post to a URL with the JSON serialized
68
+// representation of Params. It returns the deserialized result object
69
+// returned by the remote endpoint or an error. If you do not set
70
+// OS/Arch, CheckForUpdate will populate them for you. Similarly, if
71
+// Version is 0, it will be set to 1. Lastly, if Checksum is the empty
72
+// string, it will be automatically be computed for the running program's
73
+// executable file.
74
+func (p *Params) CheckForUpdate(url string, up *update.Update) (*Result, error) {
75
+ if p.Tags == nil {
76
+ p.Tags = make(map[string]string)
77
+ }
78
+
79
+ if p.Channel == "" {
80
+ p.Channel = "stable"
81
+ }
82
+
83
+ if p.OS == "" {
84
+ p.OS = runtime.GOOS
85
+ }
86
+
87
+ if p.Arch == "" {
88
+ p.Arch = runtime.GOARCH
89
+ }
90
+
91
+ if p.Version == 0 {
92
+ p.Version = 1
93
+ }
94
+
95
+ // ignore errors auto-populating the checksum
96
+ // if it fails, you just won't be able to patch
97
+ if up.TargetPath == "" {
98
+ p.Checksum = defaultChecksum()
99
+ } else {
100
+ checksum, err := update.ChecksumForFile(up.TargetPath)
101
+ if err != nil {
102
+ return nil, err
103
+ }
104
+ p.Checksum = hex.EncodeToString(checksum)
105
+ }
106
+
107
+ p.Tags["os"] = p.OS
108
+ p.Tags["arch"] = p.Arch
109
+ p.Tags["channel"] = p.Channel
110
+
111
+ body, err := json.Marshal(p)
112
+ if err != nil {
113
+ return nil, err
114
+ }
115
+
116
+ resp, err := http.Post(url, "application/json", bytes.NewReader(body))
117
+ if err != nil {
118
+ return nil, err
119
+ }
120
+
121
+ // no content means no available update
122
+ if resp.StatusCode == 204 {
123
+ return nil, NoUpdateAvailable
124
+ }
125
+
126
+ defer resp.Body.Close()
127
+ respBytes, err := ioutil.ReadAll(resp.Body)
128
+ if err != nil {
129
+ return nil, err
130
+ }
131
+
132
+ result := &Result{up: up}
133
+ if err := json.Unmarshal(respBytes, result); err != nil {
134
+ return nil, err
135
+ }
136
+
137
+ return result, nil
138
+}
139
+
140
+func (p *Params) CheckAndApplyUpdate(url string, up *update.Update) (result *Result, err error, errRecover error) {
141
+ // check for an update
142
+ result, err = p.CheckForUpdate(url, up)
143
+ if err != nil {
144
+ return
145
+ }
146
+
147
+ // run the available update
148
+ err, errRecover = result.Update()
149
+ return
150
+}
151
+
152
+func (r *Result) Update() (err error, errRecover error) {
153
+ if r.Checksum != "" {
154
+ r.up.Checksum, err = hex.DecodeString(r.Checksum)
155
+ if err != nil {
156
+ return
157
+ }
158
+ }
159
+
160
+ if r.Signature != "" {
161
+ r.up.Signature, err = hex.DecodeString(r.Signature)
162
+ if err != nil {
163
+ return
164
+ }
165
+ }
166
+
167
+ if r.PatchType != "" {
168
+ r.up.PatchType = r.PatchType
169
+ }
170
+
171
+ if r.Url == "" && r.PatchUrl == "" {
172
+ err = fmt.Errorf("Result does not contain an update url or patch update url")
173
+ return
174
+ }
175
+
176
+ if r.PatchUrl != "" {
177
+ err, errRecover = r.up.FromUrl(r.PatchUrl)
178
+ if err == nil {
179
+ // success!
180
+ return
181
+ } else {
182
+ // failed to update from patch URL, try with the whole thing
183
+ if r.Url == "" || errRecover != nil {
184
+ // we can't try updating from a URL with the full contents
185
+ // in these cases, so fail
186
+ return
187
+ } else {
188
+ r.up.PatchType = update.PATCHTYPE_NONE
189
+ }
190
+ }
191
+ }
192
+
193
+ // try updating from a URL with the full contents
194
+ return r.up.FromUrl(r.Url)
195
+}
196
+
197
+func defaultChecksum() string {
198
+ path, err := osext.Executable()
199
+ if err != nil {
200
+ return ""
201
+ }
202
+
203
+ checksum, err := update.ChecksumForFile(path)
204
+ if err != nil {
205
+ return ""
206
+ }
207
+
208
+ return hex.EncodeToString(checksum)
209
+}
Godeps/_workspace/src/github.com/inconshreveable/go-update/download/download.go
new
+230
@@ -0,0 +1,230 @@
1
+package download
2
+
3
+import (
4
+ "bytes"
5
+ "compress/gzip"
6
+ "fmt"
7
+ "io"
8
+ "net/http"
9
+ "os"
10
+ "runtime"
11
+)
12
+
13
+type roundTripper struct {
14
+ RoundTripFn func(*http.Request) (*http.Response, error)
15
+}
16
+
17
+func (rt *roundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
18
+ return rt.RoundTripFn(r)
19
+}
20
+
21
+// Download encapsulates the state and parameters to download content
22
+// from a URL which:
23
+//
24
+// - Publishes the percentage of the download completed to a channel.
25
+// - May resume a previous download that was partially completed.
26
+//
27
+// Create an instance with the New() factory function.
28
+type Download struct {
29
+ // net/http.Client to use when downloading the update.
30
+ // If nil, a default http.Client is used
31
+ HttpClient *http.Client
32
+
33
+ // As bytes are downloaded, they are written to Target.
34
+ // Download also uses the Target's Seek method to determine
35
+ // the size of partial-downloads so that it may properly
36
+ // request the remaining bytes to resume the download.
37
+ Target Target
38
+
39
+ // Progress returns the percentage of the download
40
+ // completed as an integer between 0 and 100
41
+ Progress chan (int)
42
+
43
+ // HTTP Method to use in the download request. Default is "GET"
44
+ Method string
45
+
46
+ // HTTP URL to issue the download request to
47
+ Url string
48
+}
49
+
50
+// New initializes a new Download object which will download
51
+// the content from url into target.
52
+func New(url string, target Target) *Download {
53
+ return &Download{
54
+ HttpClient: new(http.Client),
55
+ Progress: make(chan int),
56
+ Method: "GET",
57
+ Url: url,
58
+ Target: target,
59
+ }
60
+}
61
+
62
+// Get() downloads the content of a url to a target destination.
63
+//
64
+// Only HTTP/1.1 servers that implement the Range header support resuming a
65
+// partially completed download.
66
+//
67
+// On success, the server must return 200 and the content, or 206 when resuming a partial download.
68
+// If the HTTP server returns a 3XX redirect, it will be followed according to d.HttpClient's redirect policy.
69
+//
70
+func (d *Download) Get() (err error) {
71
+ // Close the progress channel whenever this function completes
72
+ defer close(d.Progress)
73
+
74
+ // determine the size of the download target to determine if we're resuming a partial download
75
+ offset, err := d.Target.Size()
76
+ if err != nil {
77
+ return
78
+ }
79
+
80
+ // create the download request
81
+ req, err := http.NewRequest(d.Method, d.Url, nil)
82
+ if err != nil {
83
+ return
84
+ }
85
+
86
+ // we have to add headers like this so they get used across redirects
87
+ trans := d.HttpClient.Transport
88
+ if trans == nil {
89
+ trans = http.DefaultTransport
90
+ }
91
+
92
+ d.HttpClient.Transport = &roundTripper{
93
+ RoundTripFn: func(r *http.Request) (*http.Response, error) {
94
+ // add header for download continuation
95
+ if offset > 0 {
96
+ r.Header.Add("Range", fmt.Sprintf("%d-", offset))
97
+ }
98
+
99
+ // ask for gzipped content so that net/http won't unzip it for us
100
+ // and destroy the content length header we need for progress calculations
101
+ r.Header.Add("Accept-Encoding", "gzip")
102
+
103
+ return trans.RoundTrip(r)
104
+ },
105
+ }
106
+
107
+ // issue the download request
108
+ resp, err := d.HttpClient.Do(req)
109
+ if err != nil {
110
+ return
111
+ }
112
+ defer resp.Body.Close()
113
+
114
+ switch resp.StatusCode {
115
+ // ok
116
+ case 200, 206:
117
+
118
+ // server error
119
+ default:
120
+ err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status)
121
+ return
122
+ }
123
+
124
+ // Determine how much we have to download
125
+ // net/http sets this to -1 when it is unknown
126
+ clength := resp.ContentLength
127
+
128
+ // Read the content from the response body
129
+ rd := resp.Body
130
+
131
+ // meter the rate at which we download content for
132
+ // progress reporting if we know how much to expect
133
+ if clength > 0 {
134
+ rd = &meteredReader{rd: rd, totalSize: clength, progress: d.Progress}
135
+ }
136
+
137
+ // Decompress the content if necessary
138
+ if resp.Header.Get("Content-Encoding") == "gzip" {
139
+ rd, err = gzip.NewReader(rd)
140
+ if err != nil {
141
+ return
142
+ }
143
+ }
144
+
145
+ // Download the update
146
+ _, err = io.Copy(d.Target, rd)
147
+ if err != nil {
148
+ return
149
+ }
150
+
151
+ return
152
+}
153
+
154
+// meteredReader wraps a ReadCloser. Calls to a meteredReader's Read() method
155
+// publish updates to a progress channel with the percentage read so far.
156
+type meteredReader struct {
157
+ rd io.ReadCloser
158
+ totalSize int64
159
+ progress chan int
160
+ totalRead int64
161
+ ticks int64
162
+}
163
+
164
+func (m *meteredReader) Close() error {
165
+ return m.rd.Close()
166
+}
167
+
168
+func (m *meteredReader) Read(b []byte) (n int, err error) {
169
+ chunkSize := (m.totalSize / 100) + 1
170
+ lenB := int64(len(b))
171
+
172
+ var nChunk int
173
+ for start := int64(0); start < lenB; start += int64(nChunk) {
174
+ end := start + chunkSize
175
+ if end > lenB {
176
+ end = lenB
177
+ }
178
+
179
+ nChunk, err = m.rd.Read(b[start:end])
180
+
181
+ n += nChunk
182
+ m.totalRead += int64(nChunk)
183
+
184
+ if m.totalRead > (m.ticks * chunkSize) {
185
+ m.ticks += 1
186
+ // try to send on channel, but don't block if it's full
187
+ select {
188
+ case m.progress <- int(m.ticks + 1):
189
+ default:
190
+ }
191
+
192
+ // give the progress channel consumer a chance to run
193
+ runtime.Gosched()
194
+ }
195
+
196
+ if err != nil {
197
+ return
198
+ }
199
+ }
200
+
201
+ return
202
+}
203
+
204
+// A Target is what you can supply to Download,
205
+// it's just an io.Writer with a Size() method so that
206
+// the a Download can "resume" an interrupted download
207
+type Target interface {
208
+ io.Writer
209
+ Size() (int, error)
210
+}
211
+
212
+type FileTarget struct {
213
+ *os.File
214
+}
215
+
216
+func (t *FileTarget) Size() (int, error) {
217
+ if fi, err := t.File.Stat(); err != nil {
218
+ return 0, err
219
+ } else {
220
+ return int(fi.Size()), nil
221
+ }
222
+}
223
+
224
+type MemoryTarget struct {
225
+ bytes.Buffer
226
+}
227
+
228
+func (t *MemoryTarget) Size() (int, error) {
229
+ return t.Buffer.Len(), nil
230
+}
Godeps/_workspace/src/github.com/inconshreveable/go-update/hide_noop.go
new
+7
@@ -0,0 +1,7 @@
1
+// +build !windows
2
+
3
+package update
4
+
5
+func hideFile(path string) error {
6
+ return nil
7
+}
Godeps/_workspace/src/github.com/inconshreveable/go-update/hide_windows.go
new
+19
@@ -0,0 +1,19 @@
1
+package update
2
+
3
+import (
4
+ "syscall"
5
+ "unsafe"
6
+)
7
+
8
+func hideFile(path string) error {
9
+ kernel32 := syscall.NewLazyDLL("kernel32.dll")
10
+ setFileAttributes := kernel32.NewProc("SetFileAttributesW")
11
+
12
+ r1, _, err := setFileAttributes.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), 2)
13
+
14
+ if r1 == 0 {
15
+ return err
16
+ } else {
17
+ return nil
18
+ }
19
+}
Godeps/_workspace/src/github.com/inconshreveable/go-update/update.go
new
+486
@@ -0,0 +1,486 @@
1
+/*
2
+go-update allows a program to update itself by replacing its executable file
3
+with a new version. It provides the flexibility to implement different updating user experiences
4
+like auto-updating, or manual user-initiated updates. It also boasts
5
+advanced features like binary patching and code signing verification.
6
+
7
+Updating your program to a new version is as easy as:
8
+
9
+ err, errRecover := update.New().FromUrl("http://release.example.com/2.0/myprogram")
10
+ if err != nil {
11
+ fmt.Printf("Update failed: %v\n", err)
12
+ }
13
+
14
+You may also choose to update from other data sources such as a file or an io.Reader:
15
+
16
+ err, errRecover := update.New().FromFile("/path/to/update")
17
+
18
+Binary Diff Patching
19
+
20
+Binary diff updates are supported and easy to use:
21
+
22
+ up := update.New().ApplyPatch(update.PATCHTYPE_BSDIFF)
23
+ err, errRecover := up.FromUrl("http://release.example.com/2.0/mypatch")
24
+
25
+Checksum Verification
26
+
27
+You should also verify the checksum of new updates as well as verify
28
+the digital signature of an update. Note that even when you choose to apply
29
+a patch, the checksum is verified against the complete update after that patch
30
+has been applied.
31
+
32
+ up := update.New().ApplyPatch(update.PATCHTYPE_BSDIFF).VerifyChecksum(checksum)
33
+ err, errRecover := up.FromUrl("http://release.example.com/2.0/mypatch")
34
+
35
+Updating other files
36
+
37
+Updating arbitrary files is also supported. You may update files which are
38
+not the currently running program:
39
+
40
+ up := update.New().Target("/usr/local/bin/some-program")
41
+ err, errRecover := up.FromUrl("http://release.example.com/2.0/some-program")
42
+
43
+Code Signing
44
+
45
+Truly secure updates use code signing to verify that the update was issued by a trusted party.
46
+To do this, you'll need to generate a public/private key pair. You can do this with openssl,
47
+or the equinox.io client (https://equinox.io/client) can easily generate one for you:
48
+
49
+ # with equinox client
50
+ equinox genkey --private-key=private.pem --public-key=public.pem
51
+
52
+ # with openssl
53
+ openssl genrsa -out private.pem 2048
54
+ openssl rsa -in private.pem -out public.pem -pubout
55
+
56
+Once you have your key pair, you can instruct your program to validate its updates
57
+with the public key:
58
+
59
+ const publicKey = `-----BEGIN PUBLIC KEY-----
60
+ ...
61
+ -----END PUBLIC KEY-----`
62
+
63
+ up, err := update.New().VerifySignatureWithPEM(publicKey)
64
+ if err != nil {
65
+ return fmt.Errorf("Bad public key: '%v': %v", publicKey, err)
66
+ }
67
+
68
+Once you've configured your program this way, it will disallow all updates unless they
69
+are properly signed. You must now pass in the signature to verify with:
70
+
71
+ up.VerifySignature(signature).FromUrl("http://dl.example.com/update")
72
+
73
+Error Handling and Recovery
74
+
75
+To perform an update, the process must be able to read its executable file and to write
76
+to the directory that contains its executable file. It can be useful to check whether the process
77
+has the necessary permissions to perform an update before trying to apply one. Use the
78
+CanUpdate call to provide a useful message to the user if the update can't proceed without
79
+elevated permissions:
80
+
81
+ up := update.New().Target("/etc/hosts")
82
+ err := up.CanUpdate()
83
+ if err != nil {
84
+ fmt.Printf("Can't update because: '%v'. Try as root or Administrator\n", err)
85
+ return
86
+ }
87
+ err, errRecover := up.FromUrl("https://example.com/new/hosts")
88
+
89
+Although exceedingly unlikely, the update operation itself is not atomic and can fail
90
+in such a way that a user's computer is left in an inconsistent state. If that happens,
91
+go-update attempts to recover to leave the system in a good state. If the recovery step
92
+fails (even more unlikely), a second error, referred to as "errRecover" will be non-nil
93
+so that you may inform your users of the bad news. You should handle this case as shown
94
+here:
95
+
96
+ err, errRecover := up.FromUrl("https://example.com/update")
97
+ if err != nil {
98
+ fmt.Printf("Update failed: %v\n", err)
99
+ if errRecover != nil {
100
+ fmt.Printf("Failed to recover bad update: %v!\n", errRecover)
101
+ fmt.Printf("Program exectuable may be missing!\n")
102
+ }
103
+ }
104
+
105
+Subpackages
106
+
107
+Sub-package check contains the client functionality for a simple protocol for negotiating
108
+whether a new update is available, where it is, and the metadata needed for verifying it.
109
+
110
+Sub-package download contains functionality for downloading from an HTTP endpoint
111
+while outputting a progress meter and supports resuming partial downloads.
112
+*/
113
+package update
114
+
115
+import (
116
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bitbucket.org/kardianos/osext"
117
+ "bytes"
118
+ "crypto"
119
+ "crypto/rsa"
120
+ "crypto/sha256"
121
+ _ "crypto/sha512" // for tls cipher support
122
+ "crypto/x509"
123
+ "encoding/pem"
124
+ "fmt"
125
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/inconshreveable/go-update/download"
126
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/kr/binarydist"
127
+ "io"
128
+ "io/ioutil"
129
+ "os"
130
+ "path/filepath"
131
+)
132
+
133
+// The type of a binary patch, if any. Only bsdiff is supported
134
+type PatchType string
135
+
136
+const (
137
+ PATCHTYPE_BSDIFF PatchType = "bsdiff"
138
+ PATCHTYPE_NONE = ""
139
+)
140
+
141
+type Update struct {
142
+ // empty string means "path of the current executable"
143
+ TargetPath string
144
+
145
+ // type of patch to apply. PATCHTYPE_NONE means "not a patch"
146
+ PatchType
147
+
148
+ // sha256 checksum of the new binary to verify against
149
+ Checksum []byte
150
+
151
+ // public key to use for signature verification
152
+ PublicKey *rsa.PublicKey
153
+
154
+ // signature to use for signature verification
155
+ Signature []byte
156
+}
157
+
158
+func (u *Update) getPath() (string, error) {
159
+ if u.TargetPath == "" {
160
+ return osext.Executable()
161
+ } else {
162
+ return u.TargetPath, nil
163
+ }
164
+}
165
+
166
+// New creates a new Update object.
167
+// A default update object assumes the complete binary
168
+// content will be used for update (not a patch) and that
169
+// the intended target is the running executable.
170
+//
171
+// Use this as the start of a chain of calls on the Update
172
+// object to build up your configuration. Example:
173
+//
174
+// up := update.New().ApplyPatch(update.PATCHTYPE_BSDIFF).VerifyChecksum(checksum)
175
+//
176
+func New() *Update {
177
+ return &Update{
178
+ TargetPath: "",
179
+ PatchType: PATCHTYPE_NONE,
180
+ }
181
+}
182
+
183
+// Target configures the update to update the file at the given path.
184
+// The emptry string means 'the executable file of the running program'.
185
+func (u *Update) Target(path string) *Update {
186
+ u.TargetPath = path
187
+ return u
188
+}
189
+
190
+// ApplyPatch configures the update to treat the contents of the update
191
+// as a patch to apply to the existing to target. You must specify the
192
+// format of the patch. Only PATCHTYPE_BSDIFF is supported at the moment.
193
+func (u *Update) ApplyPatch(patchType PatchType) *Update {
194
+ u.PatchType = patchType
195
+ return u
196
+}
197
+
198
+// VerifyChecksum configures the update to verify that the
199
+// the update has the given sha256 checksum.
200
+func (u *Update) VerifyChecksum(checksum []byte) *Update {
201
+ u.Checksum = checksum
202
+ return u
203
+}
204
+
205
+// VerifySignature configures the update to verify the given
206
+// signature of the update. You must also call one of the
207
+// VerifySignatureWith* functions to specify a public key
208
+// to use for verification.
209
+func (u *Update) VerifySignature(signature []byte) *Update {
210
+ u.Signature = signature
211
+ return u
212
+}
213
+
214
+// VerifySignatureWith configures the update to use the given RSA
215
+// public key to verify the update's signature. You must also call
216
+// VerifySignature() with a signature to check.
217
+//
218
+// You'll probably want to use VerifySignatureWithPEM instead of
219
+// parsing the public key yourself.
220
+func (u *Update) VerifySignatureWith(publicKey *rsa.PublicKey) *Update {
221
+ u.PublicKey = publicKey
222
+ return u
223
+}
224
+
225
+// VerifySignatureWithPEM configures the update to use the given PEM-formatted
226
+// RSA public key to verify the update's signature. You must also call
227
+// VerifySignature() with a signature to check.
228
+//
229
+// A PEM formatted public key typically begins with
230
+// -----BEGIN PUBLIC KEY-----
231
+func (u *Update) VerifySignatureWithPEM(publicKeyPEM []byte) (*Update, error) {
232
+ block, _ := pem.Decode(publicKeyPEM)
233
+ if block == nil {
234
+ return u, fmt.Errorf("Couldn't parse PEM data")
235
+ }
236
+
237
+ pub, err := x509.ParsePKIXPublicKey(block.Bytes)
238
+ if err != nil {
239
+ return u, err
240
+ }
241
+
242
+ var ok bool
243
+ u.PublicKey, ok = pub.(*rsa.PublicKey)
244
+ if !ok {
245
+ return u, fmt.Errorf("Public key isn't an RSA public key")
246
+ }
247
+
248
+ return u, nil
249
+}
250
+
251
+// FromUrl updates the target with the contents of the given URL.
252
+func (u *Update) FromUrl(url string) (err error, errRecover error) {
253
+ target := new(download.MemoryTarget)
254
+ err = download.New(url, target).Get()
255
+ if err != nil {
256
+ return
257
+ }
258
+
259
+ return u.FromStream(target)
260
+}
261
+
262
+// FromFile updates the target the contents of the given file.
263
+func (u *Update) FromFile(path string) (err error, errRecover error) {
264
+ // open the new updated contents
265
+ fp, err := os.Open(path)
266
+ if err != nil {
267
+ return
268
+ }
269
+ defer fp.Close()
270
+
271
+ // do the update
272
+ return u.FromStream(fp)
273
+}
274
+
275
+// FromStream updates the target file with the contents of the supplied io.Reader.
276
+//
277
+// FromStream performs the following actions to ensure a safe cross-platform update:
278
+//
279
+// 1. If configured, applies the contents of the io.Reader as a binary patch.
280
+//
281
+// 2. If configured, computes the sha256 checksum and verifies it matches.
282
+//
283
+// 3. If configured, verifies the RSA signature with a public key.
284
+//
285
+// 4. Creates a new file, /path/to/.target.new with mode 0755 with the contents of the updated file
286
+//
287
+// 5. Renames /path/to/target to /path/to/.target.old
288
+//
289
+// 6. Renames /path/to/.target.new to /path/to/target
290
+//
291
+// 7. If the rename is successful, deletes /path/to/.target.old, returns no error
292
+//
293
+// 8. If the rename fails, attempts to rename /path/to/.target.old back to /path/to/target
294
+// If this operation fails, it is reported in the errRecover return value so as not to
295
+// mask the original error that caused the recovery attempt.
296
+//
297
+// On Windows, the removal of /path/to/.target.old always fails, so instead,
298
+// we just make the old file hidden instead.
299
+func (u *Update) FromStream(updateWith io.Reader) (err error, errRecover error) {
300
+ updatePath, err := u.getPath()
301
+ if err != nil {
302
+ return
303
+ }
304
+
305
+ var newBytes []byte
306
+ // apply a patch if requested
307
+ switch u.PatchType {
308
+ case PATCHTYPE_BSDIFF:
309
+ newBytes, err = applyPatch(updateWith, updatePath)
310
+ if err != nil {
311
+ return
312
+ }
313
+ case PATCHTYPE_NONE:
314
+ // no patch to apply, go on through
315
+ newBytes, err = ioutil.ReadAll(updateWith)
316
+ if err != nil {
317
+ return
318
+ }
319
+ default:
320
+ err = fmt.Errorf("Unrecognized patch type: %s", u.PatchType)
321
+ return
322
+ }
323
+
324
+ // verify checksum if requested
325
+ if u.Checksum != nil {
326
+ if err = verifyChecksum(newBytes, u.Checksum); err != nil {
327
+ return
328
+ }
329
+ }
330
+
331
+ // verify signature if requested
332
+ if u.Signature != nil || u.PublicKey != nil {
333
+ if u.Signature == nil {
334
+ err = fmt.Errorf("No public key specified to verify signature")
335
+ return
336
+ }
337
+
338
+ if u.PublicKey == nil {
339
+ err = fmt.Errorf("No signature to verify!")
340
+ return
341
+ }
342
+
343
+ if err = verifySignature(newBytes, u.Signature, u.PublicKey); err != nil {
344
+ return
345
+ }
346
+ }
347
+
348
+ // get the directory the executable exists in
349
+ updateDir := filepath.Dir(updatePath)
350
+ filename := filepath.Base(updatePath)
351
+
352
+ // Copy the contents of of newbinary to a the new executable file
353
+ newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename))
354
+ fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
355
+ if err != nil {
356
+ return
357
+ }
358
+ defer fp.Close()
359
+ _, err = io.Copy(fp, bytes.NewReader(newBytes))
360
+
361
+ // if we don't call fp.Close(), windows won't let us move the new executable
362
+ // because the file will still be "in use"
363
+ fp.Close()
364
+
365
+ // this is where we'll move the executable to so that we can swap in the updated replacement
366
+ oldPath := filepath.Join(updateDir, fmt.Sprintf(".%s.old", filename))
367
+
368
+ // delete any existing old exec file - this is necessary on Windows for two reasons:
369
+ // 1. after a successful update, Windows can't remove the .old file because the process is still running
370
+ // 2. windows rename operations fail if the destination file already exists
371
+ _ = os.Remove(oldPath)
372
+
373
+ // move the existing executable to a new file in the same directory
374
+ err = os.Rename(updatePath, oldPath)
375
+ if err != nil {
376
+ return
377
+ }
378
+
379
+ // move the new exectuable in to become the new program
380
+ err = os.Rename(newPath, updatePath)
381
+
382
+ if err != nil {
383
+ // copy unsuccessful
384
+ errRecover = os.Rename(oldPath, updatePath)
385
+ } else {
386
+ // copy successful, remove the old binary
387
+ errRemove := os.Remove(oldPath)
388
+
389
+ // windows has trouble with removing old binaries, so hide it instead
390
+ if errRemove != nil {
391
+ _ = hideFile(oldPath)
392
+ }
393
+ }
394
+
395
+ return
396
+}
397
+
398
+// CanUpdate() determines whether the process has the correct permissions to
399
+// perform the requested update. If the update can proceed, it returns nil, otherwise
400
+// it returns the error that would occur if an update were attempted.
401
+func (u *Update) CanUpdate() (err error) {
402
+ // get the directory the file exists in
403
+ path, err := u.getPath()
404
+ if err != nil {
405
+ return
406
+ }
407
+
408
+ fileDir := filepath.Dir(path)
409
+ fileName := filepath.Base(path)
410
+
411
+ // attempt to open a file in the file's directory
412
+ newPath := filepath.Join(fileDir, fmt.Sprintf(".%s.new", fileName))
413
+ fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
414
+ if err != nil {
415
+ return
416
+ }
417
+ fp.Close()
418
+
419
+ _ = os.Remove(newPath)
420
+ return
421
+}
422
+
423
+func applyPatch(patch io.Reader, updatePath string) ([]byte, error) {
424
+ // open the file to update
425
+ old, err := os.Open(updatePath)
426
+ if err != nil {
427
+ return nil, err
428
+ }
429
+ defer old.Close()
430
+
431
+ // apply the patch
432
+ applied := new(bytes.Buffer)
433
+ if err = binarydist.Patch(old, applied, patch); err != nil {
434
+ return nil, err
435
+ }
436
+
437
+ return applied.Bytes(), nil
438
+}
439
+
440
+func verifyChecksum(updated []byte, expectedChecksum []byte) error {
441
+ checksum, err := ChecksumForBytes(updated)
442
+ if err != nil {
443
+ return err
444
+ }
445
+
446
+ if !bytes.Equal(expectedChecksum, checksum) {
447
+ return fmt.Errorf("Updated file has wrong checksum. Expected: %x, got: %x", expectedChecksum, checksum)
448
+ }
449
+
450
+ return nil
451
+}
452
+
453
+// ChecksumForFile returns the sha256 checksum for the given file
454
+func ChecksumForFile(path string) ([]byte, error) {
455
+ f, err := os.Open(path)
456
+ if err != nil {
457
+ return nil, err
458
+ }
459
+ defer f.Close()
460
+
461
+ return ChecksumForReader(f)
462
+}
463
+
464
+// ChecksumForReader returns the sha256 checksum for the entire
465
+// contents of the given reader.
466
+func ChecksumForReader(rd io.Reader) ([]byte, error) {
467
+ h := sha256.New()
468
+ if _, err := io.Copy(h, rd); err != nil {
469
+ return nil, err
470
+ }
471
+ return h.Sum(nil), nil
472
+}
473
+
474
+// ChecksumForBytes returns the sha256 checksum for the given bytes
475
+func ChecksumForBytes(source []byte) ([]byte, error) {
476
+ return ChecksumForReader(bytes.NewReader(source))
477
+}
478
+
479
+func verifySignature(source, signature []byte, publicKey *rsa.PublicKey) error {
480
+ checksum, err := ChecksumForBytes(source)
481
+ if err != nil {
482
+ return err
483
+ }
484
+
485
+ return rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, checksum, signature)
486
+}
Godeps/_workspace/src/github.com/inconshreveable/go-update/update_test.go
new
+380
@@ -0,0 +1,380 @@
1
+package update
2
+
3
+import (
4
+ "bytes"
5
+ "crypto"
6
+ "crypto/rand"
7
+ "crypto/rsa"
8
+ "crypto/x509"
9
+ "encoding/pem"
10
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/kr/binarydist"
11
+ "io/ioutil"
12
+ "net"
13
+ "net/http"
14
+ "os"
15
+ "testing"
16
+)
17
+
18
+var (
19
+ oldFile = []byte{0xDE, 0xAD, 0xBE, 0xEF}
20
+ newFile = []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}
21
+)
22
+
23
+func cleanup(path string) {
24
+ os.Remove(path)
25
+}
26
+
27
+// we write with a separate name for each test so that we can run them in parallel
28
+func writeOldFile(path string, t *testing.T) {
29
+ if err := ioutil.WriteFile(path, oldFile, 0777); err != nil {
30
+ t.Fatalf("Failed to write file for testing preparation: %v", err)
31
+ }
32
+}
33
+
34
+func validateUpdate(path string, err error, t *testing.T) {
35
+ if err != nil {
36
+ t.Fatalf("Failed to update: %v", err)
37
+ }
38
+
39
+ buf, err := ioutil.ReadFile(path)
40
+ if err != nil {
41
+ t.Fatalf("Failed to read file post-update: %v", err)
42
+ }
43
+
44
+ if !bytes.Equal(buf, newFile) {
45
+ t.Fatalf("File was not updated! Bytes read: %v, Bytes expected: %v", buf, newFile)
46
+ }
47
+}
48
+
49
+func TestFromStream(t *testing.T) {
50
+ t.Parallel()
51
+
52
+ fName := "TestFromStream"
53
+ defer cleanup(fName)
54
+ writeOldFile(fName, t)
55
+
56
+ err, _ := New().Target(fName).FromStream(bytes.NewReader(newFile))
57
+ validateUpdate(fName, err, t)
58
+}
59
+
60
+func TestFromFile(t *testing.T) {
61
+ t.Parallel()
62
+
63
+ fName := "TestFromFile"
64
+ newFName := "NewTestFromFile"
65
+ defer cleanup(fName)
66
+ defer cleanup(newFName)
67
+ writeOldFile(fName, t)
68
+
69
+ if err := ioutil.WriteFile(newFName, newFile, 0777); err != nil {
70
+ t.Fatalf("Failed to write file to update from: %v", err)
71
+ }
72
+
73
+ err, _ := New().Target(fName).FromFile(newFName)
74
+ validateUpdate(fName, err, t)
75
+}
76
+
77
+func TestFromUrl(t *testing.T) {
78
+ t.Parallel()
79
+
80
+ fName := "TestFromUrl"
81
+ defer cleanup(fName)
82
+ writeOldFile(fName, t)
83
+
84
+ l, err := net.Listen("tcp", ":0")
85
+ if err != nil {
86
+ t.Fatalf("Couldn't bind listener: %v", err)
87
+ }
88
+ addr := l.Addr().String()
89
+
90
+ go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
91
+ w.Write(newFile)
92
+ }))
93
+
94
+ err, _ = New().Target(fName).FromUrl("http://" + addr)
95
+ validateUpdate(fName, err, t)
96
+}
97
+
98
+func TestVerifyChecksum(t *testing.T) {
99
+ t.Parallel()
100
+
101
+ fName := "TestVerifyChecksum"
102
+ defer cleanup(fName)
103
+ writeOldFile(fName, t)
104
+
105
+ checksum, err := ChecksumForBytes(newFile)
106
+ if err != nil {
107
+ t.Fatalf("Failed to compute checksum: %v", err)
108
+ }
109
+
110
+ err, _ = New().Target(fName).VerifyChecksum(checksum).FromStream(bytes.NewReader(newFile))
111
+ validateUpdate(fName, err, t)
112
+}
113
+
114
+func TestVerifyChecksumNegative(t *testing.T) {
115
+ t.Parallel()
116
+
117
+ fName := "TestVerifyChecksumNegative"
118
+ defer cleanup(fName)
119
+ writeOldFile(fName, t)
120
+
121
+ badChecksum := []byte{0x0A, 0x0B, 0x0C, 0xFF}
122
+ err, _ := New().Target(fName).VerifyChecksum(badChecksum).FromStream(bytes.NewReader(newFile))
123
+ if err == nil {
124
+ t.Fatalf("Failed to detect bad checksum!")
125
+ }
126
+}
127
+
128
+func TestApplyPatch(t *testing.T) {
129
+ t.Parallel()
130
+
131
+ fName := "TestApplyPatch"
132
+ defer cleanup(fName)
133
+ writeOldFile(fName, t)
134
+
135
+ patch := new(bytes.Buffer)
136
+ err := binarydist.Diff(bytes.NewReader(oldFile), bytes.NewReader(newFile), patch)
137
+ if err != nil {
138
+ t.Fatalf("Failed to create patch: %v", err)
139
+ }
140
+
141
+ up := New().Target(fName).ApplyPatch(PATCHTYPE_BSDIFF)
142
+ err, _ = up.FromStream(bytes.NewReader(patch.Bytes()))
143
+ validateUpdate(fName, err, t)
144
+}
145
+
146
+func TestCorruptPatch(t *testing.T) {
147
+ t.Parallel()
148
+
149
+ fName := "TestCorruptPatch"
150
+ defer cleanup(fName)
151
+ writeOldFile(fName, t)
152
+
153
+ badPatch := []byte{0x44, 0x38, 0x86, 0x3c, 0x4f, 0x8d, 0x26, 0x54, 0xb, 0x11, 0xce, 0xfe, 0xc1, 0xc0, 0xf8, 0x31, 0x38, 0xa0, 0x12, 0x1a, 0xa2, 0x57, 0x2a, 0xe1, 0x3a, 0x48, 0x62, 0x40, 0x2b, 0x81, 0x12, 0xb1, 0x21, 0xa5, 0x16, 0xed, 0x73, 0xd6, 0x54, 0x84, 0x29, 0xa6, 0xd6, 0xb2, 0x1b, 0xfb, 0xe6, 0xbe, 0x7b, 0x70}
154
+ up := New().Target(fName).ApplyPatch(PATCHTYPE_BSDIFF)
155
+ err, _ := up.FromStream(bytes.NewReader(badPatch))
156
+ if err == nil {
157
+ t.Fatalf("Failed to detect corrupt patch!")
158
+ }
159
+}
160
+
161
+func TestVerifyChecksumPatchNegative(t *testing.T) {
162
+ t.Parallel()
163
+
164
+ fName := "TestVerifyChecksumPatchNegative"
165
+ defer cleanup(fName)
166
+ writeOldFile(fName, t)
167
+
168
+ checksum, err := ChecksumForBytes(newFile)
169
+ if err != nil {
170
+ t.Fatalf("Failed to compute checksum: %v", err)
171
+ }
172
+
173
+ patch := new(bytes.Buffer)
174
+ anotherFile := []byte{0x77, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
175
+ err = binarydist.Diff(bytes.NewReader(oldFile), bytes.NewReader(anotherFile), patch)
176
+ if err != nil {
177
+ t.Fatalf("Failed to create patch: %v", err)
178
+ }
179
+
180
+ up := New().Target(fName).ApplyPatch(PATCHTYPE_BSDIFF).VerifyChecksum(checksum)
181
+ err, _ = up.FromStream(bytes.NewReader(patch.Bytes()))
182
+ if err == nil {
183
+ t.Fatalf("Failed to detect patch to wrong file!")
184
+ }
185
+}
186
+
187
+const publicKey = `-----BEGIN PUBLIC KEY-----
188
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxSWmu7trWKAwDFjiCN2D
189
+Tk2jj2sgcr/CMlI4cSSiIOHrXCFxP1I8i9PvQkd4hasXQrLbT5WXKrRGv1HKUKab
190
+b9ead+kD0kxk7i2bFYvKX43oq66IW0mOLTQBO7I9UyT4L7svcMD+HUQ2BqHoaQe4
191
+y20C59dPr9Dpcz8DZkdLsBV6YKF6Ieb3iGk8oRLMWNaUqPa8f1BGgxAkvPHcqDjT
192
+x4xRnjgTRRRlZvRtALHMUkIChgxDOhoEzKpGiqnX7HtMJfrhV6h0PAXNA4h9Kjv5
193
+5fhJ08Rz7mmZmtH5JxTK5XTquo59sihSajR4bSjZbbkQ1uLkeFlY3eli3xdQ7Nrf
194
+fQIDAQAB
195
+-----END PUBLIC KEY-----`
196
+
197
+const privateKey = `-----BEGIN RSA PRIVATE KEY-----
198
+MIIEogIBAAKCAQEAxSWmu7trWKAwDFjiCN2DTk2jj2sgcr/CMlI4cSSiIOHrXCFx
199
+P1I8i9PvQkd4hasXQrLbT5WXKrRGv1HKUKabb9ead+kD0kxk7i2bFYvKX43oq66I
200
+W0mOLTQBO7I9UyT4L7svcMD+HUQ2BqHoaQe4y20C59dPr9Dpcz8DZkdLsBV6YKF6
201
+Ieb3iGk8oRLMWNaUqPa8f1BGgxAkvPHcqDjTx4xRnjgTRRRlZvRtALHMUkIChgxD
202
+OhoEzKpGiqnX7HtMJfrhV6h0PAXNA4h9Kjv55fhJ08Rz7mmZmtH5JxTK5XTquo59
203
+sihSajR4bSjZbbkQ1uLkeFlY3eli3xdQ7NrffQIDAQABAoIBAAkN+6RvrTR61voa
204
+Mvd5RQiZpEN4Bht/Fyo8gH8h0Zh1B9xJZOwlmMZLS5fdtHlfLEhR8qSrGDBL61vq
205
+I8KkhEsUufF78EL+YzxVN+Q7cWYGHIOWFokqza7hzpSxUQO6lPOMQ1eIZaNueJTB
206
+Zu07/47ISPPg/bXzgGVcpYlTCPTjUwKjtfyMqvX9AD7fIyYRm6zfE7EHj1J2sBFt
207
+Yz1OGELg6HfJwXfpnPfBvftD0hWGzJ78Bp71fPJe6n5gnqmSqRvrcXNWFnH/yqkN
208
+d6vPIxD6Z3LjvyZpkA7JillLva2L/zcIFhg4HZvQnWd8/PpDnUDonu36hcj4SC5j
209
+W4aVPLkCgYEA4XzNKWxqYcajzFGZeSxlRHupSAl2MT7Cc5085MmE7dd31wK2T8O4
210
+n7N4bkm/rjTbX85NsfWdKtWb6mpp8W3VlLP0rp4a/12OicVOkg4pv9LZDmY0sRlE
211
+YuDJk1FeCZ50UrwTZI3rZ9IhZHhkgVA6uWAs7tYndONkxNHG0pjqs4sCgYEA39MZ
212
+JwMqo3qsPntpgP940cCLflEsjS9hYNO3+Sv8Dq3P0HLVhBYajJnotf8VuU0fsQZG
213
+grmtVn1yThFbMq7X1oY4F0XBA+paSiU18c4YyUnwax2u4sw9U/Q9tmQUZad5+ueT
214
+qriMBwGv+ewO+nQxqvAsMUmemrVzrfwA5Oct+hcCgYAfiyXoNZJsOy2O15twqBVC
215
+j0oPGcO+/9iT89sg5lACNbI+EdMPNYIOVTzzsL1v0VUfAe08h++Enn1BPcG0VHkc
216
+ZFBGXTfJoXzfKQrkw7ZzbzuOGB4m6DH44xlP0oIlNlVvfX/5ASF9VJf3RiBJNsAA
217
+TsP6ZVr/rw/ZuL7nlxy+IQKBgDhL/HOXlE3yOQiuOec8WsNHTs7C1BXe6PtVxVxi
218
+988pYK/pclL6zEq5G5NLSceF4obAMVQIJ9UtUGbabrncyGUo9UrFPLsjYvprSZo8
219
+YHegpVwL50UcYgCP2kXZ/ldjPIcjYDz8lhvdDMor2cidGTEJn9P11HLNWP9V91Ob
220
+4jCZAoGAPNRSC5cC8iP/9j+s2/kdkfWJiNaolPYAUrmrkL6H39PYYZM5tnhaIYJV
221
+Oh9AgABamU0eb3p3vXTISClVgV7ifq1HyZ7BSUhMfaY2Jk/s3sUHCWFxPZe9sgEG
222
+KinIY/373KIkIV/5g4h2v1w330IWcfptxKcY/Er3DJr38f695GE=
223
+-----END RSA PRIVATE KEY-----`
224
+
225
+func sign(privatePEM string, source []byte, t *testing.T) []byte {
226
+ block, _ := pem.Decode([]byte(privatePEM))
227
+ if block == nil {
228
+ t.Fatalf("Failed to parse private key PEM")
229
+ }
230
+
231
+ priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
232
+ if err != nil {
233
+ t.Fatalf("Failed to parse private key DER")
234
+ }
235
+
236
+ checksum, err := ChecksumForBytes(source)
237
+ if err != nil {
238
+ t.Fatalf("Failed to make checksum")
239
+ }
240
+
241
+ sig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, checksum)
242
+ if err != nil {
243
+ t.Fatalf("Failed to sign: %v", sig)
244
+ }
245
+
246
+ return sig
247
+}
248
+
249
+func TestVerifySignature(t *testing.T) {
250
+ t.Parallel()
251
+
252
+ fName := "TestVerifySignature"
253
+ defer cleanup(fName)
254
+ writeOldFile(fName, t)
255
+
256
+ up, err := New().Target(fName).VerifySignatureWithPEM([]byte(publicKey))
257
+ if err != nil {
258
+ t.Fatalf("Could not parse public key: %v", err)
259
+ }
260
+
261
+ signature := sign(privateKey, newFile, t)
262
+ err, _ = up.VerifySignature(signature).FromStream(bytes.NewReader(newFile))
263
+ validateUpdate(fName, err, t)
264
+}
265
+
266
+func TestVerifyFailBadSignature(t *testing.T) {
267
+ t.Parallel()
268
+
269
+ fName := "TestVerifyFailBadSignature"
270
+ defer cleanup(fName)
271
+ writeOldFile(fName, t)
272
+
273
+ up, err := New().Target(fName).VerifySignatureWithPEM([]byte(publicKey))
274
+ if err != nil {
275
+ t.Fatalf("Could not parse public key: %v", err)
276
+ }
277
+
278
+ badSig := []byte{0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA}
279
+ err, _ = up.VerifySignature(badSig).FromStream(bytes.NewReader(newFile))
280
+ if err == nil {
281
+ t.Fatalf("Did not fail with bad signature")
282
+ }
283
+}
284
+
285
+func TestVerifyFailNoSignature(t *testing.T) {
286
+ t.Parallel()
287
+
288
+ fName := "TestVerifySignatureWithPEM"
289
+ defer cleanup(fName)
290
+ writeOldFile(fName, t)
291
+
292
+ up, err := New().Target(fName).VerifySignatureWithPEM([]byte(publicKey))
293
+ if err != nil {
294
+ t.Fatalf("Could not parse public key: %v", err)
295
+ }
296
+
297
+ err, _ = up.VerifySignature([]byte{}).FromStream(bytes.NewReader(newFile))
298
+ if err == nil {
299
+ t.Fatalf("Did not fail with empty signature")
300
+ }
301
+}
302
+
303
+const wrongKey = `-----BEGIN RSA PRIVATE KEY-----
304
+MIIEpAIBAAKCAQEArKqjT+xOFJILe0CX7lKfQy52YwWLF9devYtLeUHTbPOueGLy
305
+6CjrXJBrWIxNBxRd53y4dtgiMqCX6Gmmvuy8HnfbBuJjR2mcdEYo8UDy+aSVBQ6T
306
+/ND7Fd7KSzOruEFFzl2QFnZ/SrW/nsXdGyuF8l+YIwjZJRyV6StZkZ4ydOzOqUk9
307
+FXTeIkhX/Q7/jTETw7L3wxMyLgJAlV3lxDsPkMjxymngtbAIjwEjLsVeU+Prcz2e
308
+Ww34SZQ8qwzAdXieuDPryMwEsCcgc5NAKJFNL8TppYGDXOHI7CRXqHfNiJq2R+kQ
309
+LdxRvmfx8/iu4xM2hBzk4uSDS6TTn2AnWBm+cQIDAQABAoIBAFp//aUwaCRj/9yU
310
+GI3zhEJEIgz4pNTUL3YNgnuFwvlCJ9o1kreYavRTRdBdiSoCxM1GE7FGy3XZsoVA
311
+iwNbNaaKj6RmGD8f3b8b3u3EaxXp66mA4JQMPO5TnZgY9xJWM+5cH9+GMGXKKStg
312
+7ekFwOkuraD/TEElYHWcIRAv6KZbc/YOIa6YDKi+1Gc7u0MeIvwqN7nwaBAoJKUE
313
+ZrJIfYKIViD/ZrCpgWN47C9x8w3ne7iiDrYoYct+0reC9LFlqwVBtDnyVx/q3upW
314
+zzczbNQagu3w0QgprDGhy0ZhDNxuylV3XBWTB+xBrFQgz6rD3LzUPywlbt0N7ZmD
315
+936MVSECgYEA1IElCahF/+hC/OxFgy98DubAUDGmrvxWeZF3bvTseWZQp/gzxVS+
316
+SYumYyd2Ysx5+UjXQlVgR6BbDG13+DpSpZm6+MeWHBAR+KA2qCg009SDFv7l26/d
317
+xMT7lvIWz7ckQDb/+jvhF9HL2llyTN1Zex+n3XBeAMKNrPaubdEBFsUCgYEA0AIO
318
+tZMtzOpioAR1lGbwIguq04msDdrJNaY2TKrLeviJuQUw94fgL+3ULAPsiyxaU/Gv
319
+vln11R7aIp1SJ09T2UoFRbty+6SGRC56+Wh0pn5VnAi7aT6qdkYWhEjhqRHuXosf
320
+PYboXBuMwA0FBUTxWQL/lux2PZgvBkniYh5jI70CgYEAk9KmhhpFX2gdOT3OeRxO
321
+CzufaemwDqfAK97yGwBLg4OV9dJliQ6TNCvt+amY489jxfJSs3UafZjh3TpFKyq/
322
+FS1kb+y+0hSnu7EPdFhLr1N0QUndcb3b4iY48V7EWYgHspfP5y1CPsSVLvXr2eZc
323
+eZaiuhqReavczAXpfsDWJhUCgYEAwmUp2gfyhc+G3IVOXaLWSPseaxP+9/PAl6L+
324
+nCgCgqpEC+YOHUee/SwHXhtMtcR9pnX5CKyKUuLCehcM8C/y7N+AjerhSsw3rwDB
325
+bNVyLydiWrDOdU1bga1+3aI/QwK/AxyB1b5+6ZXVtKZ2SrZj2Aw1UZcr6eSQDhB+
326
+wbQkcwECgYBF13FMA6OOon992t9H3I+4KDgmz6G6mz3bVXSoFWfO1p/yXP04BzJl
327
+jtLFvFVTZdMs2o/wTd4SL6gYjx9mlOWwM8FblmjfiNSUVIyye33fRntEAr1n+FYI
328
+Xhv6aVnNdaGehGIqQxXFoGyiJxG3RYNkSwaTOamxY1V+ceLuO26n2Q==
329
+-----END RSA PRIVATE KEY-----`
330
+
331
+func TestVerifyFailWrongSignature(t *testing.T) {
332
+ t.Parallel()
333
+
334
+ fName := "TestVerifyFailWrongSignature"
335
+ defer cleanup(fName)
336
+ writeOldFile(fName, t)
337
+
338
+ up, err := New().Target(fName).VerifySignatureWithPEM([]byte(publicKey))
339
+ if err != nil {
340
+ t.Fatalf("Could not parse public key: %v", err)
341
+ }
342
+
343
+ signature := sign(wrongKey, newFile, t)
344
+ err, _ = up.VerifySignature(signature).FromStream(bytes.NewReader(newFile))
345
+ if err == nil {
346
+ t.Fatalf("Verified an update that was signed by an untrusted key!")
347
+ }
348
+}
349
+
350
+func TestSignatureButNoPublicKey(t *testing.T) {
351
+ t.Parallel()
352
+
353
+ fName := "TestSignatureButNoPublicKey"
354
+ defer cleanup(fName)
355
+ writeOldFile(fName, t)
356
+
357
+ sig := sign(privateKey, newFile, t)
358
+ err, _ := New().Target(fName).VerifySignature(sig).FromStream(bytes.NewReader(newFile))
359
+ if err == nil {
360
+ t.Fatalf("Allowed an update with a signautre verification when no public key was specified!")
361
+ }
362
+}
363
+
364
+func TestPublicKeyButNoSignature(t *testing.T) {
365
+ t.Parallel()
366
+
367
+ fName := "TestPublicKeyButNoSignature"
368
+ defer cleanup(fName)
369
+ writeOldFile(fName, t)
370
+
371
+ up, err := New().Target(fName).VerifySignatureWithPEM([]byte(publicKey))
372
+ if err != nil {
373
+ t.Fatalf("Could not parse public key: %v", err)
374
+ }
375
+
376
+ err, _ = up.FromStream(bytes.NewReader(newFile))
377
+ if err == nil {
378
+ t.Fatalf("Allowed an update with no signautre when a public key was specified!")
379
+ }
380
+}
Godeps/_workspace/src/github.com/jbenet/go-multihash/LICENSE
new
+21
@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2014 Juan Batiz-Benet
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+THE SOFTWARE.
Godeps/_workspace/src/github.com/kr/binarydist/.gitignore
new
+1
@@ -0,0 +1 @@
1
+test.*
Godeps/_workspace/src/github.com/kr/binarydist/License
new
+22
@@ -0,0 +1,22 @@
1
+Copyright 2012 Keith Rarick
2
+
3
+Permission is hereby granted, free of charge, to any person
4
+obtaining a copy of this software and associated documentation
5
+files (the "Software"), to deal in the Software without
6
+restriction, including without limitation the rights to use,
7
+copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+copies of the Software, and to permit persons to whom the
9
+Software is furnished to do so, subject to the following
10
+conditions:
11
+
12
+The above copyright notice and this permission notice shall be
13
+included in all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+OTHER DEALINGS IN THE SOFTWARE.
Godeps/_workspace/src/github.com/kr/binarydist/Readme.md
new
+7
@@ -0,0 +1,7 @@
1
+# binarydist
2
+
3
+Package binarydist implements binary diff and patch as described on
4
+<http://www.daemonology.net/bsdiff/>. It reads and writes files
5
+compatible with the tools there.
6
+
7
+Documentation at <http://go.pkgdoc.org/github.com/kr/binarydist>.
Godeps/_workspace/src/github.com/kr/binarydist/bzip2.go
new
+40
@@ -0,0 +1,40 @@
1
+package binarydist
2
+
3
+import (
4
+ "io"
5
+ "os/exec"
6
+)
7
+
8
+type bzip2Writer struct {
9
+ c *exec.Cmd
10
+ w io.WriteCloser
11
+}
12
+
13
+func (w bzip2Writer) Write(b []byte) (int, error) {
14
+ return w.w.Write(b)
15
+}
16
+
17
+func (w bzip2Writer) Close() error {
18
+ if err := w.w.Close(); err != nil {
19
+ return err
20
+ }
21
+ return w.c.Wait()
22
+}
23
+
24
+// Package compress/bzip2 implements only decompression,
25
+// so we'll fake it by running bzip2 in another process.
26
+func newBzip2Writer(w io.Writer) (wc io.WriteCloser, err error) {
27
+ var bw bzip2Writer
28
+ bw.c = exec.Command("bzip2", "-c")
29
+ bw.c.Stdout = w
30
+
31
+ if bw.w, err = bw.c.StdinPipe(); err != nil {
32
+ return nil, err
33
+ }
34
+
35
+ if err = bw.c.Start(); err != nil {
36
+ return nil, err
37
+ }
38
+
39
+ return bw, nil
40
+}
Godeps/_workspace/src/github.com/kr/binarydist/common_test.go
new
+93
@@ -0,0 +1,93 @@
1
+package binarydist
2
+
3
+import (
4
+ "crypto/rand"
5
+ "io"
6
+ "io/ioutil"
7
+ "os"
8
+)
9
+
10
+func mustOpen(path string) *os.File {
11
+ f, err := os.Open(path)
12
+ if err != nil {
13
+ panic(err)
14
+ }
15
+
16
+ return f
17
+}
18
+
19
+func mustReadAll(r io.Reader) []byte {
20
+ b, err := ioutil.ReadAll(r)
21
+ if err != nil {
22
+ panic(err)
23
+ }
24
+ return b
25
+}
26
+
27
+func fileCmp(a, b *os.File) int64 {
28
+ sa, err := a.Seek(0, 2)
29
+ if err != nil {
30
+ panic(err)
31
+ }
32
+
33
+ sb, err := b.Seek(0, 2)
34
+ if err != nil {
35
+ panic(err)
36
+ }
37
+
38
+ if sa != sb {
39
+ return sa
40
+ }
41
+
42
+ _, err = a.Seek(0, 0)
43
+ if err != nil {
44
+ panic(err)
45
+ }
46
+
47
+ _, err = b.Seek(0, 0)
48
+ if err != nil {
49
+ panic(err)
50
+ }
51
+
52
+ pa, err := ioutil.ReadAll(a)
53
+ if err != nil {
54
+ panic(err)
55
+ }
56
+
57
+ pb, err := ioutil.ReadAll(b)
58
+ if err != nil {
59
+ panic(err)
60
+ }
61
+
62
+ for i := range pa {
63
+ if pa[i] != pb[i] {
64
+ return int64(i)
65
+ }
66
+ }
67
+ return -1
68
+}
69
+
70
+func mustWriteRandFile(path string, size int) *os.File {
71
+ p := make([]byte, size)
72
+ _, err := rand.Read(p)
73
+ if err != nil {
74
+ panic(err)
75
+ }
76
+
77
+ f, err := os.Create(path)
78
+ if err != nil {
79
+ panic(err)
80
+ }
81
+
82
+ _, err = f.Write(p)
83
+ if err != nil {
84
+ panic(err)
85
+ }
86
+
87
+ _, err = f.Seek(0, 0)
88
+ if err != nil {
89
+ panic(err)
90
+ }
91
+
92
+ return f
93
+}
Godeps/_workspace/src/github.com/kr/binarydist/diff.go
new
+408
@@ -0,0 +1,408 @@
1
+package binarydist
2
+
3
+import (
4
+ "bytes"
5
+ "encoding/binary"
6
+ "io"
7
+ "io/ioutil"
8
+)
9
+
10
+func swap(a []int, i, j int) { a[i], a[j] = a[j], a[i] }
11
+
12
+func split(I, V []int, start, length, h int) {
13
+ var i, j, k, x, jj, kk int
14
+
15
+ if length < 16 {
16
+ for k = start; k < start+length; k += j {
17
+ j = 1
18
+ x = V[I[k]+h]
19
+ for i = 1; k+i < start+length; i++ {
20
+ if V[I[k+i]+h] < x {
21
+ x = V[I[k+i]+h]
22
+ j = 0
23
+ }
24
+ if V[I[k+i]+h] == x {
25
+ swap(I, k+i, k+j)
26
+ j++
27
+ }
28
+ }
29
+ for i = 0; i < j; i++ {
30
+ V[I[k+i]] = k + j - 1
31
+ }
32
+ if j == 1 {
33
+ I[k] = -1
34
+ }
35
+ }
36
+ return
37
+ }
38
+
39
+ x = V[I[start+length/2]+h]
40
+ jj = 0
41
+ kk = 0
42
+ for i = start; i < start+length; i++ {
43
+ if V[I[i]+h] < x {
44
+ jj++
45
+ }
46
+ if V[I[i]+h] == x {
47
+ kk++
48
+ }
49
+ }
50
+ jj += start
51
+ kk += jj
52
+
53
+ i = start
54
+ j = 0
55
+ k = 0
56
+ for i < jj {
57
+ if V[I[i]+h] < x {
58
+ i++
59
+ } else if V[I[i]+h] == x {
60
+ swap(I, i, jj+j)
61
+ j++
62
+ } else {
63
+ swap(I, i, kk+k)
64
+ k++
65
+ }
66
+ }
67
+
68
+ for jj+j < kk {
69
+ if V[I[jj+j]+h] == x {
70
+ j++
71
+ } else {
72
+ swap(I, jj+j, kk+k)
73
+ k++
74
+ }
75
+ }
76
+
77
+ if jj > start {
78
+ split(I, V, start, jj-start, h)
79
+ }
80
+
81
+ for i = 0; i < kk-jj; i++ {
82
+ V[I[jj+i]] = kk - 1
83
+ }
84
+ if jj == kk-1 {
85
+ I[jj] = -1
86
+ }
87
+
88
+ if start+length > kk {
89
+ split(I, V, kk, start+length-kk, h)
90
+ }
91
+}
92
+
93
+func qsufsort(obuf []byte) []int {
94
+ var buckets [256]int
95
+ var i, h int
96
+ I := make([]int, len(obuf)+1)
97
+ V := make([]int, len(obuf)+1)
98
+
99
+ for _, c := range obuf {
100
+ buckets[c]++
101
+ }
102
+ for i = 1; i < 256; i++ {
103
+ buckets[i] += buckets[i-1]
104
+ }
105
+ copy(buckets[1:], buckets[:])
106
+ buckets[0] = 0
107
+
108
+ for i, c := range obuf {
109
+ buckets[c]++
110
+ I[buckets[c]] = i
111
+ }
112
+
113
+ I[0] = len(obuf)
114
+ for i, c := range obuf {
115
+ V[i] = buckets[c]
116
+ }
117
+
118
+ V[len(obuf)] = 0
119
+ for i = 1; i < 256; i++ {
120
+ if buckets[i] == buckets[i-1]+1 {
121
+ I[buckets[i]] = -1
122
+ }
123
+ }
124
+ I[0] = -1
125
+
126
+ for h = 1; I[0] != -(len(obuf) + 1); h += h {
127
+ var n int
128
+ for i = 0; i < len(obuf)+1; {
129
+ if I[i] < 0 {
130
+ n -= I[i]
131
+ i -= I[i]
132
+ } else {
133
+ if n != 0 {
134
+ I[i-n] = -n
135
+ }
136
+ n = V[I[i]] + 1 - i
137
+ split(I, V, i, n, h)
138
+ i += n
139
+ n = 0
140
+ }
141
+ }
142
+ if n != 0 {
143
+ I[i-n] = -n
144
+ }
145
+ }
146
+
147
+ for i = 0; i < len(obuf)+1; i++ {
148
+ I[V[i]] = i
149
+ }
150
+ return I
151
+}
152
+
153
+func matchlen(a, b []byte) (i int) {
154
+ for i < len(a) && i < len(b) && a[i] == b[i] {
155
+ i++
156
+ }
157
+ return i
158
+}
159
+
160
+func search(I []int, obuf, nbuf []byte, st, en int) (pos, n int) {
161
+ if en-st < 2 {
162
+ x := matchlen(obuf[I[st]:], nbuf)
163
+ y := matchlen(obuf[I[en]:], nbuf)
164
+
165
+ if x > y {
166
+ return I[st], x
167
+ } else {
168
+ return I[en], y
169
+ }
170
+ }
171
+
172
+ x := st + (en-st)/2
173
+ if bytes.Compare(obuf[I[x]:], nbuf) < 0 {
174
+ return search(I, obuf, nbuf, x, en)
175
+ } else {
176
+ return search(I, obuf, nbuf, st, x)
177
+ }
178
+ panic("unreached")
179
+}
180
+
181
+// Diff computes the difference between old and new, according to the bsdiff
182
+// algorithm, and writes the result to patch.
183
+func Diff(old, new io.Reader, patch io.Writer) error {
184
+ obuf, err := ioutil.ReadAll(old)
185
+ if err != nil {
186
+ return err
187
+ }
188
+
189
+ nbuf, err := ioutil.ReadAll(new)
190
+ if err != nil {
191
+ return err
192
+ }
193
+
194
+ pbuf, err := diffBytes(obuf, nbuf)
195
+ if err != nil {
196
+ return err
197
+ }
198
+
199
+ _, err = patch.Write(pbuf)
200
+ return err
201
+}
202
+
203
+func diffBytes(obuf, nbuf []byte) ([]byte, error) {
204
+ var patch seekBuffer
205
+ err := diff(obuf, nbuf, &patch)
206
+ if err != nil {
207
+ return nil, err
208
+ }
209
+ return patch.buf, nil
210
+}
211
+
212
+func diff(obuf, nbuf []byte, patch io.WriteSeeker) error {
213
+ var lenf int
214
+ I := qsufsort(obuf)
215
+ db := make([]byte, len(nbuf))
216
+ eb := make([]byte, len(nbuf))
217
+ var dblen, eblen int
218
+
219
+ var hdr header
220
+ hdr.Magic = magic
221
+ hdr.NewSize = int64(len(nbuf))
222
+ err := binary.Write(patch, signMagLittleEndian{}, &hdr)
223
+ if err != nil {
224
+ return err
225
+ }
226
+
227
+ // Compute the differences, writing ctrl as we go
228
+ pfbz2, err := newBzip2Writer(patch)
229
+ if err != nil {
230
+ return err
231
+ }
232
+ var scan, pos, length int
233
+ var lastscan, lastpos, lastoffset int
234
+ for scan < len(nbuf) {
235
+ var oldscore int
236
+ scan += length
237
+ for scsc := scan; scan < len(nbuf); scan++ {
238
+ pos, length = search(I, obuf, nbuf[scan:], 0, len(obuf))
239
+
240
+ for ; scsc < scan+length; scsc++ {
241
+ if scsc+lastoffset < len(obuf) &&
242
+ obuf[scsc+lastoffset] == nbuf[scsc] {
243
+ oldscore++
244
+ }
245
+ }
246
+
247
+ if (length == oldscore && length != 0) || length > oldscore+8 {
248
+ break
249
+ }
250
+
251
+ if scan+lastoffset < len(obuf) && obuf[scan+lastoffset] == nbuf[scan] {
252
+ oldscore--
253
+ }
254
+ }
255
+
256
+ if length != oldscore || scan == len(nbuf) {
257
+ var s, Sf int
258
+ lenf = 0
259
+ for i := 0; lastscan+i < scan && lastpos+i < len(obuf); {
260
+ if obuf[lastpos+i] == nbuf[lastscan+i] {
261
+ s++
262
+ }
263
+ i++
264
+ if s*2-i > Sf*2-lenf {
265
+ Sf = s
266
+ lenf = i
267
+ }
268
+ }
269
+
270
+ lenb := 0
271
+ if scan < len(nbuf) {
272
+ var s, Sb int
273
+ for i := 1; (scan >= lastscan+i) && (pos >= i); i++ {
274
+ if obuf[pos-i] == nbuf[scan-i] {
275
+ s++
276
+ }
277
+ if s*2-i > Sb*2-lenb {
278
+ Sb = s
279
+ lenb = i
280
+ }
281
+ }
282
+ }
283
+
284
+ if lastscan+lenf > scan-lenb {
285
+ overlap := (lastscan + lenf) - (scan - lenb)
286
+ s := 0
287
+ Ss := 0
288
+ lens := 0
289
+ for i := 0; i < overlap; i++ {
290
+ if nbuf[lastscan+lenf-overlap+i] == obuf[lastpos+lenf-overlap+i] {
291
+ s++
292
+ }
293
+ if nbuf[scan-lenb+i] == obuf[pos-lenb+i] {
294
+ s--
295
+ }
296
+ if s > Ss {
297
+ Ss = s
298
+ lens = i + 1
299
+ }
300
+ }
301
+
302
+ lenf += lens - overlap
303
+ lenb -= lens
304
+ }
305
+
306
+ for i := 0; i < lenf; i++ {
307
+ db[dblen+i] = nbuf[lastscan+i] - obuf[lastpos+i]
308
+ }
309
+ for i := 0; i < (scan-lenb)-(lastscan+lenf); i++ {
310
+ eb[eblen+i] = nbuf[lastscan+lenf+i]
311
+ }
312
+
313
+ dblen += lenf
314
+ eblen += (scan - lenb) - (lastscan + lenf)
315
+
316
+ err = binary.Write(pfbz2, signMagLittleEndian{}, int64(lenf))
317
+ if err != nil {
318
+ pfbz2.Close()
319
+ return err
320
+ }
321
+
322
+ val := (scan - lenb) - (lastscan + lenf)
323
+ err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val))
324
+ if err != nil {
325
+ pfbz2.Close()
326
+ return err
327
+ }
328
+
329
+ val = (pos - lenb) - (lastpos + lenf)
330
+ err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val))
331
+ if err != nil {
332
+ pfbz2.Close()
333
+ return err
334
+ }
335
+
336
+ lastscan = scan - lenb
337
+ lastpos = pos - lenb
338
+ lastoffset = pos - scan
339
+ }
340
+ }
341
+ err = pfbz2.Close()
342
+ if err != nil {
343
+ return err
344
+ }
345
+
346
+ // Compute size of compressed ctrl data
347
+ l64, err := patch.Seek(0, 1)
348
+ if err != nil {
349
+ return err
350
+ }
351
+ hdr.CtrlLen = int64(l64 - 32)
352
+
353
+ // Write compressed diff data
354
+ pfbz2, err = newBzip2Writer(patch)
355
+ if err != nil {
356
+ return err
357
+ }
358
+ n, err := pfbz2.Write(db[:dblen])
359
+ if err != nil {
360
+ pfbz2.Close()
361
+ return err
362
+ }
363
+ if n != dblen {
364
+ pfbz2.Close()
365
+ return io.ErrShortWrite
366
+ }
367
+ err = pfbz2.Close()
368
+ if err != nil {
369
+ return err
370
+ }
371
+
372
+ // Compute size of compressed diff data
373
+ n64, err := patch.Seek(0, 1)
374
+ if err != nil {
375
+ return err
376
+ }
377
+ hdr.DiffLen = n64 - l64
378
+
379
+ // Write compressed extra data
380
+ pfbz2, err = newBzip2Writer(patch)
381
+ if err != nil {
382
+ return err
383
+ }
384
+ n, err = pfbz2.Write(eb[:eblen])
385
+ if err != nil {
386
+ pfbz2.Close()
387
+ return err
388
+ }
389
+ if n != eblen {
390
+ pfbz2.Close()
391
+ return io.ErrShortWrite
392
+ }
393
+ err = pfbz2.Close()
394
+ if err != nil {
395
+ return err
396
+ }
397
+
398
+ // Seek to the beginning, write the header, and close the file
399
+ _, err = patch.Seek(0, 0)
400
+ if err != nil {
401
+ return err
402
+ }
403
+ err = binary.Write(patch, signMagLittleEndian{}, &hdr)
404
+ if err != nil {
405
+ return err
406
+ }
407
+ return nil
408
+}
Godeps/_workspace/src/github.com/kr/binarydist/diff_test.go
new
+67
@@ -0,0 +1,67 @@
1
+package binarydist
2
+
3
+import (
4
+ "bytes"
5
+ "io/ioutil"
6
+ "os"
7
+ "os/exec"
8
+ "testing"
9
+)
10
+
11
+var diffT = []struct {
12
+ old *os.File
13
+ new *os.File
14
+}{
15
+ {
16
+ old: mustWriteRandFile("test.old", 1e3),
17
+ new: mustWriteRandFile("test.new", 1e3),
18
+ },
19
+ {
20
+ old: mustOpen("testdata/sample.old"),
21
+ new: mustOpen("testdata/sample.new"),
22
+ },
23
+}
24
+
25
+func TestDiff(t *testing.T) {
26
+ for _, s := range diffT {
27
+ got, err := ioutil.TempFile("/tmp", "bspatch.")
28
+ if err != nil {
29
+ panic(err)
30
+ }
31
+ os.Remove(got.Name())
32
+
33
+ exp, err := ioutil.TempFile("/tmp", "bspatch.")
34
+ if err != nil {
35
+ panic(err)
36
+ }
37
+
38
+ cmd := exec.Command("bsdiff", s.old.Name(), s.new.Name(), exp.Name())
39
+ cmd.Stdout = os.Stdout
40
+ err = cmd.Run()
41
+ os.Remove(exp.Name())
42
+ if err != nil {
43
+ panic(err)
44
+ }
45
+
46
+ err = Diff(s.old, s.new, got)
47
+ if err != nil {
48
+ t.Fatal("err", err)
49
+ }
50
+
51
+ _, err = got.Seek(0, 0)
52
+ if err != nil {
53
+ panic(err)
54
+ }
55
+ gotBuf := mustReadAll(got)
56
+ expBuf := mustReadAll(exp)
57
+
58
+ if !bytes.Equal(gotBuf, expBuf) {
59
+ t.Fail()
60
+ t.Logf("diff %s %s", s.old.Name(), s.new.Name())
61
+ t.Logf("%s: len(got) = %d", got.Name(), len(gotBuf))
62
+ t.Logf("%s: len(exp) = %d", exp.Name(), len(expBuf))
63
+ i := matchlen(gotBuf, expBuf)
64
+ t.Logf("produced different output at pos %d; %d != %d", i, gotBuf[i], expBuf[i])
65
+ }
66
+ }
67
+}
Godeps/_workspace/src/github.com/kr/binarydist/doc.go
new
+24
@@ -0,0 +1,24 @@
1
+// Package binarydist implements binary diff and patch as described on
2
+// http://www.daemonology.net/bsdiff/. It reads and writes files
3
+// compatible with the tools there.
4
+package binarydist
5
+
6
+var magic = [8]byte{'B', 'S', 'D', 'I', 'F', 'F', '4', '0'}
7
+
8
+// File format:
9
+// 0 8 "BSDIFF40"
10
+// 8 8 X
11
+// 16 8 Y
12
+// 24 8 sizeof(newfile)
13
+// 32 X bzip2(control block)
14
+// 32+X Y bzip2(diff block)
15
+// 32+X+Y ??? bzip2(extra block)
16
+// with control block a set of triples (x,y,z) meaning "add x bytes
17
+// from oldfile to x bytes from the diff block; copy y bytes from the
18
+// extra block; seek forwards in oldfile by z bytes".
19
+type header struct {
20
+ Magic [8]byte
21
+ CtrlLen int64
22
+ DiffLen int64
23
+ NewSize int64
24
+}
Godeps/_workspace/src/github.com/kr/binarydist/encoding.go
new
+53
@@ -0,0 +1,53 @@
1
+package binarydist
2
+
3
+// SignMagLittleEndian is the numeric encoding used by the bsdiff tools.
4
+// It implements binary.ByteOrder using a sign-magnitude format
5
+// and little-endian byte order. Only methods Uint64 and String
6
+// have been written; the rest panic.
7
+type signMagLittleEndian struct{}
8
+
9
+func (signMagLittleEndian) Uint16(b []byte) uint16 { panic("unimplemented") }
10
+
11
+func (signMagLittleEndian) PutUint16(b []byte, v uint16) { panic("unimplemented") }
12
+
13
+func (signMagLittleEndian) Uint32(b []byte) uint32 { panic("unimplemented") }
14
+
15
+func (signMagLittleEndian) PutUint32(b []byte, v uint32) { panic("unimplemented") }
16
+
17
+func (signMagLittleEndian) Uint64(b []byte) uint64 {
18
+ y := int64(b[0]) |
19
+ int64(b[1])<<8 |
20
+ int64(b[2])<<16 |
21
+ int64(b[3])<<24 |
22
+ int64(b[4])<<32 |
23
+ int64(b[5])<<40 |
24
+ int64(b[6])<<48 |
25
+ int64(b[7]&0x7f)<<56
26
+
27
+ if b[7]&0x80 != 0 {
28
+ y = -y
29
+ }
30
+ return uint64(y)
31
+}
32
+
33
+func (signMagLittleEndian) PutUint64(b []byte, v uint64) {
34
+ x := int64(v)
35
+ neg := x < 0
36
+ if neg {
37
+ x = -x
38
+ }
39
+
40
+ b[0] = byte(x)
41
+ b[1] = byte(x >> 8)
42
+ b[2] = byte(x >> 16)
43
+ b[3] = byte(x >> 24)
44
+ b[4] = byte(x >> 32)
45
+ b[5] = byte(x >> 40)
46
+ b[6] = byte(x >> 48)
47
+ b[7] = byte(x >> 56)
48
+ if neg {
49
+ b[7] |= 0x80
50
+ }
51
+}
52
+
53
+func (signMagLittleEndian) String() string { return "signMagLittleEndian" }
Godeps/_workspace/src/github.com/kr/binarydist/patch.go
new
+109
@@ -0,0 +1,109 @@
1
+package binarydist
2
+
3
+import (
4
+ "bytes"
5
+ "compress/bzip2"
6
+ "encoding/binary"
7
+ "errors"
8
+ "io"
9
+ "io/ioutil"
10
+)
11
+
12
+var ErrCorrupt = errors.New("corrupt patch")
13
+
14
+// Patch applies patch to old, according to the bspatch algorithm,
15
+// and writes the result to new.
16
+func Patch(old io.Reader, new io.Writer, patch io.Reader) error {
17
+ var hdr header
18
+ err := binary.Read(patch, signMagLittleEndian{}, &hdr)
19
+ if err != nil {
20
+ return err
21
+ }
22
+ if hdr.Magic != magic {
23
+ return ErrCorrupt
24
+ }
25
+ if hdr.CtrlLen < 0 || hdr.DiffLen < 0 || hdr.NewSize < 0 {
26
+ return ErrCorrupt
27
+ }
28
+
29
+ ctrlbuf := make([]byte, hdr.CtrlLen)
30
+ _, err = io.ReadFull(patch, ctrlbuf)
31
+ if err != nil {
32
+ return err
33
+ }
34
+ cpfbz2 := bzip2.NewReader(bytes.NewReader(ctrlbuf))
35
+
36
+ diffbuf := make([]byte, hdr.DiffLen)
37
+ _, err = io.ReadFull(patch, diffbuf)
38
+ if err != nil {
39
+ return err
40
+ }
41
+ dpfbz2 := bzip2.NewReader(bytes.NewReader(diffbuf))
42
+
43
+ // The entire rest of the file is the extra block.
44
+ epfbz2 := bzip2.NewReader(patch)
45
+
46
+ obuf, err := ioutil.ReadAll(old)
47
+ if err != nil {
48
+ return err
49
+ }
50
+
51
+ nbuf := make([]byte, hdr.NewSize)
52
+
53
+ var oldpos, newpos int64
54
+ for newpos < hdr.NewSize {
55
+ var ctrl struct{ Add, Copy, Seek int64 }
56
+ err = binary.Read(cpfbz2, signMagLittleEndian{}, &ctrl)
57
+ if err != nil {
58
+ return err
59
+ }
60
+
61
+ // Sanity-check
62
+ if newpos+ctrl.Add > hdr.NewSize {
63
+ return ErrCorrupt
64
+ }
65
+
66
+ // Read diff string
67
+ _, err = io.ReadFull(dpfbz2, nbuf[newpos:newpos+ctrl.Add])
68
+ if err != nil {
69
+ return ErrCorrupt
70
+ }
71
+
72
+ // Add old data to diff string
73
+ for i := int64(0); i < ctrl.Add; i++ {
74
+ if oldpos+i >= 0 && oldpos+i < int64(len(obuf)) {
75
+ nbuf[newpos+i] += obuf[oldpos+i]
76
+ }
77
+ }
78
+
79
+ // Adjust pointers
80
+ newpos += ctrl.Add
81
+ oldpos += ctrl.Add
82
+
83
+ // Sanity-check
84
+ if newpos+ctrl.Copy > hdr.NewSize {
85
+ return ErrCorrupt
86
+ }
87
+
88
+ // Read extra string
89
+ _, err = io.ReadFull(epfbz2, nbuf[newpos:newpos+ctrl.Copy])
90
+ if err != nil {
91
+ return ErrCorrupt
92
+ }
93
+
94
+ // Adjust pointers
95
+ newpos += ctrl.Copy
96
+ oldpos += ctrl.Seek
97
+ }
98
+
99
+ // Write the new file
100
+ for len(nbuf) > 0 {
101
+ n, err := new.Write(nbuf)
102
+ if err != nil {
103
+ return err
104
+ }
105
+ nbuf = nbuf[n:]
106
+ }
107
+
108
+ return nil
109
+}
Godeps/_workspace/src/github.com/kr/binarydist/patch_test.go
new
+62
@@ -0,0 +1,62 @@
1
+package binarydist
2
+
3
+import (
4
+ "io/ioutil"
5
+ "os"
6
+ "os/exec"
7
+ "testing"
8
+)
9
+
10
+func TestPatch(t *testing.T) {
11
+ mustWriteRandFile("test.old", 1e3)
12
+ mustWriteRandFile("test.new", 1e3)
13
+
14
+ got, err := ioutil.TempFile("/tmp", "bspatch.")
15
+ if err != nil {
16
+ panic(err)
17
+ }
18
+ os.Remove(got.Name())
19
+
20
+ err = exec.Command("bsdiff", "test.old", "test.new", "test.patch").Run()
21
+ if err != nil {
22
+ panic(err)
23
+ }
24
+
25
+ err = Patch(mustOpen("test.old"), got, mustOpen("test.patch"))
26
+ if err != nil {
27
+ t.Fatal("err", err)
28
+ }
29
+
30
+ ref, err := got.Seek(0, 2)
31
+ if err != nil {
32
+ panic(err)
33
+ }
34
+
35
+ t.Logf("got %d bytes", ref)
36
+ if n := fileCmp(got, mustOpen("test.new")); n > -1 {
37
+ t.Fatalf("produced different output at pos %d", n)
38
+ }
39
+}
40
+
41
+func TestPatchHk(t *testing.T) {
42
+ got, err := ioutil.TempFile("/tmp", "bspatch.")
43
+ if err != nil {
44
+ panic(err)
45
+ }
46
+ os.Remove(got.Name())
47
+
48
+ err = Patch(mustOpen("testdata/sample.old"), got, mustOpen("testdata/sample.patch"))
49
+ if err != nil {
50
+ t.Fatal("err", err)
51
+ }
52
+
53
+ ref, err := got.Seek(0, 2)
54
+ if err != nil {
55
+ panic(err)
56
+ }
57
+
58
+ t.Logf("got %d bytes", ref)
59
+ if n := fileCmp(got, mustOpen("testdata/sample.new")); n > -1 {
60
+ t.Fatalf("produced different output at pos %d", n)
61
+ }
62
+}
Godeps/_workspace/src/github.com/kr/binarydist/seek.go
new
+43
@@ -0,0 +1,43 @@
1
+package binarydist
2
+
3
+import (
4
+ "errors"
5
+)
6
+
7
+type seekBuffer struct {
8
+ buf []byte
9
+ pos int
10
+}
11
+
12
+func (b *seekBuffer) Write(p []byte) (n int, err error) {
13
+ n = copy(b.buf[b.pos:], p)
14
+ if n == len(p) {
15
+ b.pos += n
16
+ return n, nil
17
+ }
18
+ b.buf = append(b.buf, p[n:]...)
19
+ b.pos += len(p)
20
+ return len(p), nil
21
+}
22
+
23
+func (b *seekBuffer) Seek(offset int64, whence int) (ret int64, err error) {
24
+ var abs int64
25
+ switch whence {
26
+ case 0:
27
+ abs = offset
28
+ case 1:
29
+ abs = int64(b.pos) + offset
30
+ case 2:
31
+ abs = int64(len(b.buf)) + offset
32
+ default:
33
+ return 0, errors.New("binarydist: invalid whence")
34
+ }
35
+ if abs < 0 {
36
+ return 0, errors.New("binarydist: negative position")
37
+ }
38
+ if abs >= 1<<31 {
39
+ return 0, errors.New("binarydist: position out of range")
40
+ }
41
+ b.pos = int(abs)
42
+ return abs, nil
43
+}
Godeps/_workspace/src/github.com/kr/binarydist/sort_test.go
new
+33
@@ -0,0 +1,33 @@
1
+package binarydist
2
+
3
+import (
4
+ "bytes"
5
+ "crypto/rand"
6
+ "testing"
7
+)
8
+
9
+var sortT = [][]byte{
10
+ mustRandBytes(1000),
11
+ mustReadAll(mustOpen("test.old")),
12
+ []byte("abcdefabcdef"),
13
+}
14
+
15
+func TestQsufsort(t *testing.T) {
16
+ for _, s := range sortT {
17
+ I := qsufsort(s)
18
+ for i := 1; i < len(I); i++ {
19
+ if bytes.Compare(s[I[i-1]:], s[I[i]:]) > 0 {
20
+ t.Fatalf("unsorted at %d", i)
21
+ }
22
+ }
23
+ }
24
+}
25
+
26
+func mustRandBytes(n int) []byte {
27
+ b := make([]byte, n)
28
+ _, err := rand.Read(b)
29
+ if err != nil {
30
+ panic(err)
31
+ }
32
+ return b
33
+}
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new
Binary files /dev/null and b/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new differ
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old
Binary files /dev/null and b/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old differ
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch
Binary files /dev/null and b/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch differ