66 lines
1.5 KiB
Go
66 lines
1.5 KiB
Go
package store
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
func persistPrivateData(dir, name string, data []byte) error {
|
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
return err
|
|
}
|
|
info, err := os.Lstat(dir)
|
|
if err != nil || !info.IsDir() || info.Mode().Perm() != 0700 {
|
|
return errors.New("private data directory must have mode 0700")
|
|
}
|
|
target := filepath.Join(dir, name)
|
|
if info, err := os.Lstat(target); err == nil {
|
|
if !info.Mode().IsRegular() || info.Mode().Perm() != 0600 {
|
|
return errors.New("existing private data must be a regular 0600 file")
|
|
}
|
|
existing, err := os.ReadFile(target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(existing, data) {
|
|
return errors.New("private original data conflicts with existing file")
|
|
}
|
|
f, err := os.Open(target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := errors.Join(f.Sync(), f.Close()); err != nil {
|
|
return err
|
|
}
|
|
return syncPrivateDirectory(dir)
|
|
} else if !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
f, err := os.CreateTemp(dir, ".pending-")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
temp := f.Name()
|
|
defer os.Remove(temp)
|
|
_, writeErr := f.Write(data)
|
|
syncErr := f.Sync()
|
|
closeErr := f.Close()
|
|
if err := errors.Join(writeErr, syncErr, closeErr); err != nil {
|
|
return fmt.Errorf("persist private original bytes: %w", err)
|
|
}
|
|
if err := os.Rename(temp, target); err != nil {
|
|
return err
|
|
}
|
|
return syncPrivateDirectory(dir)
|
|
}
|
|
func syncPrivateDirectory(dir string) error {
|
|
f, err := os.Open(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return errors.Join(f.Sync(), f.Close())
|
|
}
|