fix(backup): privately stage restore archive

This commit is contained in:
2026-08-18 08:13:13 +02:00
parent df00f6bfa8
commit 54698e7340
8 changed files with 178 additions and 23 deletions
+37 -16
View File
@@ -275,25 +275,24 @@ func WriteCanonicalNewPrivateFile(path string, contents []byte, mode os.FileMode
return writeCanonicalNewFile(path, contents, mode, true)
}
// CreateCanonicalNewPrivateFile exclusively creates an owner-private regular file under an
// already private parent and returns a read/write handle for streamed contents. Callers must close
// the returned handle and remove the file if their stream fails.
func CreateCanonicalNewPrivateFile(path string) (*os.File, error) {
if err := validateCanonicalNewFile(path, true); err != nil {
return nil, err
}
file, err := createCanonicalNewPrivateParentReadWriteFile(path, 0o600)
if err != nil {
return nil, ErrUnsafeFile
}
return file, nil
}
func writeCanonicalNewFile(path string, contents []byte, mode os.FileMode, requirePrivateParent bool) error {
if err := ValidateCanonicalPath(path); err != nil {
if err := validateCanonicalNewFile(path, requirePrivateParent); err != nil {
return err
}
parent := filepath.Dir(path)
if err := requireCanonicalDirectory(parent); err != nil {
return err
}
if requirePrivateParent && ValidatePrivateDirectory(parent) != nil {
return ErrUnsafeFile
}
if info, err := os.Lstat(path); err == nil {
if !info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 || info.Mode()&os.ModeType != 0 {
return ErrUnsafeFile
}
return ErrUnsafeFile
} else if !errors.Is(err, os.ErrNotExist) {
return ErrUnsafeFile
}
var (
file *os.File
err error
@@ -323,6 +322,28 @@ func writeCanonicalNewFile(path string, contents []byte, mode os.FileMode, requi
return nil
}
func validateCanonicalNewFile(path string, requirePrivateParent bool) error {
if err := ValidateCanonicalPath(path); err != nil {
return err
}
parent := filepath.Dir(path)
if err := requireCanonicalDirectory(parent); err != nil {
return err
}
if requirePrivateParent && ValidatePrivateDirectory(parent) != nil {
return ErrUnsafeFile
}
if info, err := os.Lstat(path); err == nil {
if !info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 || info.Mode()&os.ModeType != 0 {
return ErrUnsafeFile
}
return ErrUnsafeFile
} else if !errors.Is(err, os.ErrNotExist) {
return ErrUnsafeFile
}
return nil
}
// ReplaceCanonicalRegular durably replaces one existing private regular file without following
// symlinked path components. Platform implementations keep the temporary file in the target
// directory and use the platform's atomic replace primitive.