1
0
mirror of https://github.com/restic/restic.git synced 2024-06-26 07:49:01 +02:00

local: Use Layout for filename generation

This commit is contained in:
Alexander Neumann 2017-03-26 21:53:26 +02:00
parent 6a201f7962
commit 3fd6fa6f86

View File

@ -17,6 +17,7 @@ import (
// Local is a backend in a local directory. // Local is a backend in a local directory.
type Local struct { type Local struct {
Config Config
backend.Layout
} }
var _ restic.Backend = &Local{} var _ restic.Backend = &Local{}
@ -35,35 +36,49 @@ func paths(dir string) []string {
// Open opens the local backend as specified by config. // Open opens the local backend as specified by config.
func Open(cfg Config) (*Local, error) { func Open(cfg Config) (*Local, error) {
be := &Local{Config: cfg}
be.Layout = &backend.DefaultLayout{
Path: cfg.Path,
Join: filepath.Join,
}
// test if all necessary dirs are there // test if all necessary dirs are there
for _, d := range paths(cfg.Path) { for _, d := range be.Paths() {
if _, err := fs.Stat(d); err != nil { if _, err := fs.Stat(d); err != nil {
return nil, errors.Wrap(err, "Open") return nil, errors.Wrap(err, "Open")
} }
} }
return &Local{Config: cfg}, nil return be, nil
} }
// Create creates all the necessary files and directories for a new local // Create creates all the necessary files and directories for a new local
// backend at dir. Afterwards a new config blob should be created. // backend at dir. Afterwards a new config blob should be created.
func Create(cfg Config) (*Local, error) { func Create(cfg Config) (*Local, error) {
be := &Local{
Config: cfg,
Layout: &backend.DefaultLayout{
Path: cfg.Path,
Join: filepath.Join,
},
}
// test if config file already exists // test if config file already exists
_, err := fs.Lstat(filepath.Join(cfg.Path, backend.Paths.Config)) _, err := fs.Lstat(be.Filename(restic.Handle{Type: restic.ConfigFile}))
if err == nil { if err == nil {
return nil, errors.New("config file already exists") return nil, errors.New("config file already exists")
} }
// create paths for data, refs and temp // create paths for data, refs and temp
for _, d := range paths(cfg.Path) { for _, d := range be.Paths() {
err := fs.MkdirAll(d, backend.Modes.Dir) err := fs.MkdirAll(d, backend.Modes.Dir)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "MkdirAll") return nil, errors.Wrap(err, "MkdirAll")
} }
} }
// open backend return be, nil
return Open(cfg)
} }
// Location returns this backend's location (the directory name). // Location returns this backend's location (the directory name).
@ -71,36 +86,6 @@ func (b *Local) Location() string {
return b.Path return b.Path
} }
// Construct path for given Type and name.
func filename(base string, t restic.FileType, name string) string {
if t == restic.ConfigFile {
return filepath.Join(base, "config")
}
return filepath.Join(dirname(base, t, name), name)
}
// Construct directory for given Type.
func dirname(base string, t restic.FileType, name string) string {
var n string
switch t {
case restic.DataFile:
n = backend.Paths.Data
if len(name) > 2 {
n = filepath.Join(n, name[:2])
}
case restic.SnapshotFile:
n = backend.Paths.Snapshots
case restic.IndexFile:
n = backend.Paths.Index
case restic.LockFile:
n = backend.Paths.Locks
case restic.KeyFile:
n = backend.Paths.Keys
}
return filepath.Join(base, n)
}
// copyToTempfile saves p into a tempfile in tempdir. // copyToTempfile saves p into a tempfile in tempdir.
func copyToTempfile(tempdir string, rd io.Reader) (filename string, err error) { func copyToTempfile(tempdir string, rd io.Reader) (filename string, err error) {
tmpfile, err := ioutil.TempFile(tempdir, "temp-") tmpfile, err := ioutil.TempFile(tempdir, "temp-")
@ -132,18 +117,7 @@ func (b *Local) Save(h restic.Handle, rd io.Reader) (err error) {
return err return err
} }
tmpfile, err := copyToTempfile(filepath.Join(b.Path, backend.Paths.Temp), rd) filename := b.Filename(h)
debug.Log("saved %v to %v", h, tmpfile)
if err != nil {
return err
}
filename := filename(b.Path, h.Type, h.Name)
// test if new path already exists
if _, err := fs.Stat(filename); err == nil {
return errors.Errorf("Rename(): file %v already exists", filename)
}
// create directories if necessary, ignore errors // create directories if necessary, ignore errors
if h.Type == restic.DataFile { if h.Type == restic.DataFile {
@ -153,12 +127,28 @@ func (b *Local) Save(h restic.Handle, rd io.Reader) (err error) {
} }
} }
err = fs.Rename(tmpfile, filename) // create new file
debug.Log("save %v: rename %v -> %v: %v", f, err := fs.OpenFile(filename, os.O_CREATE|os.O_EXCL|os.O_WRONLY, backend.Modes.File)
h, filepath.Base(tmpfile), filepath.Base(filename), err)
if err != nil { if err != nil {
return errors.Wrap(err, "Rename") return errors.Wrap(err, "OpenFile")
}
// save data, then sync
_, err = io.Copy(f, rd)
if err != nil {
f.Close()
return errors.Wrap(err, "Write")
}
if err = f.Sync(); err != nil {
f.Close()
return errors.Wrap(err, "Sync")
}
err = f.Close()
if err != nil {
f.Close()
return errors.Wrap(err, "Close")
} }
// set mode to read-only // set mode to read-only
@ -183,7 +173,7 @@ func (b *Local) Load(h restic.Handle, length int, offset int64) (io.ReadCloser,
return nil, errors.New("offset is negative") return nil, errors.New("offset is negative")
} }
f, err := os.Open(filename(b.Path, h.Type, h.Name)) f, err := fs.Open(b.Filename(h))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -210,7 +200,7 @@ func (b *Local) Stat(h restic.Handle) (restic.FileInfo, error) {
return restic.FileInfo{}, err return restic.FileInfo{}, err
} }
fi, err := fs.Stat(filename(b.Path, h.Type, h.Name)) fi, err := fs.Stat(b.Filename(h))
if err != nil { if err != nil {
return restic.FileInfo{}, errors.Wrap(err, "Stat") return restic.FileInfo{}, errors.Wrap(err, "Stat")
} }
@ -221,7 +211,7 @@ func (b *Local) Stat(h restic.Handle) (restic.FileInfo, error) {
// Test returns true if a blob of the given type and name exists in the backend. // Test returns true if a blob of the given type and name exists in the backend.
func (b *Local) Test(h restic.Handle) (bool, error) { func (b *Local) Test(h restic.Handle) (bool, error) {
debug.Log("Test %v", h) debug.Log("Test %v", h)
_, err := fs.Stat(filename(b.Path, h.Type, h.Name)) _, err := fs.Stat(b.Filename(h))
if err != nil { if err != nil {
if os.IsNotExist(errors.Cause(err)) { if os.IsNotExist(errors.Cause(err)) {
return false, nil return false, nil
@ -235,7 +225,7 @@ func (b *Local) Test(h restic.Handle) (bool, error) {
// Remove removes the blob with the given name and type. // Remove removes the blob with the given name and type.
func (b *Local) Remove(h restic.Handle) error { func (b *Local) Remove(h restic.Handle) error {
debug.Log("Remove %v", h) debug.Log("Remove %v", h)
fn := filename(b.Path, h.Type, h.Name) fn := b.Filename(h)
// reset read-only flag // reset read-only flag
err := fs.Chmod(fn, 0666) err := fs.Chmod(fn, 0666)
@ -316,7 +306,7 @@ func (b *Local) List(t restic.FileType, done <-chan struct{}) <-chan string {
} }
ch := make(chan string) ch := make(chan string)
items, err := lister(filepath.Join(dirname(b.Path, t, ""))) items, err := lister(b.Dirname(restic.Handle{Type: t}))
if err != nil { if err != nil {
close(ch) close(ch)
return ch return ch