aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/cat.go54
-rw-r--r--cmd/list.go20
-rw-r--r--snap/fs.go147
-rw-r--r--snap/parsing.go3
4 files changed, 214 insertions, 10 deletions
diff --git a/cmd/cat.go b/cmd/cat.go
index d5a275d..186f7da 100644
--- a/cmd/cat.go
+++ b/cmd/cat.go
@@ -3,19 +3,63 @@ package cmd
import (
"fmt"
"golang.flu0r1ne.net/zfdiff/snap"
+ "os"
+ "io"
)
func Cat(params []string) {
- if(len(params) == 0) {
+ n_params := len(params)
+
+ if(n_params == 0) {
die.Fatal("Reference file is required")
}
- if(len(params) > 3) {
+ if(n_params > 3) {
die.Fatal("Too many arguments provided")
}
- snapRef := snap.ToRelative(params[0])
- reference := params[1]
+ var reference string
+ snapish := ""
+
+ if(n_params == 1) {
+ reference = params[0]
+ } else {
+ snapish, reference = params[0], params[1]
+ }
+
+ snapRef := snap.ToRelative(snapish)
+
+ oracle := snap.GetOracle(reference)
+
+ path := oracle.ResolveRelative(snapRef)
+
+ file, err := os.Open(path)
+
+ if err != nil {
+ die.Fatal("Could not open snapshot %s\nError: %v", path, err)
+ }
+
+ defer func() {
+ if err = file.Close(); err != nil {
+ die.Fatal("Could not close file %s\nError: %v", path, err)
+ }
+ }()
+
+ buf := make([]byte, 10*1024*1024) //10Ki
+
+ for {
+ n, err := file.Read(buf)
+
+ if n > 0 {
+ fmt.Printf("%s", buf[:n])
+ }
+
+ if err == io.EOF {
+ break
+ }
- fmt.Println(snapRef, reference);
+ if err != nil {
+ die.Fatal("Encountered error while reading file: %v", err)
+ }
+ }
} \ No newline at end of file
diff --git a/cmd/list.go b/cmd/list.go
index 99d5f24..5849975 100644
--- a/cmd/list.go
+++ b/cmd/list.go
@@ -3,13 +3,14 @@ package cmd
import (
"fmt"
"flag"
+ "golang.flu0r1ne.net/zfdiff/snap"
)
func List(params []string) {
flags := flag.NewFlagSet("list", flag.ExitOnError)
var withPaths bool
-
+
withName := aliasedBoolVar(
flags,
&withPaths,
@@ -32,6 +33,19 @@ func List(params []string) {
reference := flags.Arg(0)
- fmt.Printf(reference)
- fmt.Printf("Your flag is: %t", withPaths);
+ snaps := snap.GetTimeseries(reference)
+
+ for _, s := range snaps {
+ if s.Reference == "" {
+ continue
+ }
+
+ fmt.Printf("%s", s.Name)
+
+ if withPaths {
+ fmt.Printf(", %s", s.Reference)
+ }
+
+ fmt.Println("")
+ }
}
diff --git a/snap/fs.go b/snap/fs.go
new file mode 100644
index 0000000..d0dc83d
--- /dev/null
+++ b/snap/fs.go
@@ -0,0 +1,147 @@
+package snap
+
+import (
+ "time"
+ "path/filepath"
+ "os"
+ "sort"
+)
+
+type Snapshot struct {
+ Name string
+ ModTime time.Time
+ Reference string
+}
+
+func getZFSRoot(reference string) string {
+ cursor := filepath.Dir(reference)
+ zfsNotFound := false
+
+ for !zfsNotFound {
+ zfsDir := filepath.Join(cursor, ".zfs")
+
+ if _, err := os.Stat(zfsDir); !os.IsNotExist(err) {
+ break
+ }
+
+ cursor = filepath.Join(cursor, "..")
+
+ zfsNotFound = filepath.ToSlash(cursor) == "/"
+ }
+
+ if(zfsNotFound) {
+ die.Fatal("Could not find a .zfs directory. Are you within a zfs dataset?")
+ }
+
+ return cursor
+}
+
+func getSnapshots(reference string) []*Snapshot {
+ zfsRoot := getZFSRoot(reference)
+
+ snapshotDir := filepath.Join(zfsRoot, ".zfs", "snapshot");
+
+ files, err := os.ReadDir(snapshotDir)
+ if err != nil {
+ die.Fatalf("Could not find the snapshots directory inside %s", snapshotDir);
+ }
+
+ relPath, err := filepath.Rel(zfsRoot, reference)
+ if err != nil {
+ die.Fatal("Could not find relative path to reference file: %v", err)
+ }
+
+ snaps := make([]*Snapshot, len(files))
+
+ for i, file := range files {
+
+ info, err := file.Info()
+ if(err != nil) {
+ die.Fatalf("Could not read file %v", err)
+ }
+
+ name := file.Name()
+ snaps[i] = &Snapshot{
+ Name: name,
+ ModTime: info.ModTime(),
+ }
+
+ pathInSnap := filepath.Join(snapshotDir, name, relPath)
+
+ if _, err := os.Stat(pathInSnap); err != nil {
+ if !os.IsNotExist(err) {
+ die.Fatalf("Error encountered while attempting to read reference file in snapshot:\n%v", err)
+ }
+ } else {
+ snaps[i].Reference = pathInSnap
+ }
+
+ }
+
+ return snaps
+}
+
+func GetTimeseries(reference string) []*Snapshot {
+ reference, err := filepath.Abs(reference)
+
+ if err != nil {
+ die.Fatal(err)
+ }
+
+ snaps := getSnapshots(reference)
+
+ sort.Slice(snaps, func(i, j int) bool {
+ return snaps[i].ModTime.Before(snaps[j].ModTime)
+ })
+
+ return snaps
+}
+
+func buildNameMap(snapTs []*Snapshot) map[string] int {
+ namemap := make(map[string] int)
+
+ for i, snap := range snapTs {
+ namemap[snap.Name] = i
+ }
+
+ return namemap
+}
+
+type SnapshotOracle struct {
+ timeseries []*Snapshot
+ namemap map[string] int
+};
+
+func GetOracle(reference string) *SnapshotOracle {
+ timeseries := GetTimeseries(reference)
+ namemap := buildNameMap(timeseries)
+
+
+ return &SnapshotOracle {
+ timeseries,
+ namemap,
+ }
+}
+
+func (oracle *SnapshotOracle) ResolveRelative(ref *Relative) string {
+ n_ts := len(oracle.timeseries)
+ wanted := n_ts - 1
+
+ if ref.snapshot != "" {
+ id, ok := oracle.namemap[ref.snapshot]
+
+ if !ok {
+ die.Fatal("Could not find the snapshot %s", ref.snapshot)
+ }
+
+ wanted = id
+ }
+
+ wanted += ref.offset
+
+ if wanted < 0 || wanted >= n_ts {
+ die.Fatal("The snapshot you requested is out of range.")
+ }
+
+ return oracle.timeseries[wanted].Reference
+} \ No newline at end of file
diff --git a/snap/parsing.go b/snap/parsing.go
index e878077..fcacec9 100644
--- a/snap/parsing.go
+++ b/snap/parsing.go
@@ -78,7 +78,7 @@ func ToRelative(snapish string) * Relative {
return ref;
}
- if ref := parseRelativeSyntax(snapish, "~"); ref != nil {
+ if ref := parseRelativeSyntax(snapish, "^"); ref != nil {
ref.offset = -ref.offset;
return ref;
}
@@ -88,7 +88,6 @@ func ToRelative(snapish string) * Relative {
};
}
-// FROM, TO
func ParseDiff(snapdiff string) (fromRel, toRel *Relative) {
snapishes := strings.Split(snapdiff, "..")