48 lines
1.5 KiB
Go
48 lines
1.5 KiB
Go
//go:build windows
|
|
|
|
package backup
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/aritmolab/thothii/tools/tht/internal/safeio"
|
|
)
|
|
|
|
// replaceRestoreFile deliberately supports only owner-private Windows parents. The retained
|
|
// native directory handle pins every ancestor, rejects reparse components, creates an owner-only
|
|
// temporary file, and publishes it by an NT RootDirectory-relative atomic rename. Installations
|
|
// whose restore targets do not satisfy that custody contract fail closed instead of falling back
|
|
// to a path-based replacement.
|
|
func replaceRestoreFile(target string, contents []byte, mode os.FileMode) (resultErr error) {
|
|
if safeio.ValidateCanonicalPath(target) != nil || mode&os.ModeType != 0 || mode.Perm() == 0 || len(contents) == 0 {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
parent, found, err := safeio.OpenPrivateDirectory(filepath.Dir(target), false)
|
|
if err != nil || !found || parent == nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
defer func() {
|
|
if closeErr := parent.Close(); closeErr != nil {
|
|
resultErr = safeio.ErrUnsafeFile
|
|
}
|
|
}()
|
|
safeio.NotifyPrivateDirectoryTestHookForTest("after-restore-parent-open")
|
|
if parent.Validate() != nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
created, err := parent.CreateRegular(filepath.Base(target), contents)
|
|
if err != nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
if !created {
|
|
if err := parent.ReplaceRegular(filepath.Base(target), contents); err != nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
}
|
|
if parent.Validate() != nil {
|
|
return safeio.ErrUnsafeFile
|
|
}
|
|
return nil
|
|
}
|