@cryptotaxi247 / kubo / commits / deb9aebc4

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 +[![GoDoc](https://godoc.org/github.com/inconshreveable/go-update?status.svg)](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