mirror of
https://github.com/restic/restic.git
synced 2024-09-12 22:18:09 +02:00
6e1a3987b7
This commit adds a command called `self-update` which downloads the latest released version of restic from GitHub and replacing the current binary with it. It does not rely on any external program (so it'll work everywhere), but still verifies the GPG signature using the embedded GPG public key. By default, the `self-update` command is hidden behind the `selfupdate` built tag, which is only set when restic is built using `build.go`. The reason for this is that downstream distributions will then not include the command by default, so users are encouraged to use the platform-specific distribution mechanism.
161 lines
3.3 KiB
Go
161 lines
3.3 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package armor
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"io"
|
|
)
|
|
|
|
var armorHeaderSep = []byte(": ")
|
|
var blockEnd = []byte("\n=")
|
|
var newline = []byte("\n")
|
|
var armorEndOfLineOut = []byte("-----\n")
|
|
|
|
// writeSlices writes its arguments to the given Writer.
|
|
func writeSlices(out io.Writer, slices ...[]byte) (err error) {
|
|
for _, s := range slices {
|
|
_, err = out.Write(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// lineBreaker breaks data across several lines, all of the same byte length
|
|
// (except possibly the last). Lines are broken with a single '\n'.
|
|
type lineBreaker struct {
|
|
lineLength int
|
|
line []byte
|
|
used int
|
|
out io.Writer
|
|
haveWritten bool
|
|
}
|
|
|
|
func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
|
|
return &lineBreaker{
|
|
lineLength: lineLength,
|
|
line: make([]byte, lineLength),
|
|
used: 0,
|
|
out: out,
|
|
}
|
|
}
|
|
|
|
func (l *lineBreaker) Write(b []byte) (n int, err error) {
|
|
n = len(b)
|
|
|
|
if n == 0 {
|
|
return
|
|
}
|
|
|
|
if l.used == 0 && l.haveWritten {
|
|
_, err = l.out.Write([]byte{'\n'})
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if l.used+len(b) < l.lineLength {
|
|
l.used += copy(l.line[l.used:], b)
|
|
return
|
|
}
|
|
|
|
l.haveWritten = true
|
|
_, err = l.out.Write(l.line[0:l.used])
|
|
if err != nil {
|
|
return
|
|
}
|
|
excess := l.lineLength - l.used
|
|
l.used = 0
|
|
|
|
_, err = l.out.Write(b[0:excess])
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = l.Write(b[excess:])
|
|
return
|
|
}
|
|
|
|
func (l *lineBreaker) Close() (err error) {
|
|
if l.used > 0 {
|
|
_, err = l.out.Write(l.line[0:l.used])
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// encoding keeps track of a running CRC24 over the data which has been written
|
|
// to it and outputs a OpenPGP checksum when closed, followed by an armor
|
|
// trailer.
|
|
//
|
|
// It's built into a stack of io.Writers:
|
|
// encoding -> base64 encoder -> lineBreaker -> out
|
|
type encoding struct {
|
|
out io.Writer
|
|
breaker *lineBreaker
|
|
b64 io.WriteCloser
|
|
crc uint32
|
|
blockType []byte
|
|
}
|
|
|
|
func (e *encoding) Write(data []byte) (n int, err error) {
|
|
e.crc = crc24(e.crc, data)
|
|
return e.b64.Write(data)
|
|
}
|
|
|
|
func (e *encoding) Close() (err error) {
|
|
err = e.b64.Close()
|
|
if err != nil {
|
|
return
|
|
}
|
|
e.breaker.Close()
|
|
|
|
var checksumBytes [3]byte
|
|
checksumBytes[0] = byte(e.crc >> 16)
|
|
checksumBytes[1] = byte(e.crc >> 8)
|
|
checksumBytes[2] = byte(e.crc)
|
|
|
|
var b64ChecksumBytes [4]byte
|
|
base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
|
|
|
|
return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
|
|
}
|
|
|
|
// Encode returns a WriteCloser which will encode the data written to it in
|
|
// OpenPGP armor.
|
|
func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
|
|
bType := []byte(blockType)
|
|
err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for k, v := range headers {
|
|
err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
_, err = out.Write(newline)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
e := &encoding{
|
|
out: out,
|
|
breaker: newLineBreaker(out, 64),
|
|
crc: crc24Init,
|
|
blockType: bType,
|
|
}
|
|
e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
|
|
return e, nil
|
|
}
|