mirror of
https://github.com/octoleo/restic.git
synced 2024-11-16 01:57:10 +00:00
local: Use Layout for filename generation
This commit is contained in:
parent
6a201f7962
commit
3fd6fa6f86
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user