mirror of
https://github.com/kopia/kopia.git
synced 2026-05-18 11:44:36 -04:00
fix(snapshots): fixed snapshotting of \\server\share (#4603)
* fix(snapshots): fixed snapshotting of \\server\share * fixed linter
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user