aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rw-r--r--cache/cache.go114
-rw-r--r--config/config.go4
-rw-r--r--main.go23
-rw-r--r--snapshot.go3
-rw-r--r--write_dir.go3
6 files changed, 146 insertions, 4 deletions
diff --git a/README.md b/README.md
index db923fc..330e913 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,9 @@ Here is a commented example config. For more details, consult the documentation
# This config key defines the default storage backend, as defined below
default_storage = "local_encrypted"
+ # This defines the location of the cache file (can speed up creating backups; can be left out)
+ cache_path = "~/.cache/petrific.cache"
+
[signing]
# Use this GPG key to sign snapshots
key = "0123456789ABCDEF0123456789ABCDEF01234567"
diff --git a/cache/cache.go b/cache/cache.go
index 32b8325..2d20087 100644
--- a/cache/cache.go
+++ b/cache/cache.go
@@ -1,13 +1,20 @@
package cache
import (
+ "bufio"
"code.laria.me/petrific/objects"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
"time"
)
type Cache interface {
PathUpdated(path string) (mtime time.Time, id objects.ObjectId, ok bool)
SetPathUpdated(path string, mtime time.Time, id objects.ObjectId)
+ Close() error
}
type NopCache struct{}
@@ -18,3 +25,110 @@ func (NopCache) PathUpdated(_ string) (_ time.Time, _ objects.ObjectId, ok bool)
}
func (NopCache) SetPathUpdated(_ string, _ time.Time, _ objects.ObjectId) {}
+
+func (NopCache) Close() error { return nil }
+
+type fileCacheEntry struct {
+ mtime time.Time
+ id objects.ObjectId
+}
+
+type FileCache struct {
+ cache map[string]fileCacheEntry
+ location string
+}
+
+func (fc FileCache) PathUpdated(path string) (time.Time, objects.ObjectId, bool) {
+ entry, ok := fc.cache[path]
+ return entry.mtime, entry.id, ok
+}
+
+func (fc FileCache) SetPathUpdated(path string, mtime time.Time, id objects.ObjectId) {
+ fc.cache[path] = fileCacheEntry{mtime, id}
+}
+
+func NewFileCache(location string) FileCache {
+ return FileCache{make(map[string]fileCacheEntry), location}
+}
+
+func escapeName(name string) string {
+ name = strings.Replace(name, "\\", "\\\\", -1)
+ name = strings.Replace(name, "\n", "\\n", -1)
+ return name
+}
+
+func unescapeName(name string) string {
+ name = strings.Replace(name, "\\n", "\n", -1)
+ name = strings.Replace(name, "\\\\", "\\", -1)
+ return name
+}
+
+func (fc FileCache) dump(w io.Writer) error {
+ for path, entry := range fc.cache {
+ if _, err := fmt.Fprintf(
+ w,
+ "%s %d %d %s\n",
+ entry.id,
+ entry.mtime.Unix(),
+ entry.mtime.Nanosecond(),
+ escapeName(path),
+ ); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (fc FileCache) load(r io.Reader) error {
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ parts := strings.SplitN(scanner.Text(), " ", 4)
+ if len(parts) != 4 {
+ return fmt.Errorf("Could not load FileCache: Expected 4 entries, got %d", len(parts))
+ }
+
+ id, err := objects.ParseObjectId(parts[0])
+ if err != nil {
+ return err
+ }
+
+ sec, err := strconv.ParseInt(parts[1], 10, 64)
+ if err != nil {
+ return err
+ }
+
+ nsec, err := strconv.ParseInt(parts[2], 10, 64)
+ if err != nil {
+ return err
+ }
+
+ fc.cache[unescapeName(parts[3])] = fileCacheEntry{time.Unix(sec, nsec), id}
+ }
+
+ return scanner.Err()
+}
+
+func (fc FileCache) Load() error {
+ f, err := os.Open(fc.location)
+ switch {
+ case os.IsNotExist(err):
+ return nil
+ case err != nil:
+ return err
+ default:
+ }
+ defer f.Close()
+
+ return fc.load(f)
+}
+
+func (fc FileCache) Close() error {
+ f, err := os.Create(fc.location)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ return fc.dump(f)
+}
diff --git a/config/config.go b/config/config.go
index b643477..7deef69 100644
--- a/config/config.go
+++ b/config/config.go
@@ -11,6 +11,9 @@
// # This config key defines the default storage backend, as defined below ([storage.local_compressed])
// default_storage = "local_compressed"
//
+// # This defines the location of the cache file (can speed up creating backups; can be left out)
+// cache_path = "~/.cache/petrific.cache"
+//
// [signing]
// # Use this GPG key to sign snapshots
// key = "0123456789ABCDEF0123456789ABCDEF01234567"
@@ -55,6 +58,7 @@ var (
)
type Config struct {
+ CachePath string `toml:"cache_path,omitempty"`
DefaultStorage string `toml:"default_storage"`
Signing struct {
Key string
diff --git a/main.go b/main.go
index 11202f3..5ea627f 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
package main
import (
+ "code.laria.me/petrific/cache"
"code.laria.me/petrific/config"
"code.laria.me/petrific/storage"
"code.laria.me/petrific/storage/registry"
@@ -44,6 +45,7 @@ var (
var conf config.Config
var objectstore storage.Storage
+var id_cache cache.Cache = cache.NopCache{}
func main() {
os.Exit(Main())
@@ -64,6 +66,11 @@ func Main() int {
}
defer objectstore.Close()
+ if !loadCache(conf) {
+ return 1
+ }
+ defer id_cache.Close()
+
remaining := make([]string, 0)
for _, arg := range flag.Args() {
if arg != "" {
@@ -84,6 +91,22 @@ func Main() int {
return cmd(remaining[1:])
}
+func loadCache(conf config.Config) bool {
+ if conf.CachePath == "" {
+ return true
+ }
+
+ file_cache := cache.NewFileCache(config.ExpandTilde(conf.CachePath))
+ if err := file_cache.Load(); err != nil {
+ fmt.Fprintf(os.Stderr, "Loading cache %s: %s", conf.CachePath, err)
+ return false
+ }
+
+ id_cache = file_cache
+
+ return true
+}
+
func loadConfig() bool {
var err error
conf, err = config.LoadConfig(*flagConfPath)
diff --git a/snapshot.go b/snapshot.go
index 844a85a..db181ba 100644
--- a/snapshot.go
+++ b/snapshot.go
@@ -2,7 +2,6 @@ package main
import (
"code.laria.me/petrific/backup"
- "code.laria.me/petrific/cache"
"code.laria.me/petrific/fs"
"code.laria.me/petrific/gpg"
"code.laria.me/petrific/objects"
@@ -115,7 +114,7 @@ func TakeSnapshot(args []string) int {
return 1
}
- tree_id, err := backup.WriteDir(objectstore, dir_path, d, cache.NopCache{})
+ tree_id, err := backup.WriteDir(objectstore, dir_path, d, id_cache)
if err != nil {
errout(err)
return 1
diff --git a/write_dir.go b/write_dir.go
index 34abcd1..3ee5a97 100644
--- a/write_dir.go
+++ b/write_dir.go
@@ -2,7 +2,6 @@ package main
import (
"code.laria.me/petrific/backup"
- "code.laria.me/petrific/cache"
"code.laria.me/petrific/fs"
"fmt"
"os"
@@ -46,7 +45,7 @@ func WriteDir(args []string) int {
return 1
}
- id, err := backup.WriteDir(objectstore, dir_path, d, cache.NopCache{})
+ id, err := backup.WriteDir(objectstore, dir_path, d, id_cache)
if err != nil {
errout(err)
return 1