Live apply now accepts only :5433/agent_system_dev plus a data/dev host root, maps stored work dirs fail-closed, and reports unique O_EXCL backups after a parent-directory fsync. Co-authored-by: Cursor <cursoragent@cursor.com>
103 lines
2.4 KiB
Go
103 lines
2.4 KiB
Go
package fsutil
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
)
|
|
|
|
// dirSync is the directory durability hook; tests replace it.
|
|
var dirSync = syncDirImpl
|
|
|
|
// WriteFileAtomic writes data via temp file + Sync + rename so readers never see a partial file.
|
|
// After a successful rename the parent directory is opened and Sync'd.
|
|
func WriteFileAtomic(path string, data []byte, perm os.FileMode) error {
|
|
return writeFileAtomic(path, data, perm, false)
|
|
}
|
|
|
|
// WriteFileAtomicExclusive is WriteFileAtomic but creates the destination with O_EXCL.
|
|
func WriteFileAtomicExclusive(path string, data []byte, perm os.FileMode) error {
|
|
return writeFileAtomic(path, data, perm, true)
|
|
}
|
|
|
|
func writeFileAtomic(path string, data []byte, perm os.FileMode, exclusive bool) error {
|
|
dir := filepath.Dir(path)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
if exclusive {
|
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, perm)
|
|
if err != nil {
|
|
if os.IsExist(err) {
|
|
return fmt.Errorf("fsutil: refuse overwrite of existing %s", path)
|
|
}
|
|
return err
|
|
}
|
|
if cerr := f.Close(); cerr != nil {
|
|
return cerr
|
|
}
|
|
}
|
|
tmp, err := os.CreateTemp(dir, "."+filepath.Base(path)+".*.tmp")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmpName := tmp.Name()
|
|
cleanup := true
|
|
defer func() {
|
|
if cleanup {
|
|
_ = os.Remove(tmpName)
|
|
}
|
|
}()
|
|
if _, err := tmp.Write(data); err != nil {
|
|
_ = tmp.Close()
|
|
return fmt.Errorf("atomic write: %w", err)
|
|
}
|
|
if err := tmp.Sync(); err != nil {
|
|
_ = tmp.Close()
|
|
return fmt.Errorf("atomic sync: %w", err)
|
|
}
|
|
if err := tmp.Close(); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Chmod(tmpName, perm); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Rename(tmpName, path); err != nil {
|
|
return err
|
|
}
|
|
cleanup = false
|
|
if err := dirSync(dir); err != nil {
|
|
if ignorableDirSync(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SyncDir fsyncs a directory so create/rename/unlink entries become durable.
|
|
func SyncDir(dir string) error {
|
|
return dirSync(dir)
|
|
}
|
|
|
|
func syncDirImpl(dir string) error {
|
|
d, err := os.Open(dir)
|
|
if err != nil {
|
|
return fmt.Errorf("fsutil: open dir %s: %w", dir, err)
|
|
}
|
|
defer d.Close()
|
|
if err := d.Sync(); err != nil {
|
|
if ignorableDirSync(err) {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("fsutil: sync dir %s: %w", dir, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ignorableDirSync(err error) bool {
|
|
return errors.Is(err, syscall.EINVAL) || errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOSYS)
|
|
}
|