mirror of
https://github.com/kopia/kopia.git
synced 2026-03-18 06:06:16 -04:00
135 lines
2.9 KiB
Go
135 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"path/filepath"
|
|
|
|
"github.com/kopia/kopia/fs/repofs"
|
|
"github.com/kopia/kopia/vault"
|
|
|
|
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
|
)
|
|
|
|
var (
|
|
backupsCommand = app.Command("backups", "List history of file or directory backups.")
|
|
backupsPath = backupsCommand.Arg("source", "File or directory to show history of.").String()
|
|
maxResultsPerPath = backupsCommand.Flag("maxresults", "Maximum number of results.").Default("100").Int()
|
|
)
|
|
|
|
func findBackups(vlt *vault.Vault, path string) ([]string, string, error) {
|
|
var relPath string
|
|
|
|
for len(path) > 0 {
|
|
sourceInfo := repofs.SnapshotSourceInfo{
|
|
Path: path,
|
|
Host: getBackupHostName(),
|
|
UserName: getBackupUser(),
|
|
}
|
|
|
|
prefix := sourceInfo.HashString() + "."
|
|
|
|
list, err := vlt.List("B" + prefix)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
if len(list) > 0 {
|
|
return list, relPath, nil
|
|
}
|
|
|
|
if len(relPath) > 0 {
|
|
relPath = filepath.Base(path) + "/" + relPath
|
|
} else {
|
|
relPath = filepath.Base(path)
|
|
}
|
|
|
|
log.Printf("No backups of %v@%v:%v", sourceInfo.UserName, sourceInfo.Host, sourceInfo.Path)
|
|
|
|
parent := filepath.Dir(path)
|
|
if parent == path {
|
|
break
|
|
}
|
|
path = parent
|
|
}
|
|
|
|
return nil, "", nil
|
|
}
|
|
|
|
func runBackupsCommand(context *kingpin.ParseContext) error {
|
|
conn := mustOpenConnection()
|
|
defer conn.Close()
|
|
|
|
var previous []string
|
|
var relPath string
|
|
var err error
|
|
|
|
if *backupsPath != "" {
|
|
path, err := filepath.Abs(*backupsPath)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid directory: '%s': %s", *backupsPath, err)
|
|
}
|
|
|
|
previous, relPath, err = findBackups(conn.Vault, filepath.Clean(path))
|
|
if relPath != "" {
|
|
relPath = "/" + relPath
|
|
}
|
|
} else {
|
|
previous, err = conn.Vault.List("B")
|
|
}
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("cannot list backups: %v", err)
|
|
}
|
|
|
|
var lastSource repofs.SnapshotSourceInfo
|
|
var count int
|
|
|
|
for _, m := range loadBackupManifests(conn.Vault, previous) {
|
|
if m.Source != lastSource {
|
|
fmt.Printf("%v@%v:%v\n", m.Source.UserName, m.Source.Host, m.Source.Path)
|
|
lastSource = m.Source
|
|
count = 0
|
|
}
|
|
|
|
if count < *maxResultsPerPath {
|
|
fmt.Printf(" %v%v %v\n", m.Handle, relPath, m.StartTime.Format("2006-01-02 15:04:05 MST"))
|
|
count++
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func loadBackupManifests(vlt *vault.Vault, names []string) []*repofs.Snapshot {
|
|
result := make([]*repofs.Snapshot, len(names))
|
|
sem := make(chan bool, 5)
|
|
|
|
for i, n := range names {
|
|
sem <- true
|
|
go func(i int, n string) {
|
|
defer func() { <-sem }()
|
|
|
|
m, err := loadBackupManifest(vlt, n)
|
|
if err != nil {
|
|
log.Printf("WARNING: Unable to parse backup manifest %v: %v", n, err)
|
|
return
|
|
}
|
|
result[i] = m
|
|
}(i, n)
|
|
}
|
|
|
|
for i := 0; i < cap(sem); i++ {
|
|
sem <- true
|
|
}
|
|
close(sem)
|
|
|
|
return result
|
|
}
|
|
|
|
func init() {
|
|
backupsCommand.Action(runBackupsCommand)
|
|
backupsCommand.Flag("host", "Override backup hostname.").StringVar(backupHostName)
|
|
backupsCommand.Flag("user", "Override backup user.").StringVar(backupUser)
|
|
}
|