Skip to content

Commit 366c69e

Browse files
Fix snapshot archive/restore entry symmetry
Co-authored-by: Ben Schellenberger <bschellenberger2600@users.noreply.github.com>
1 parent b87d1d8 commit 366c69e

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

snapshots.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ func SnapshotRepoE(repoPath string) (*Snapshot, error) {
4949
return err
5050
}
5151

52+
// Keep snapshot/restore symmetric: only archive entry types restore supports.
53+
// This intentionally skips device files, sockets, and FIFOs.
54+
if !supportsSnapshotEntry(info) {
55+
return nil
56+
}
57+
5258
linkTarget := ""
5359
if info.Mode()&os.ModeSymlink != 0 {
5460
target, err := os.Readlink(path)
@@ -113,6 +119,11 @@ func SnapshotRepoE(repoPath string) (*Snapshot, error) {
113119
}, nil
114120
}
115121

122+
func supportsSnapshotEntry(info os.FileInfo) bool {
123+
mode := info.Mode()
124+
return mode.IsDir() || mode.IsRegular() || mode&os.ModeSymlink != 0
125+
}
126+
116127
// RestoreSnapshot restores a snapshot to a new temporary directory
117128
// Returns the path to the restored repository
118129
func RestoreSnapshot(t *testing.T, snapshot *Snapshot) string {

snapshots_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"path/filepath"
99
"strings"
1010
"testing"
11+
"time"
1112
)
1213

1314
func TestRestoreSnapshotRejectsUnsafeSnapshotNames(t *testing.T) {
@@ -200,3 +201,37 @@ func TestSnapshotRoundtripRestoresSymlinkEntries(t *testing.T) {
200201
t.Fatalf("expected symlink target %q, got %q", "target.txt", destination)
201202
}
202203
}
204+
205+
type stubFileInfo struct {
206+
mode os.FileMode
207+
}
208+
209+
func (s stubFileInfo) Name() string { return "stub" }
210+
func (s stubFileInfo) Size() int64 { return 0 }
211+
func (s stubFileInfo) Mode() os.FileMode { return s.mode }
212+
func (s stubFileInfo) ModTime() time.Time { return time.Time{} }
213+
func (s stubFileInfo) IsDir() bool { return s.mode.IsDir() }
214+
func (s stubFileInfo) Sys() any { return nil }
215+
216+
func TestSupportsSnapshotEntry(t *testing.T) {
217+
tests := []struct {
218+
name string
219+
mode os.FileMode
220+
want bool
221+
}{
222+
{name: "regular file", mode: 0644, want: true},
223+
{name: "directory", mode: os.ModeDir | 0755, want: true},
224+
{name: "symlink", mode: os.ModeSymlink, want: true},
225+
{name: "named pipe", mode: os.ModeNamedPipe, want: false},
226+
{name: "character device", mode: os.ModeCharDevice, want: false},
227+
}
228+
229+
for _, tt := range tests {
230+
t.Run(tt.name, func(t *testing.T) {
231+
got := supportsSnapshotEntry(stubFileInfo{mode: tt.mode})
232+
if got != tt.want {
233+
t.Fatalf("supportsSnapshotEntry(%v) = %v, want %v", tt.mode, got, tt.want)
234+
}
235+
})
236+
}
237+
}

0 commit comments

Comments
 (0)