2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-21 20:35:12 +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"
"log"
"os"
"sort"
"strings"
"github.com/fd0/khepri"
"github.com/jessevdk/go-flags"
)
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{}) {
@ -50,18 +48,17 @@ func main() {
os.Exit(0)
}
if len(args) == 0 {
cmds := []string{}
for k := range commands {
cmds = append(cmds, k)
}
sort.Strings(cmds)
fmt.Printf("nothing to do, available commands: [%v]\n", strings.Join(cmds, "|"))
os.Exit(0)
}
cmd := args[0]
if cmd == "init" {
err = commandInit(Opts.Repo)
if err != nil {
errx(1, "error executing command %q: %v", cmd, err)
}
return
}
f, ok := commands[cmd]
if !ok {
errx(1, "unknown command: %q\n", cmd)
@ -70,7 +67,7 @@ func main() {
repo, err := khepri.NewRepository(Opts.Repo)
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:])

View File

@ -1,7 +1,10 @@
package khepri
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"hash"
@ -14,10 +17,11 @@ import (
)
const (
dirMode = 0700
blobPath = "blobs"
refPath = "refs"
tempPath = "tmp"
dirMode = 0700
blobPath = "blobs"
refPath = "refs"
tempPath = "tmp"
configFileName = "config.json"
)
var (
@ -34,10 +38,27 @@ func (n Name) Encode() string {
type HashFunc func() hash.Hash
type Repository struct {
path string
hash HashFunc
path string
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
const (
@ -67,15 +88,16 @@ func (t Type) String() string {
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) {
var err error
d := &Repository{
path: path,
hash: sha256.New,
}
err := d.create()
d.config, err = d.read_config()
if err != nil {
return nil, err
}
@ -83,22 +105,92 @@ func NewRepository(path string) (*Repository, error) {
return d, nil
}
func (r *Repository) create() error {
dirs := []string{
r.path,
path.Join(r.path, blobPath),
path.Join(r.path, refPath),
path.Join(r.path, tempPath),
func (r *Repository) read_config() (*Config, error) {
// try to open config file
f, err := os.Open(path.Join(r.path, configFileName))
if err != nil {
return nil, err
}
for _, dir := range dirs {
err := os.MkdirAll(dir, dirMode)
if err != nil {
return err
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)
}
}
return nil
// 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 {
err := os.MkdirAll(dir, dirMode)
if err != nil {
return nil, err
}
}
// 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.

View File

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

View File

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