//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 }