diff options
Diffstat (limited to 'snap/fs.go')
-rw-r--r-- | snap/fs.go | 147 |
1 files changed, 147 insertions, 0 deletions
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 |