From e597de429b4bc8d2974d5fd13345f8b5a5d6bd84 Mon Sep 17 00:00:00 2001 From: ronyweng Date: Mon, 28 Dec 2020 13:59:01 +0800 Subject: [PATCH] Fix BasePathFs symlink 1. Support related path 2. Make ReadlinkIfPossible return path under basepath Fixes #282 --- basepath.go | 18 ++++++++++++++---- basepath_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/basepath.go b/basepath.go index 4f98328..f314abc 100644 --- a/basepath.go +++ b/basepath.go @@ -185,9 +185,12 @@ func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { } func (b *BasePathFs) SymlinkIfPossible(oldname, newname string) error { - oldname, err := b.RealPath(oldname) - if err != nil { - return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err} + var err error + if filepath.IsAbs(oldname) { + oldname, err = b.RealPath(oldname) + if err != nil { + return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err} + } } newname, err = b.RealPath(newname) if err != nil { @@ -205,7 +208,14 @@ func (b *BasePathFs) ReadlinkIfPossible(name string) (string, error) { return "", &os.PathError{Op: "readlink", Path: name, Err: err} } if reader, ok := b.source.(LinkReader); ok { - return reader.ReadlinkIfPossible(name) + linkpath, err := reader.ReadlinkIfPossible(name) + if err != nil { + return "", err + } + if filepath.IsAbs(linkpath) { + linkpath = strings.TrimPrefix(filepath.Clean(linkpath), filepath.Clean(b.path)) + } + return linkpath, nil } return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink} } diff --git a/basepath_test.go b/basepath_test.go index e314c05..a21acd1 100644 --- a/basepath_test.go +++ b/basepath_test.go @@ -188,3 +188,44 @@ func TestBasePathTempFile(t *testing.T) { t.Fatalf("TempFile realpath leaked: expected %s, got %s", expected, actual) } } + +func TestBasePathSymlink(t *testing.T) { + type SymlinkTest struct { + Testcase, Link, Target string + } + symlinkTests := []SymlinkTest{ + {Testcase: "Absolute path", Link: "/foo/baz/abs_link", Target: "/foo/file"}, + {Testcase: "Relative path", Link: "/foo/baz/ref_link", Target: "../file"}, + } + + fs := NewOsFs() + baseDir, err := TempDir(fs, "", "base") + if err != nil { + t.Fatal("error creating tempDir", err) + } + defer fs.RemoveAll(baseDir) + + var bp Fs = NewBasePathFs(fs, baseDir).(*BasePathFs) + bp.MkdirAll("/foo/baz", 0777) + bp.Create("/foo/file") + linker, _ := bp.(Linker) + lreader, _ := bp.(LinkReader) + + for _, test := range symlinkTests { + err = linker.SymlinkIfPossible(test.Target, test.Link) + if err != nil { + t.Errorf("%s: error creating symlink %s", test.Testcase, err.Error()) + } + linkpath, err := lreader.ReadlinkIfPossible(test.Link) + if err != nil { + t.Errorf("%s: error read symlink %s", test.Testcase, err.Error()) + } + if linkpath != test.Target { + t.Errorf("%s: link not match %s != %s", test.Testcase, linkpath, test.Target) + } + _, err = bp.Stat(test.Link) + if err != nil { + t.Errorf("%s: error stat symlink %s", test.Testcase, err.Error()) + } + } +}