60 lines
1.6 KiB
Go
60 lines
1.6 KiB
Go
//go:build windows
|
|
|
|
package backup
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/aritmolab/thothii/tools/tht/internal/safeio"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
func replaceRestoreFile(target string, contents []byte, mode os.FileMode) error {
|
|
if safeio.ValidateCanonicalPath(target) != nil || mode&os.ModeType != 0 || mode.Perm() == 0 {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
parent := filepath.Dir(target)
|
|
resolved, err := filepath.EvalSymlinks(parent)
|
|
if err != nil || resolved != parent || !safeWindowsRestoreTarget(target) {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
temporary, err := os.CreateTemp(parent, ".tht-restore-*.tmp")
|
|
if err != nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
temporaryPath := temporary.Name()
|
|
defer os.Remove(temporaryPath)
|
|
if err := temporary.Chmod(mode.Perm()); err == nil {
|
|
_, err = temporary.Write(contents)
|
|
}
|
|
if err == nil {
|
|
err = temporary.Sync()
|
|
}
|
|
closeErr := temporary.Close()
|
|
if err == nil {
|
|
err = closeErr
|
|
}
|
|
if err != nil || !safeWindowsRestoreTarget(target) {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
from, fromErr := windows.UTF16PtrFromString(temporaryPath)
|
|
to, toErr := windows.UTF16PtrFromString(target)
|
|
if fromErr != nil || toErr != nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
if err := windows.MoveFileEx(from, to, windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH); err != nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func safeWindowsRestoreTarget(target string) bool {
|
|
info, err := os.Lstat(target)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return true
|
|
}
|
|
return err == nil && info.Mode().IsRegular() && info.Mode()&os.ModeSymlink == 0
|
|
}
|