fix(snapshots): fixed snapshotting of \\server\share (#4603)

* fix(snapshots): fixed snapshotting of \\server\share

* fixed linter
This commit is contained in:
Jarek Kowalski
2025-05-25 12:43:51 -07:00
committed by GitHub
parent fec575bd90
commit e9e73e6c3c
3 changed files with 60 additions and 44 deletions

View File

@@ -100,7 +100,9 @@ func (f *fileWithMetadata) Entry() (fs.Entry, error) {
return nil, errors.Wrap(err, "unable to stat() local file")
}
return newFilesystemFile(newEntry(fi, dirPrefix(f.Name()))), nil
basename, prefix := splitDirPrefix(f.Name())
return newFilesystemFile(newEntry(basename, fi, prefix)), nil
}
func (fsf *filesystemFile) Open(_ context.Context) (fs.Reader, error) {
@@ -130,16 +132,16 @@ func (e *filesystemErrorEntry) ErrorInfo() error {
return e.err
}
// dirPrefix returns the directory prefix for a given path - the initial part of the path up to and including the final slash (or backslash on Windows).
// this is similar to filepath.Dir() except dirPrefix("\\foo\bar") == "\\foo\", which is unsupported in filepath.
func dirPrefix(s string) string {
// splitDirPrefix returns the directory prefix for a given path - the initial part of the path up to and including the final slash (or backslash on Windows).
// this is similar to filepath.Dir() and filepath.Base() except splitDirPrefix("\\foo\bar") == "\\foo\", which is unsupported in filepath.
func splitDirPrefix(s string) (basename, prefix string) {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == filepath.Separator || s[i] == '/' {
return s[0 : i+1]
return s[i+1:], s[0 : i+1]
}
}
return ""
return s, ""
}
// Directory returns fs.Directory for the specified path.

View File

@@ -88,11 +88,13 @@ func (fsd *filesystemDirectory) Child(_ context.Context, name string) (fs.Entry,
return nil, errors.Wrap(err, "unable to get child")
}
return entryFromDirEntry(st, fullPath+string(filepath.Separator)), nil
return entryFromDirEntry(name, st, fullPath+string(filepath.Separator)), nil
}
func toDirEntryOrNil(dirEntry os.DirEntry, prefix string) (fs.Entry, error) {
fi, err := os.Lstat(prefix + dirEntry.Name())
n := dirEntry.Name()
fi, err := os.Lstat(prefix + n)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
@@ -101,7 +103,11 @@ func toDirEntryOrNil(dirEntry os.DirEntry, prefix string) (fs.Entry, error) {
return nil, errors.Wrap(err, "error reading directory")
}
return entryFromDirEntry(fi, prefix), nil
return entryFromDirEntry(n, fi, prefix), nil
}
func isWindows() bool {
return runtime.GOOS == "windows"
}
// NewEntry returns fs.Entry for the specified path, the result will be one of supported entry types: fs.File, fs.Directory, fs.Symlink
@@ -115,8 +121,7 @@ func NewEntry(path string) (fs.Entry, error) {
// cause os.Lstat to fail with "Incorrect function" error unless they
// end with a separator. Retry the operation with the separator added.
var e syscall.Errno
//nolint:goconst
if runtime.GOOS == "windows" &&
if isWindows() &&
!strings.HasSuffix(path, string(filepath.Separator)) &&
errors.As(err, &e) && e == 1 {
fi, err = os.Lstat(path + string(filepath.Separator))
@@ -128,42 +133,44 @@ func NewEntry(path string) (fs.Entry, error) {
}
if path == "/" {
return entryFromDirEntry(fi, ""), nil
return entryFromDirEntry("/", fi, ""), nil
}
return entryFromDirEntry(fi, dirPrefix(path)), nil
basename, prefix := splitDirPrefix(path)
return entryFromDirEntry(basename, fi, prefix), nil
}
func entryFromDirEntry(fi os.FileInfo, prefix string) fs.Entry {
isplaceholder := strings.HasSuffix(fi.Name(), ShallowEntrySuffix)
func entryFromDirEntry(basename string, fi os.FileInfo, prefix string) fs.Entry {
isplaceholder := strings.HasSuffix(basename, ShallowEntrySuffix)
maskedmode := fi.Mode() & os.ModeType
switch {
case maskedmode == os.ModeDir && !isplaceholder:
return newFilesystemDirectory(newEntry(fi, prefix))
return newFilesystemDirectory(newEntry(basename, fi, prefix))
case maskedmode == os.ModeDir && isplaceholder:
return newShallowFilesystemDirectory(newEntry(fi, prefix))
return newShallowFilesystemDirectory(newEntry(basename, fi, prefix))
case maskedmode == os.ModeSymlink && !isplaceholder:
return newFilesystemSymlink(newEntry(fi, prefix))
return newFilesystemSymlink(newEntry(basename, fi, prefix))
case maskedmode == 0 && !isplaceholder:
return newFilesystemFile(newEntry(fi, prefix))
return newFilesystemFile(newEntry(basename, fi, prefix))
case maskedmode == 0 && isplaceholder:
return newShallowFilesystemFile(newEntry(fi, prefix))
return newShallowFilesystemFile(newEntry(basename, fi, prefix))
default:
return newFilesystemErrorEntry(newEntry(fi, prefix), fs.ErrUnknown)
return newFilesystemErrorEntry(newEntry(basename, fi, prefix), fs.ErrUnknown)
}
}
var _ os.FileInfo = (*filesystemEntry)(nil)
func newEntry(fi os.FileInfo, prefix string) filesystemEntry {
func newEntry(basename string, fi os.FileInfo, prefix string) filesystemEntry {
return filesystemEntry{
TrimShallowSuffix(fi.Name()),
TrimShallowSuffix(basename),
fi.Size(),
fi.ModTime().UnixNano(),
fi.Mode(),

View File

@@ -5,7 +5,6 @@
"fmt"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/pkg/errors"
@@ -250,7 +249,7 @@ func verifyChild(t *testing.T, dir fs.Directory) {
}
func TestLocalFilesystemPath(t *testing.T) {
if runtime.GOOS == "windows" {
if isWindows() {
t.Skip()
}
@@ -273,29 +272,37 @@ func TestLocalFilesystemPath(t *testing.T) {
}
}
func TestDirPrefix(t *testing.T) {
cases := map[string]string{
"foo": "",
"/": "/",
"/tmp": "/",
"/tmp/": "/tmp/",
"/tmp/foo": "/tmp/",
func TestSplitDirPrefix(t *testing.T) {
type pair struct {
prefix string
basename string
}
if runtime.GOOS == "windows" {
cases["c:/"] = "c:/"
cases["c:\\"] = "c:\\"
cases["c:/temp"] = "c:/"
cases["c:\\temp"] = "c:\\"
cases["c:/temp/orary"] = "c:/temp/"
cases["c:\\temp\\orary"] = "c:\\temp\\"
cases["c:/temp\\orary"] = "c:/temp\\"
cases["c:\\temp/orary"] = "c:\\temp/"
cases["\\\\server\\path"] = "\\\\server\\"
cases["\\\\server\\path\\subdir"] = "\\\\server\\path\\"
cases := map[string]pair{
"foo": {"", "foo"},
"/": {"/", ""},
"/tmp": {"/", "tmp"},
"/tmp/": {"/tmp/", ""},
"/tmp/foo": {"/tmp/", "foo"},
}
if isWindows() {
cases["c:/"] = pair{"c:/", ""}
cases["c:\\"] = pair{"c:\\", ""}
cases["c:/temp"] = pair{"c:/", "temp"}
cases["c:\\temp"] = pair{"c:\\", "temp"}
cases["c:/temp/orary"] = pair{"c:/temp/", "orary"}
cases["c:\\temp\\orary"] = pair{"c:\\temp\\", "orary"}
cases["c:/temp\\orary"] = pair{"c:/temp\\", "orary"}
cases["c:\\temp/orary"] = pair{"c:\\temp/", "orary"}
cases["\\\\server\\path"] = pair{"\\\\server\\", "path"}
cases["\\\\server\\path\\"] = pair{"\\\\server\\path\\", ""}
cases["\\\\server\\path\\subdir"] = pair{"\\\\server\\path\\", "subdir"}
}
for input, want := range cases {
require.Equal(t, want, dirPrefix(input), input)
basename, prefix := splitDirPrefix(input)
require.Equal(t, want.basename, basename, input)
require.Equal(t, want.prefix, prefix, input)
}
}