Skip to content

Commit 5b68af6

Browse files
committed
add path overlap protection
1 parent 7aceddd commit 5b68af6

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

internal/cmd/mount.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"log"
66
"os"
77
"os/signal"
8+
"path/filepath"
9+
"strings"
810

911
"bazil.org/fuse"
1012
"bazil.org/fuse/fs"
@@ -29,13 +31,45 @@ MOUNTPOINT is the directory where the filesystem will be mounted.`,
2931
}
3032
}
3133

34+
// pathsOverlap checks if two paths overlap (one contains the other).
35+
// It returns true if either path is a parent/child of the other.
36+
func pathsOverlap(path1, path2 string) bool {
37+
// Convert to absolute paths and clean them
38+
abs1, err1 := filepath.Abs(path1)
39+
abs2, err2 := filepath.Abs(path2)
40+
41+
// If we can't resolve absolute paths, compare as-is
42+
if err1 != nil {
43+
abs1 = filepath.Clean(path1)
44+
}
45+
if err2 != nil {
46+
abs2 = filepath.Clean(path2)
47+
}
48+
49+
// Ensure paths end with separator for accurate prefix checking
50+
if !strings.HasSuffix(abs1, string(filepath.Separator)) {
51+
abs1 += string(filepath.Separator)
52+
}
53+
if !strings.HasSuffix(abs2, string(filepath.Separator)) {
54+
abs2 += string(filepath.Separator)
55+
}
56+
57+
// Check if either path is a prefix of the other
58+
return strings.HasPrefix(abs1, abs2) || strings.HasPrefix(abs2, abs1)
59+
}
60+
3261
func runMount(cmd *cobra.Command, args []string) {
3362
// Print version info on startup
3463
fmt.Printf("djafs %s starting...\n", version.GetFullVersion())
3564

3665
storagePath := args[0]
3766
mountpoint := args[1]
3867

68+
// Validate that storage path and mountpoint don't overlap
69+
if pathsOverlap(storagePath, mountpoint) {
70+
log.Fatalf("Storage path and mountpoint cannot overlap: storage=%s, mount=%s", storagePath, mountpoint)
71+
}
72+
3973
// Ensure storage directory exists
4074
if err := os.MkdirAll(storagePath, 0755); err != nil {
4175
log.Fatalf("Failed to create storage directory: %v", err)

internal/cmd/mount_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestPathsOverlap(t *testing.T) {
8+
tests := []struct {
9+
name string
10+
path1 string
11+
path2 string
12+
expected bool
13+
}{
14+
{
15+
name: "identical paths",
16+
path1: "/tmp/storage",
17+
path2: "/tmp/storage",
18+
expected: true,
19+
},
20+
{
21+
name: "path1 contains path2",
22+
path1: "/tmp/storage/data",
23+
path2: "/tmp/storage",
24+
expected: true,
25+
},
26+
{
27+
name: "path2 contains path1",
28+
path1: "/tmp/storage",
29+
path2: "/tmp/storage/mount",
30+
expected: true,
31+
},
32+
{
33+
name: "completely separate paths",
34+
path1: "/tmp/storage",
35+
path2: "/mnt/mount",
36+
expected: false,
37+
},
38+
{
39+
name: "sibling directories",
40+
path1: "/tmp/storage",
41+
path2: "/tmp/mount",
42+
expected: false,
43+
},
44+
{
45+
name: "relative paths - overlapping",
46+
path1: "storage",
47+
path2: "storage/mount",
48+
expected: true,
49+
},
50+
{
51+
name: "relative paths - separate",
52+
path1: "storage",
53+
path2: "mount",
54+
expected: false,
55+
},
56+
}
57+
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
result := pathsOverlap(tt.path1, tt.path2)
61+
if result != tt.expected {
62+
t.Errorf("pathsOverlap(%q, %q) = %v, expected %v", tt.path1, tt.path2, result, tt.expected)
63+
}
64+
})
65+
}
66+
}

0 commit comments

Comments
 (0)