Skip to content

Commit 6f201d3

Browse files
committed
feat: add Lstat method to filesystem interface and implementations, with tests
1 parent b822033 commit 6f201d3

4 files changed

Lines changed: 62 additions & 0 deletions

File tree

pkg/fs/fs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type (
1111
Open(path string) (ReadableFile, error)
1212
OpenFile(path string, flag int, perm fs.FileMode) (WritableFile, error)
1313
Stat(path string) (fs.FileInfo, error)
14+
Lstat(path string) (fs.FileInfo, error)
1415
Exists(path string) (bool, error)
1516
}
1617

pkg/fs/fs_disabled.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ func (n disabledFS) Stat(_ string) (fs.FileInfo, error) {
2424
return nil, ErrRootNotConfigured
2525
}
2626

27+
func (n disabledFS) Lstat(_ string) (fs.FileInfo, error) {
28+
return nil, ErrRootNotConfigured
29+
}
30+
2731
func (n disabledFS) Exists(_ string) (bool, error) {
2832
return false, ErrRootNotConfigured
2933
}

pkg/fs/fs_root.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func (r *rootFS) Stat(path string) (fs.FileInfo, error) {
4141
return r.root.Stat(path)
4242
}
4343

44+
func (r *rootFS) Lstat(path string) (fs.FileInfo, error) {
45+
return r.root.Lstat(path)
46+
}
47+
4448
func (r *rootFS) Exists(path string) (bool, error) {
4549
_, err := r.root.Stat(path)
4650
if err == nil {

pkg/fs/fs_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package fs
2+
3+
import (
4+
"context"
5+
"errors"
6+
stdfs "io/fs"
7+
"os"
8+
"path/filepath"
9+
"testing"
10+
)
11+
12+
func TestRootFSLstatDoesNotFollowFinalSymlink(t *testing.T) {
13+
root := t.TempDir()
14+
if err := os.WriteFile(filepath.Join(root, "target.txt"), []byte("target"), 0o600); err != nil {
15+
t.Fatalf("write target: %v", err)
16+
}
17+
if err := os.Symlink("target.txt", filepath.Join(root, "link.txt")); err != nil {
18+
t.Skipf("symlinks are unavailable: %v", err)
19+
}
20+
21+
filesystem, err := New(WithRoot(root))
22+
if err != nil {
23+
t.Fatalf("create filesystem: %v", err)
24+
}
25+
26+
metadata, err := ReaderFrom(WithFileSystem(context.Background(), filesystem))
27+
if err != nil {
28+
t.Fatalf("resolve link metadata: %v", err)
29+
}
30+
31+
info, err := metadata.Lstat("link.txt")
32+
if err != nil {
33+
t.Fatalf("lstat link: %v", err)
34+
}
35+
if info.Mode()&stdfs.ModeSymlink == 0 {
36+
t.Fatalf("expected symlink metadata, got mode %v", info.Mode())
37+
}
38+
}
39+
40+
func TestDisabledFSLstatPreservesRootDenial(t *testing.T) {
41+
filesystem, err := New()
42+
if err != nil {
43+
t.Fatalf("create disabled filesystem: %v", err)
44+
}
45+
metadata, err := ReaderFrom(WithFileSystem(context.Background(), filesystem))
46+
if err != nil {
47+
t.Fatalf("resolve disabled link metadata: %v", err)
48+
}
49+
50+
if _, err := metadata.Lstat("file.txt"); !errors.Is(err, ErrRootNotConfigured) {
51+
t.Fatalf("expected ErrRootNotConfigured, got %v", err)
52+
}
53+
}

0 commit comments

Comments
 (0)