diff --git a/src/restic/cache.go b/src/restic/cache.go deleted file mode 100644 index 1af4e9605..000000000 --- a/src/restic/cache.go +++ /dev/null @@ -1,290 +0,0 @@ -package restic - -import ( - "io" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/pkg/errors" - - "restic/backend" - "restic/debug" - "restic/fs" - "restic/repository" -) - -// Cache is used to locally cache items from a repository. -type Cache struct { - base string -} - -// NewCache returns a new cache at cacheDir. If it is the empty string, the -// default cache location is chosen. -func NewCache(repo *repository.Repository, cacheDir string) (*Cache, error) { - var err error - - if cacheDir == "" { - cacheDir, err = getCacheDir() - if err != nil { - return nil, err - } - } - - basedir := filepath.Join(cacheDir, repo.Config.ID) - debug.Log("Cache.New", "opened cache at %v", basedir) - - return &Cache{base: basedir}, nil -} - -// Has checks if the local cache has the id. -func (c *Cache) Has(t backend.Type, subtype string, id backend.ID) (bool, error) { - filename, err := c.filename(t, subtype, id) - if err != nil { - return false, err - } - fd, err := fs.Open(filename) - defer fd.Close() - - if err != nil { - if os.IsNotExist(errors.Cause(err)) { - debug.Log("Cache.Has", "test for file %v: not cached", filename) - return false, nil - } - - debug.Log("Cache.Has", "test for file %v: error %v", filename, err) - return false, errors.Wrap(err, "Open") - } - - debug.Log("Cache.Has", "test for file %v: is cached", filename) - return true, nil -} - -// Store returns an io.WriteCloser that is used to save new information to the -// cache. The returned io.WriteCloser must be closed by the caller after all -// data has been written. -func (c *Cache) Store(t backend.Type, subtype string, id backend.ID) (io.WriteCloser, error) { - filename, err := c.filename(t, subtype, id) - if err != nil { - return nil, err - } - - dirname := filepath.Dir(filename) - err = fs.MkdirAll(dirname, 0700) - if err != nil { - return nil, errors.Wrap(err, "MkdirAll") - } - - file, err := fs.Create(filename) - if err != nil { - debug.Log("Cache.Store", "error creating file %v: %v", filename, err) - return nil, errors.Wrap(err, "Create") - } - - debug.Log("Cache.Store", "created file %v", filename) - return file, nil -} - -// Load returns information from the cache. The returned io.ReadCloser must be -// closed by the caller. -func (c *Cache) Load(t backend.Type, subtype string, id backend.ID) (io.ReadCloser, error) { - filename, err := c.filename(t, subtype, id) - if err != nil { - return nil, err - } - - return fs.Open(filename) -} - -func (c *Cache) purge(t backend.Type, subtype string, id backend.ID) error { - filename, err := c.filename(t, subtype, id) - if err != nil { - return err - } - - err = fs.Remove(filename) - debug.Log("Cache.purge", "Remove file %v: %v", filename, err) - - if err != nil && os.IsNotExist(errors.Cause(err)) { - return nil - } - - return errors.Wrap(err, "Remove") -} - -// Clear removes information from the cache that isn't present in the repository any more. -func (c *Cache) Clear(repo *repository.Repository) error { - list, err := c.list(backend.Snapshot) - if err != nil { - return err - } - - for _, entry := range list { - debug.Log("Cache.Clear", "found entry %v", entry) - - if ok, err := repo.Backend().Test(backend.Snapshot, entry.ID.String()); !ok || err != nil { - debug.Log("Cache.Clear", "snapshot %v doesn't exist any more, removing %v", entry.ID, entry) - - err = c.purge(backend.Snapshot, entry.Subtype, entry.ID) - if err != nil { - return err - } - } - } - - return nil -} - -type cacheEntry struct { - ID backend.ID - Subtype string -} - -func (c cacheEntry) String() string { - if c.Subtype != "" { - return c.ID.Str() + "." + c.Subtype - } - return c.ID.Str() -} - -func (c *Cache) list(t backend.Type) ([]cacheEntry, error) { - var dir string - - switch t { - case backend.Snapshot: - dir = filepath.Join(c.base, "snapshots") - default: - return nil, errors.Errorf("cache not supported for type %v", t) - } - - fd, err := fs.Open(dir) - if err != nil { - if os.IsNotExist(errors.Cause(err)) { - return []cacheEntry{}, nil - } - return nil, errors.Wrap(err, "Open") - } - defer fd.Close() - - fis, err := fd.Readdir(-1) - if err != nil { - return nil, errors.Wrap(err, "Readdir") - } - - entries := make([]cacheEntry, 0, len(fis)) - - for _, fi := range fis { - parts := strings.SplitN(fi.Name(), ".", 2) - - id, err := backend.ParseID(parts[0]) - // ignore invalid cache entries for now - if err != nil { - debug.Log("Cache.List", "unable to parse name %v as id: %v", parts[0], err) - continue - } - - e := cacheEntry{ID: id} - - if len(parts) == 2 { - e.Subtype = parts[1] - } - - entries = append(entries, e) - } - - return entries, nil -} - -func (c *Cache) filename(t backend.Type, subtype string, id backend.ID) (string, error) { - filename := id.String() - if subtype != "" { - filename += "." + subtype - } - - switch t { - case backend.Snapshot: - return filepath.Join(c.base, "snapshots", filename), nil - } - - return "", errors.Errorf("cache not supported for type %v", t) -} - -func getCacheDir() (string, error) { - if dir := os.Getenv("RESTIC_CACHE"); dir != "" { - return dir, nil - } - if runtime.GOOS == "windows" { - return getWindowsCacheDir() - } - - return getXDGCacheDir() -} - -// getWindowsCacheDir will return %APPDATA%\restic or create -// a folder in the temporary folder called "restic". -func getWindowsCacheDir() (string, error) { - cachedir := os.Getenv("APPDATA") - if cachedir == "" { - cachedir = os.TempDir() - } - cachedir = filepath.Join(cachedir, "restic") - fi, err := fs.Stat(cachedir) - - if os.IsNotExist(errors.Cause(err)) { - err = fs.MkdirAll(cachedir, 0700) - if err != nil { - return "", errors.Wrap(err, "MkdirAll") - } - - return cachedir, nil - } - - if err != nil { - return "", errors.Wrap(err, "Stat") - } - - if !fi.IsDir() { - return "", errors.Errorf("cache dir %v is not a directory", cachedir) - } - return cachedir, nil -} - -// getXDGCacheDir returns the cache directory according to XDG basedir spec, see -// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html -func getXDGCacheDir() (string, error) { - xdgcache := os.Getenv("XDG_CACHE_HOME") - home := os.Getenv("HOME") - - if xdgcache == "" && home == "" { - return "", errors.New("unable to locate cache directory (XDG_CACHE_HOME and HOME unset)") - } - - cachedir := "" - if xdgcache != "" { - cachedir = filepath.Join(xdgcache, "restic") - } else if home != "" { - cachedir = filepath.Join(home, ".cache", "restic") - } - - fi, err := fs.Stat(cachedir) - if os.IsNotExist(errors.Cause(err)) { - err = fs.MkdirAll(cachedir, 0700) - if err != nil { - return "", errors.Wrap(err, "MkdirAll") - } - - fi, err = fs.Stat(cachedir) - debug.Log("getCacheDir", "create cache dir %v", cachedir) - } - - if err != nil { - return "", errors.Wrap(err, "Stat") - } - - if !fi.IsDir() { - return "", errors.Errorf("cache dir %v is not a directory", cachedir) - } - - return cachedir, nil -} diff --git a/src/restic/cache_test.go b/src/restic/cache_test.go deleted file mode 100644 index c72b26e2a..000000000 --- a/src/restic/cache_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package restic_test - -import ( - "testing" - - "restic" - . "restic/test" -) - -func TestCache(t *testing.T) { - repo := SetupRepo() - defer TeardownRepo(repo) - - _, err := restic.NewCache(repo, "") - OK(t, err) - - arch := restic.NewArchiver(repo) - - // archive some files, this should automatically cache all blobs from the snapshot - _, _, err = arch.Snapshot(nil, []string{BenchArchiveDirectory}, nil) - if err != nil { - t.Fatal(err) - } - - // TODO: test caching index -}