Skip to content

Commit 09ad16f

Browse files
authored
Merge pull request #1120 from percona/PBM-1483-fix-panic-phys-restore
PBM-1483: PBM segfault on physical restore with FS storage
2 parents 8481321 + 159a602 commit 09ad16f

File tree

2 files changed

+190
-1
lines changed

2 files changed

+190
-1
lines changed

pbm/storage/fs/fs.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,10 @@ func (fs *FS) List(prefix, suffix string) ([]storage.FileInfo, error) {
196196
return errors.Wrap(err, "walking the path")
197197
}
198198

199-
info, _ := entry.Info()
199+
info, err := entry.Info()
200+
if err != nil {
201+
return errors.Wrap(err, "getting file info")
202+
}
200203
if info.IsDir() {
201204
return nil
202205
}

pbm/storage/fs/fs_test.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package fs
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
"time"
9+
10+
"github.com/percona/percona-backup-mongodb/pbm/storage"
11+
)
12+
13+
func TestList(t *testing.T) {
14+
t.Run("basic usage", func(t *testing.T) {
15+
tmpDir := setupTestFiles(t)
16+
fs := &FS{root: tmpDir}
17+
18+
testCases := []struct {
19+
desc string
20+
prefix string
21+
suffix string
22+
wantFiles []storage.FileInfo
23+
}{
24+
{
25+
desc: "list all non-tmp files",
26+
prefix: "",
27+
suffix: "",
28+
wantFiles: []storage.FileInfo{
29+
{Name: "file1.txt", Size: 8},
30+
{Name: "file2.log", Size: 8},
31+
{Name: "subdir/file4.txt", Size: 8},
32+
{Name: "subdir/file5.log", Size: 8},
33+
},
34+
},
35+
{
36+
desc: "list txt files only",
37+
prefix: "",
38+
suffix: ".txt",
39+
wantFiles: []storage.FileInfo{{Name: "file1.txt", Size: 8}, {Name: "subdir/file4.txt", Size: 8}},
40+
},
41+
{
42+
desc: "list tmp files explicitly",
43+
prefix: "",
44+
suffix: ".tmp",
45+
wantFiles: []storage.FileInfo{{Name: "file3.txt.tmp", Size: 8}, {Name: "subdir/file6.txt.tmp", Size: 8}},
46+
},
47+
{
48+
desc: "List files with prefix only",
49+
prefix: "subdir",
50+
suffix: "",
51+
wantFiles: []storage.FileInfo{{Name: "file4.txt", Size: 8}, {Name: "file5.log", Size: 8}},
52+
},
53+
{
54+
desc: "list files with prefix & suffix",
55+
prefix: "subdir",
56+
suffix: ".log",
57+
wantFiles: []storage.FileInfo{{Name: "file5.log", Size: 8}},
58+
},
59+
{
60+
desc: "non existing prefix",
61+
prefix: "nonexistent",
62+
suffix: "",
63+
wantFiles: []storage.FileInfo{},
64+
},
65+
{
66+
desc: "empty dir",
67+
prefix: "empty",
68+
suffix: "",
69+
wantFiles: []storage.FileInfo{},
70+
},
71+
}
72+
73+
for _, tC := range testCases {
74+
t.Run(tC.desc, func(t *testing.T) {
75+
files, err := fs.List(tC.prefix, tC.suffix)
76+
if err != nil {
77+
t.Errorf("got error while executing list: %v", err)
78+
}
79+
80+
if len(files) != len(tC.wantFiles) {
81+
t.Errorf("wrong number of returned files: want:%d, got=%d", len(tC.wantFiles), len(files))
82+
}
83+
84+
gotFiles := map[string]int64{}
85+
for _, f := range files {
86+
gotFiles[f.Name] = f.Size
87+
}
88+
89+
for _, f := range tC.wantFiles {
90+
size, exists := gotFiles[f.Name]
91+
if !exists {
92+
t.Errorf("missing file: %s", f.Name)
93+
}
94+
if f.Size != size {
95+
t.Errorf("wrong file size: want=%d, got=%d", f.Size, size)
96+
}
97+
}
98+
})
99+
}
100+
})
101+
102+
t.Run("list and delete in parallel", func(t *testing.T) {
103+
tmpDir := setupTestFiles(t)
104+
fs := &FS{root: tmpDir}
105+
106+
errCh := make(chan error)
107+
go func() {
108+
for range 10 {
109+
_, err := fs.List("", "")
110+
if err != nil {
111+
errCh <- err
112+
break
113+
}
114+
}
115+
}()
116+
117+
go func() {
118+
files, err := fs.List("", "")
119+
if err != nil {
120+
t.Errorf("try to fetch deletion list: %v", err)
121+
}
122+
for _, f := range files {
123+
if err := os.Remove(filepath.Join(tmpDir, f.Name)); err != nil {
124+
t.Errorf("error while deleting: %v", err)
125+
}
126+
}
127+
}()
128+
129+
wantErr := "getting file info"
130+
select {
131+
case err := <-errCh:
132+
if !strings.Contains(err.Error(), wantErr) {
133+
t.Fatalf("want err: %s, got=%v", wantErr, err)
134+
}
135+
case <-time.After(2 * time.Second):
136+
t.Log("timed out while waiting for err")
137+
}
138+
})
139+
}
140+
141+
func setupTestFiles(t *testing.T) string {
142+
tmpDir, err := os.MkdirTemp("", "fs-test-*")
143+
if err != nil {
144+
t.Fatalf("error while creating setup files: %v", err)
145+
}
146+
147+
t.Cleanup(func() {
148+
os.RemoveAll(tmpDir)
149+
})
150+
151+
// tmpDir/
152+
// - file1.txt
153+
// - file2.log
154+
// - file3.txt.tmp
155+
// - subdir/
156+
// - file4.txt
157+
// - file5.log
158+
// - file6.txt.tmp
159+
// - empty/
160+
createTestFile(t, filepath.Join(tmpDir, "file1.txt"), "content1")
161+
createTestFile(t, filepath.Join(tmpDir, "file2.log"), "content2")
162+
createTestFile(t, filepath.Join(tmpDir, "file3.txt.tmp"), "content3")
163+
164+
createTestDir(t, filepath.Join(tmpDir, "subdir"))
165+
createTestFile(t, filepath.Join(tmpDir, "subdir", "file4.txt"), "content4")
166+
createTestFile(t, filepath.Join(tmpDir, "subdir", "file5.log"), "content5")
167+
createTestFile(t, filepath.Join(tmpDir, "subdir", "file6.txt.tmp"), "content6")
168+
169+
createTestDir(t, filepath.Join(tmpDir, "empty"))
170+
171+
return tmpDir
172+
}
173+
174+
func createTestFile(t *testing.T, path, content string) {
175+
t.Helper()
176+
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
177+
t.Fatalf("error while creating file %s: %v", path, err)
178+
}
179+
}
180+
181+
func createTestDir(t *testing.T, path string) {
182+
t.Helper()
183+
if err := os.Mkdir(path, 0o755); err != nil {
184+
t.Fatalf("error while creating dir %s: %v", path, err)
185+
}
186+
}

0 commit comments

Comments
 (0)