2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-22 12:55:18 +00:00

Add method to create repository

Also disables automatic creation on open
This commit is contained in:
Alexander Neumann 2014-09-23 21:16:54 +02:00
parent f0287b2c9a
commit 03ca69407d
5 changed files with 145 additions and 35 deletions

20
cmd/khepri/cmd_init.go Normal file
View File

@ -0,0 +1,20 @@
package main
import (
"fmt"
"os"
"github.com/fd0/khepri"
)
func commandInit(path string) error {
repo, err := khepri.CreateRepository(path)
if err != nil {
fmt.Fprintf(os.Stderr, "creating repository at %s failed: %v\n", path, err)
os.Exit(1)
}
fmt.Printf("created khepri repository at %s\n", repo.Path())
return nil
}

View File

@ -4,15 +4,13 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"sort"
"strings"
"github.com/fd0/khepri" "github.com/fd0/khepri"
"github.com/jessevdk/go-flags" "github.com/jessevdk/go-flags"
) )
var Opts struct { var Opts struct {
Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restor from"` Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"`
} }
func errx(code int, format string, data ...interface{}) { func errx(code int, format string, data ...interface{}) {
@ -50,17 +48,16 @@ func main() {
os.Exit(0) os.Exit(0)
} }
if len(args) == 0 { cmd := args[0]
cmds := []string{}
for k := range commands { if cmd == "init" {
cmds = append(cmds, k) err = commandInit(Opts.Repo)
} if err != nil {
sort.Strings(cmds) errx(1, "error executing command %q: %v", cmd, err)
fmt.Printf("nothing to do, available commands: [%v]\n", strings.Join(cmds, "|"))
os.Exit(0)
} }
cmd := args[0] return
}
f, ok := commands[cmd] f, ok := commands[cmd]
if !ok { if !ok {
@ -70,7 +67,7 @@ func main() {
repo, err := khepri.NewRepository(Opts.Repo) repo, err := khepri.NewRepository(Opts.Repo)
if err != nil { if err != nil {
errx(1, "unable to create/open repo: %v", err) errx(1, "unable to open repo: %v", err)
} }
err = f(repo, args[1:]) err = f(repo, args[1:])

View File

@ -1,7 +1,10 @@
package khepri package khepri
import ( import (
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/hex"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"hash" "hash"
@ -18,6 +21,7 @@ const (
blobPath = "blobs" blobPath = "blobs"
refPath = "refs" refPath = "refs"
tempPath = "tmp" tempPath = "tmp"
configFileName = "config.json"
) )
var ( var (
@ -36,8 +40,25 @@ type HashFunc func() hash.Hash
type Repository struct { type Repository struct {
path string path string
hash HashFunc hash HashFunc
config *Config
} }
type Config struct {
Salt string
N uint
R uint `json:"r"`
P uint `json:"p"`
}
// TODO: figure out scrypt values on the fly depending on the current
// hardware.
const (
scrypt_N = 65536
scrypt_r = 8
scrypt_p = 1
scrypt_saltsize = 64
)
type Type int type Type int
const ( const (
@ -67,15 +88,16 @@ func (t Type) String() string {
panic(fmt.Sprintf("unknown type %d", t)) panic(fmt.Sprintf("unknown type %d", t))
} }
// NewDirRepository creates a new dir-baked repository at the given path. // NewRepository opens a dir-baked repository at the given path.
func NewRepository(path string) (*Repository, error) { func NewRepository(path string) (*Repository, error) {
var err error
d := &Repository{ d := &Repository{
path: path, path: path,
hash: sha256.New, hash: sha256.New,
} }
err := d.create() d.config, err = d.read_config()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,22 +105,92 @@ func NewRepository(path string) (*Repository, error) {
return d, nil return d, nil
} }
func (r *Repository) create() error { func (r *Repository) read_config() (*Config, error) {
dirs := []string{ // try to open config file
r.path, f, err := os.Open(path.Join(r.path, configFileName))
path.Join(r.path, blobPath), if err != nil {
path.Join(r.path, refPath), return nil, err
path.Join(r.path, tempPath),
} }
cfg := new(Config)
buf, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, cfg)
if err != nil {
return nil, err
}
return cfg, nil
}
// CreateRepository creates all the necessary files and directories for the
// Repository.
func CreateRepository(p string) (*Repository, error) {
dirs := []string{
p,
path.Join(p, blobPath),
path.Join(p, refPath),
path.Join(p, tempPath),
}
var configfile = path.Join(p, configFileName)
// test if repository directories or config file already exist
if _, err := os.Stat(configfile); err == nil {
return nil, fmt.Errorf("config file %s already exists", configfile)
}
for _, d := range dirs[1:] {
if _, err := os.Stat(d); err == nil {
return nil, fmt.Errorf("dir %s already exists", d)
}
}
// create initial json configuration
cfg := &Config{
N: scrypt_N,
R: scrypt_r,
P: scrypt_p,
}
// generate salt
buf := make([]byte, scrypt_saltsize)
n, err := rand.Read(buf)
if n != scrypt_saltsize || err != nil {
panic("unable to read enough random bytes for salt")
}
cfg.Salt = hex.EncodeToString(buf)
// create ps for blobs, refs and temp
for _, dir := range dirs { for _, dir := range dirs {
err := os.MkdirAll(dir, dirMode) err := os.MkdirAll(dir, dirMode)
if err != nil { if err != nil {
return err return nil, err
} }
} }
return nil // write config file
f, err := os.Create(configfile)
defer f.Close()
if err != nil {
return nil, err
}
s, err := json.Marshal(cfg)
if err != nil {
return nil, err
}
_, err = f.Write(s)
if err != nil {
return nil, err
}
// open repository
return NewRepository(p)
} }
// SetHash changes the hash function used for deriving IDs. Default is SHA256. // SetHash changes the hash function used for deriving IDs. Default is SHA256.

View File

@ -30,7 +30,7 @@ func setupRepo() (*khepri.Repository, error) {
return nil, err return nil, err
} }
repo, err := khepri.NewRepository(tempdir) repo, err := khepri.CreateRepository(tempdir)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,7 @@
set -e set -e
prepare prepare
run khepri init
run khepri backup "${BASE}/fake-data" run khepri backup "${BASE}/fake-data"
run khepri restore "$(khepri list ref)" "${BASE}/fake-data-restore" run khepri restore "$(khepri list ref)" "${BASE}/fake-data-restore"
dirdiff "${BASE}/fake-data" "${BASE}/fake-data-restore" dirdiff "${BASE}/fake-data" "${BASE}/fake-data-restore"