vmbackupmanager: fixes for windows compatibility (#641)

* app/vmbackupmanager/storage: fix path join for windows

See: https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4704

Signed-off-by: Zakhar Bessarab <z.bessarab@victoriametrics.com>

* lib/backup: fixes for windows support

- close dir before running os.RemoveAll. Windows FS does not allow to delete directory before all handles will be closed.

- add path "normalization" for local FS to use the same format of paths for both *unix and Windows

See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4704

Signed-off-by: Zakhar Bessarab <z.bessarab@victoriametrics.com>

---------

Signed-off-by: Zakhar Bessarab <z.bessarab@victoriametrics.com>
This commit is contained in:
Zakhar Bessarab 2023-08-01 11:18:04 +04:00 committed by Aliaksandr Valialkin
parent 7602b95bcb
commit f2df8ad480
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
7 changed files with 60 additions and 5 deletions

View file

@ -52,7 +52,7 @@ func appendFilesInternal(dst []string, d *os.File) ([]string, error) {
// Process directory // Process directory
dst, err = AppendFiles(dst, path) dst, err = AppendFiles(dst, path)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot list %q: %w", path, err) return nil, fmt.Errorf("cannot append files %q: %w", path, err)
} }
continue continue
} }
@ -124,9 +124,6 @@ func removeEmptyDirs(dir string) (bool, error) {
return false, err return false, err
} }
ok, err := removeEmptyDirsInternal(d) ok, err := removeEmptyDirsInternal(d)
if err1 := d.Close(); err1 != nil {
err = err1
}
if err != nil { if err != nil {
return false, err return false, err
} }
@ -157,7 +154,7 @@ func removeEmptyDirsInternal(d *os.File) (bool, error) {
// Process directory // Process directory
ok, err := removeEmptyDirs(path) ok, err := removeEmptyDirs(path)
if err != nil { if err != nil {
return false, fmt.Errorf("cannot list %q: %w", path, err) return false, fmt.Errorf("cannot remove empty dirs %q: %w", path, err)
} }
if !ok { if !ok {
dirEntries++ dirEntries++
@ -221,6 +218,9 @@ func removeEmptyDirsInternal(d *os.File) (bool, error) {
if dirEntries > 0 { if dirEntries > 0 {
return false, nil return false, nil
} }
if err := d.Close(); err != nil {
return false, fmt.Errorf("cannot close %q: %w", dir, err)
}
// Use os.RemoveAll() instead of os.Remove(), since the dir may contain special files such as flock.lock and backupnames.RestoreInProgressFilename, // Use os.RemoveAll() instead of os.Remove(), since the dir may contain special files such as flock.lock and backupnames.RestoreInProgressFilename,
// which must be ignored. // which must be ignored.
if err := os.RemoveAll(dir); err != nil { if err := os.RemoveAll(dir); err != nil {

View file

@ -68,6 +68,7 @@ func (fs *FS) ListParts() ([]common.Part, error) {
return nil, fmt.Errorf("cannot stat file %q for part %q: %w", file, p.Path, err) return nil, fmt.Errorf("cannot stat file %q for part %q: %w", file, p.Path, err)
} }
p.ActualSize = uint64(fi.Size()) p.ActualSize = uint64(fi.Size())
p.Path = pathToCanonical(p.Path)
parts = append(parts, p) parts = append(parts, p)
} }
return parts, nil return parts, nil
@ -75,6 +76,7 @@ func (fs *FS) ListParts() ([]common.Part, error) {
// DeletePart deletes the given part p from fs. // DeletePart deletes the given part p from fs.
func (fs *FS) DeletePart(p common.Part) error { func (fs *FS) DeletePart(p common.Part) error {
p.Path = canonicalPathToLocal(p.Path)
path := fs.path(p) path := fs.path(p)
if err := os.Remove(path); err != nil { if err := os.Remove(path); err != nil {
return fmt.Errorf("cannot remove %q: %w", path, err) return fmt.Errorf("cannot remove %q: %w", path, err)
@ -95,6 +97,7 @@ func (fs *FS) CopyPart(srcFS common.OriginFS, p common.Part) error {
if !ok { if !ok {
return fmt.Errorf("cannot perform server-side copying from %s to %s: both of them must be fsremote", srcFS, fs) return fmt.Errorf("cannot perform server-side copying from %s to %s: both of them must be fsremote", srcFS, fs)
} }
p.Path = canonicalPathToLocal(p.Path)
srcPath := src.path(p) srcPath := src.path(p)
dstPath := fs.path(p) dstPath := fs.path(p)
if err := fs.mkdirAll(dstPath); err != nil { if err := fs.mkdirAll(dstPath); err != nil {
@ -139,6 +142,7 @@ func (fs *FS) CopyPart(srcFS common.OriginFS, p common.Part) error {
// DownloadPart download part p from fs to w. // DownloadPart download part p from fs to w.
func (fs *FS) DownloadPart(p common.Part, w io.Writer) error { func (fs *FS) DownloadPart(p common.Part, w io.Writer) error {
p.Path = canonicalPathToLocal(p.Path)
path := fs.path(p) path := fs.path(p)
r, err := os.Open(path) r, err := os.Open(path)
if err != nil { if err != nil {
@ -201,6 +205,7 @@ func (fs *FS) path(p common.Part) string {
// //
// The function does nothing if the filePath doesn't exist. // The function does nothing if the filePath doesn't exist.
func (fs *FS) DeleteFile(filePath string) error { func (fs *FS) DeleteFile(filePath string) error {
filePath = canonicalPathToLocal(filePath)
path := filepath.Join(fs.Dir, filePath) path := filepath.Join(fs.Dir, filePath)
err := os.Remove(path) err := os.Remove(path)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {

View file

@ -0,0 +1,12 @@
//go:build freebsd || openbsd || dragonfly || netbsd
// +build freebsd openbsd dragonfly netbsd
package fsremote
func pathToCanonical(path string) string {
return path
}
func canonicalPathToLocal(path string) string {
return path
}

View file

@ -0,0 +1,9 @@
package fsremote
func pathToCanonical(path string) string {
return path
}
func canonicalPathToLocal(path string) string {
return path
}

View file

@ -0,0 +1,9 @@
package fsremote
func pathToCanonical(path string) string {
return path
}
func canonicalPathToLocal(path string) string {
return path
}

View file

@ -0,0 +1,9 @@
package fsremote
func pathToCanonical(path string) string {
return path
}
func canonicalPathToLocal(path string) string {
return path
}

View file

@ -0,0 +1,11 @@
package fsremote
import "strings"
func pathToCanonical(path string) string {
return strings.ReplaceAll(path, "\\", "/")
}
func canonicalPathToLocal(path string) string {
return strings.ReplaceAll(path, "/", "\\")
}