Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384package config
import ( "crypto/rand" "encoding/hex" "encoding/json" "fmt" "os" "path/filepath" "time")
// lockFile takes an exclusive lock file, retrying every 20ms up to attempts// times. The error wraps os.ErrExist when another holder kept it throughout.func lockFile(path string, attempts int) (release func(), err error) { for attempt := 0; attempt < attempts; attempt++ { if attempt > 0 { time.Sleep(20 * time.Millisecond) } var file *os.File file, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) if err == nil { return func() { _ = file.Close() _ = os.Remove(path) }, nil } if !os.IsExist(err) { return nil, err } } return nil, err}
// lockedUpdate prepares directory as a private store and runs update while// holding lockName inside it.func lockedUpdate(directory, lockName string, attempts int, update func() error) error { if err := os.MkdirAll(directory, 0700); err != nil { return err } _ = os.Chmod(directory, 0700) release, err := lockFile(filepath.Join(directory, lockName), attempts) if err != nil { return err } defer release() return update()}
// writeJSONAtomic replaces directory/name with indented JSON, mode 0600, via a// synced temp file so readers never see a partial write.func writeJSONAtomic(directory, name string, value any) error { encoded, err := json.MarshalIndent(value, "", " ") if err != nil { return err } encoded = append(encoded, '\n')
randomBytes := make([]byte, 16) _, _ = rand.Read(randomBytes) stem := name[:len(name)-len(filepath.Ext(name))] tempPath := filepath.Join(directory, fmt.Sprintf("%s.%s.tmp", stem, hex.EncodeToString(randomBytes)))
tempFile, err := os.OpenFile(tempPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) if err != nil { return err } defer func() { _ = tempFile.Close() _ = os.Remove(tempPath) }()
if _, err := tempFile.Write(encoded); err != nil { return err } if err := tempFile.Sync(); err != nil { return err } if err := tempFile.Close(); err != nil { return err } return os.Rename(tempPath, filepath.Join(directory, name))}