Skip to content

Commit 9f56153

Browse files
committed
db: fix Open issue with single-item relative paths
Previously Open with a relative path containing a single element would fail on real filesystems, because Open would attempt to open the empty path. Fix #3842.
1 parent 0d43ddc commit 9f56153

File tree

8 files changed

+159
-46
lines changed

8 files changed

+159
-46
lines changed

checkpoint.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,20 @@ func mkdirAllAndSyncParents(fs vfs.FS, destDir string) (vfs.File, error) {
9191
// Collect paths for all directories between destDir (excluded) and its
9292
// closest existing ancestor (included).
9393
var parentPaths []string
94-
foundExistingAncestor := false
95-
for parentPath := fs.PathDir(destDir); parentPath != "."; parentPath = fs.PathDir(parentPath) {
94+
for parentPath := fs.PathDir(destDir); ; parentPath = fs.PathDir(parentPath) {
9695
parentPaths = append(parentPaths, parentPath)
96+
if fs.PathDir(parentPath) == parentPath {
97+
break
98+
}
9799
_, err := fs.Stat(parentPath)
98100
if err == nil {
99101
// Exit loop at the closest existing ancestor.
100-
foundExistingAncestor = true
101102
break
102103
}
103104
if !oserror.IsNotExist(err) {
104105
return nil, err
105106
}
106107
}
107-
// Handle empty filesystem edge case.
108-
if !foundExistingAncestor {
109-
parentPaths = append(parentPaths, "")
110-
}
111108
// Create destDir and any of its missing parents.
112109
if err := fs.MkdirAll(destDir, 0755); err != nil {
113110
return nil, err

open_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,3 +1478,49 @@ func TestOpenRatchetsNextFileNum(t *testing.T) {
14781478
require.NoError(t, d.Compact([]byte("a"), []byte("z"), false))
14791479

14801480
}
1481+
1482+
func TestMkdirAllAndSyncParents(t *testing.T) {
1483+
if filepath.Separator != '/' {
1484+
t.Skip("skipping due to path separator")
1485+
}
1486+
pwd, err := os.Getwd()
1487+
require.NoError(t, err)
1488+
defer func() { os.Chdir(pwd) }()
1489+
1490+
filesystems := map[string]vfs.FS{}
1491+
rootPaths := map[string]string{}
1492+
var buf bytes.Buffer
1493+
datadriven.RunTest(t, "testdata/mkdir_all_and_sync_parents", func(t *testing.T, td *datadriven.TestData) string {
1494+
buf.Reset()
1495+
switch td.Cmd {
1496+
case "mkfs":
1497+
var fsName string
1498+
td.ScanArgs(t, "fs", &fsName)
1499+
if td.HasArg("memfs") {
1500+
filesystems[fsName] = vfs.NewMem()
1501+
return "new memfs"
1502+
}
1503+
filesystems[fsName] = vfs.Default
1504+
rootPaths[fsName] = t.TempDir()
1505+
return "new default fs"
1506+
case "mkdir-all-and-sync-parents":
1507+
var fsName, path string
1508+
td.ScanArgs(t, "fs", &fsName)
1509+
td.ScanArgs(t, "path", &path)
1510+
if p, ok := rootPaths[fsName]; ok {
1511+
require.NoError(t, os.Chdir(p))
1512+
}
1513+
fs := vfs.WithLogging(filesystems[fsName], func(format string, args ...interface{}) {
1514+
fmt.Fprintf(&buf, format+"\n", args...)
1515+
})
1516+
f, err := mkdirAllAndSyncParents(fs, path)
1517+
if err != nil {
1518+
return err.Error()
1519+
}
1520+
require.NoError(t, f.Close())
1521+
return buf.String()
1522+
default:
1523+
return fmt.Sprintf("unrecognized command %q", td.Cmd)
1524+
}
1525+
})
1526+
}

testdata/checkpoint

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
open db
22
----
33
mkdir-all: db 0755
4-
open-dir:
5-
sync:
6-
close:
4+
open-dir: .
5+
sync: .
6+
close: .
77
open-dir: db
88
close: db
99
open-dir: db
@@ -92,9 +92,9 @@ mkdir-all: checkpoints/checkpoint1 0755
9292
open-dir: checkpoints
9393
sync: checkpoints
9494
close: checkpoints
95-
open-dir:
96-
sync:
97-
close:
95+
open-dir: .
96+
sync: .
97+
close: .
9898
open-dir: checkpoints/checkpoint1
9999
link: db/OPTIONS-000003 -> checkpoints/checkpoint1/OPTIONS-000003
100100
open-dir: checkpoints/checkpoint1

testdata/checkpoint_shared

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
open db
22
----
33
mkdir-all: db 0755
4-
open-dir:
5-
sync:
6-
close:
4+
open-dir: .
5+
sync: .
6+
close: .
77
open-dir: db
88
close: db
99
open-dir: db
@@ -80,9 +80,9 @@ mkdir-all: checkpoints/checkpoint1 0755
8080
open-dir: checkpoints
8181
sync: checkpoints
8282
close: checkpoints
83-
open-dir:
84-
sync:
85-
close:
83+
open-dir: .
84+
sync: .
85+
close: .
8686
open-dir: checkpoints/checkpoint1
8787
link: db/OPTIONS-000003 -> checkpoints/checkpoint1/OPTIONS-000003
8888
open-dir: checkpoints/checkpoint1

testdata/cleaner

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
open db archive
33
----
44
mkdir-all: db 0755
5-
open-dir:
6-
sync:
7-
close:
5+
open-dir: .
6+
sync: .
7+
close: .
88
open-dir: db
99
close: db
1010
mkdir-all: db_wal 0755
11-
open-dir:
12-
sync:
13-
close:
11+
open-dir: .
12+
sync: .
13+
close: .
1414
open-dir: db_wal
1515
close: db_wal
1616
open-dir: db
@@ -132,15 +132,15 @@ list db_wal/archive
132132
open db1
133133
----
134134
mkdir-all: db1 0755
135-
open-dir:
136-
sync:
137-
close:
135+
open-dir: .
136+
sync: .
137+
close: .
138138
open-dir: db1
139139
close: db1
140140
mkdir-all: db1_wal 0755
141-
open-dir:
142-
sync:
143-
close:
141+
open-dir: .
142+
sync: .
143+
close: .
144144
open-dir: db1_wal
145145
close: db1_wal
146146
open-dir: db1
@@ -211,15 +211,15 @@ close: db1/000456.sst
211211
open db1
212212
----
213213
mkdir-all: db1 0755
214-
open-dir:
215-
sync:
216-
close:
214+
open-dir: .
215+
sync: .
216+
close: .
217217
open-dir: db1
218218
close: db1
219219
mkdir-all: db1_wal 0755
220-
open-dir:
221-
sync:
222-
close:
220+
open-dir: .
221+
sync: .
222+
close: .
223223
open-dir: db1_wal
224224
close: db1_wal
225225
open-dir: db1

testdata/event_listener

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
open
22
----
33
mkdir-all: db 0755
4-
open-dir:
5-
sync:
6-
close:
4+
open-dir: .
5+
sync: .
6+
close: .
77
open-dir: db
88
close: db
99
mkdir-all: wal 0755
10-
open-dir:
11-
sync:
12-
close:
10+
open-dir: .
11+
sync: .
12+
close: .
1313
open-dir: wal
1414
close: wal
1515
open-dir: db
@@ -346,9 +346,9 @@ sstables
346346
checkpoint
347347
----
348348
mkdir-all: checkpoint 0755
349-
open-dir:
350-
sync:
351-
close:
349+
open-dir: .
350+
sync: .
351+
close: .
352352
open-dir: checkpoint
353353
link: db/OPTIONS-000003 -> checkpoint/OPTIONS-000003
354354
open-dir: checkpoint
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
mkfs memfs fs=mem1
2+
----
3+
new memfs
4+
5+
mkdir-all-and-sync-parents fs=mem1 path=foo/bar/baz/bax
6+
----
7+
mkdir-all: foo/bar/baz/bax 0755
8+
open-dir: foo/bar/baz
9+
sync: foo/bar/baz
10+
close: foo/bar/baz
11+
open-dir: foo/bar
12+
sync: foo/bar
13+
close: foo/bar
14+
open-dir: foo
15+
sync: foo
16+
close: foo
17+
open-dir: .
18+
sync: .
19+
close: .
20+
open-dir: foo/bar/baz/bax
21+
close: foo/bar/baz/bax
22+
23+
# Repeating the same command should only sync the parent, and then the new data
24+
# directory itself.
25+
26+
mkdir-all-and-sync-parents fs=mem1 path=foo/bar/baz/bax
27+
----
28+
mkdir-all: foo/bar/baz/bax 0755
29+
open-dir: foo/bar/baz
30+
sync: foo/bar/baz
31+
close: foo/bar/baz
32+
open-dir: foo/bar/baz/bax
33+
close: foo/bar/baz/bax
34+
35+
mkfs fs=default1
36+
----
37+
new default fs
38+
39+
mkdir-all-and-sync-parents fs=default1 path=foo/bar/baz/bax
40+
----
41+
mkdir-all: foo/bar/baz/bax 0755
42+
open-dir: foo/bar/baz
43+
sync: foo/bar/baz
44+
close: foo/bar/baz
45+
open-dir: foo/bar
46+
sync: foo/bar
47+
close: foo/bar
48+
open-dir: foo
49+
sync: foo
50+
close: foo
51+
open-dir: .
52+
sync: .
53+
close: .
54+
open-dir: foo/bar/baz/bax
55+
close: foo/bar/baz/bax
56+
57+
# Repeating the same command should only sync the parent, and then the new data
58+
# directory itself.
59+
60+
mkdir-all-and-sync-parents fs=default1 path=foo/bar/baz/bax
61+
----
62+
mkdir-all: foo/bar/baz/bax 0755
63+
open-dir: foo/bar/baz
64+
sync: foo/bar/baz
65+
close: foo/bar/baz
66+
open-dir: foo/bar/baz/bax
67+
close: foo/bar/baz/bax

vfs/mem_fs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ func (y *MemFS) walk(fullname string, f func(dir *memNode, frag string, final bo
172172
for len(fullname) > 0 && fullname[0] == sep[0] {
173173
fullname = fullname[1:]
174174
}
175+
if fullname == "." {
176+
fullname = ""
177+
}
175178
dir := y.root
176179

177180
for {

0 commit comments

Comments
 (0)