Skip to content

Commit

Permalink
check existing snapshot before doing a new one
Browse files Browse the repository at this point in the history
  • Loading branch information
Son Roy Almerol committed Nov 12, 2024
1 parent e8a2842 commit 42e661e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 54 deletions.
25 changes: 15 additions & 10 deletions internal/agent/sftp/filelister.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ func (fl *FileLister) ListAt(fileList []os.FileInfo, offset int64) (int, error)
return 0, io.EOF
}

if n := copy(fileList, fl.files[offset:]); n < len(fl.files) {
n := copy(fileList, fl.files[offset:])
if n < len(fileList) {
return n, io.EOF
} else {
return n, nil
}
return n, nil
}

func (h *SftpHandler) FileLister(dirPath string) (*FileLister, error) {
Expand Down Expand Up @@ -63,9 +63,18 @@ func (h *SftpHandler) FileStat(filename string) (*FileLister, error) {
var stat fs.FileInfo
var err error

stat, err = os.Stat(filename)
if err != nil {
return nil, err
isRoot := strings.TrimPrefix(filename, h.Snapshot.SnapshotPath) == ""

if isRoot {
stat, err = os.Stat(filename)
if err != nil {
return nil, err
}
} else {
stat, err = os.Lstat(filename)
if err != nil {
return nil, err
}
}

if skipFile(filename, stat) {
Expand All @@ -79,10 +88,6 @@ func (h *SftpHandler) setFilePath(r *sftp.Request) {
r.Filepath = filepath.Join(h.Snapshot.SnapshotPath, filepath.Clean(r.Filepath))
}

func (h *SftpHandler) fetch(path string) (*os.File, error) {
return os.Open(path)
}

func wildcardToRegex(pattern string) string {
// Escape backslashes and convert path to regex-friendly format
escapedPattern := regexp.QuoteMeta(pattern)
Expand Down
28 changes: 10 additions & 18 deletions internal/agent/sftp/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"log"
"os"
"sync"

"github.com/pkg/sftp"
Expand All @@ -16,9 +17,9 @@ import (

type SftpHandler struct {
ctx context.Context
mu sync.Mutex
DriveLetter string
Snapshot *snapshots.WinVSSSnapshot
mu sync.Mutex
}

func NewSftpHandler(ctx context.Context, driveLetter string, snapshot *snapshots.WinVSSSnapshot) (*sftp.Handlers, error) {
Expand All @@ -33,40 +34,31 @@ func NewSftpHandler(ctx context.Context, driveLetter string, snapshot *snapshots
}

func (h *SftpHandler) Fileread(r *sftp.Request) (io.ReaderAt, error) {
h.mu.Lock()
defer h.mu.Unlock()
flags := r.Pflags()
if !flags.Read {
return nil, os.ErrInvalid
}

h.Snapshot.UpdateTimestamp()
go h.Snapshot.UpdateTimestamp()
h.setFilePath(r)

file, err := h.fetch(r.Filepath)
if err != nil {
log.Printf("error reading file: %v", err)
return nil, err
}

return file, nil
return os.Open(r.Filepath)
}

func (h *SftpHandler) Filewrite(r *sftp.Request) (io.WriterAt, error) {
h.mu.Lock()
defer h.mu.Unlock()

return nil, fmt.Errorf("unsupported file command: %s", r.Method)
}

func (h *SftpHandler) Filecmd(r *sftp.Request) error {
h.mu.Lock()
defer h.mu.Unlock()

return fmt.Errorf("unsupported file command: %s", r.Method)
}

func (h *SftpHandler) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
_ = r.WithContext(r.Context())

h.mu.Lock()
defer h.mu.Unlock()

h.Snapshot.UpdateTimestamp()
h.setFilePath(r)

switch r.Method {
Expand Down
63 changes: 37 additions & 26 deletions internal/agent/snapshots/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,65 +27,76 @@ func getVSSFolder() (string, error) {
}

func Snapshot(driveLetter string) (*WinVSSSnapshot, error) {
knownSnaps := &KnownSnapshots{
registry: "KnownSnaps",
}

knownSnaps := &KnownSnapshots{registry: "KnownSnaps"}
volName := filepath.VolumeName(fmt.Sprintf("%s:", driveLetter))

// Check if there's an existing valid snapshot
vssFolder, err := getVSSFolder()
if err != nil {
return nil, fmt.Errorf("Snapshot: error getting app data folder -> %w", err)
}

snapshotPath := filepath.Join(vssFolder, driveLetter)
if knownSnap, err := knownSnaps.Get(snapshotPath); err == nil {
if time.Since(knownSnap.GetTimestamp()) < 15*time.Minute {
return knownSnap, nil
}
knownSnap.Close()
_ = vss.Remove(snapshotPath) // Clean up old snapshot link if expired
}

// Attempt to create a new snapshot
err = vss.CreateLink(snapshotPath, volName)
if err != nil {
if strings.Contains(err.Error(), "shadow copy operation is already in progress") {
// Wait for the in-progress shadow copy operation to complete
for {
if _, err := vss.Get(snapshotPath); err == nil {
break
}
}
} else {
if strings.Contains(err.Error(), "already exists") {
if _, err := vss.Get(snapshotPath); err == nil {
if knownSnap, err := knownSnaps.Get(snapshotPath); err == nil {
if knownSnap.SnapshotPath == snapshotPath && time.Since(knownSnap.LastAccessed) < (15*time.Minute) {
return knownSnap, nil
} else if time.Since(knownSnap.LastAccessed) >= (15 * time.Minute) {
knownSnap.Close()
}
}
_ = vss.Remove(snapshotPath)
}

_ = os.Remove(snapshotPath)

if err := vss.CreateLink(snapshotPath, volName); err != nil {
return nil, fmt.Errorf("Snapshot: error creating snapshot (%s to %s) -> %w", volName, snapshotPath, err)
}
} else {
} else if strings.Contains(err.Error(), "already exists") {
_ = vss.Remove(snapshotPath)
err = vss.CreateLink(snapshotPath, volName)
if err != nil {
return nil, fmt.Errorf("Snapshot: error creating snapshot (%s to %s) -> %w", volName, snapshotPath, err)
}
} else {
return nil, fmt.Errorf("Snapshot: error creating snapshot (%s to %s) -> %w", volName, snapshotPath, err)
}
}

// Retrieve snapshot details and save the new snapshot
sc, err := vss.Get(snapshotPath)
if err != nil {
_ = vss.Remove(snapshotPath)
return nil, fmt.Errorf("Snapshot: error getting snapshot details -> %w", err)
}

newSnapshot := WinVSSSnapshot{
newSnapshot := &WinVSSSnapshot{
SnapshotPath: snapshotPath,
LastAccessed: time.Now(),
Id: sc.ID,
}
knownSnaps.Save(newSnapshot)

return newSnapshot, nil
}

func (instance *WinVSSSnapshot) GetTimestamp() time.Time {
if instance.LastAccessed.IsZero() {
knownSnaps := &KnownSnapshots{
registry: "KnownSnaps",
}

knownSnaps.Save(&newSnapshot)
snap, err := knownSnaps.Get(instance.SnapshotPath)
if err == nil {
instance.LastAccessed = snap.LastAccessed
return snap.LastAccessed
}
}

return &newSnapshot, nil
return instance.LastAccessed
}

func (instance *WinVSSSnapshot) UpdateTimestamp() {
Expand Down
1 change: 1 addition & 0 deletions internal/backend/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func Mount(target *store.Target) (*AgentMount, error) {
mountArgs := []string{
"mount",
"--daemon",
"--vfs-cache-mode", "minimal",
"--read-only",
"--uid", "0",
"--gid", "0",
Expand Down

0 comments on commit 42e661e

Please sign in to comment.