github.com/anthropics/anthropic-sdk-go@v1.70.1/internal/filestore/filestore_unix.go (about) 1 //go:build !windows 2 3 package filestore 4 5 import ( 6 "crypto/rand" 7 "encoding/hex" 8 "errors" 9 "os" 10 "path/filepath" 11 "syscall" 12 ) 13 14 // platformCheck returns an error when the platform cannot uphold the store's 15 // guarantees; unix has O_NOFOLLOW, so it never fails here. 16 func platformCheck() error { return nil } 17 18 // replaceViaTemp writes data atomically: a random ".fs-<hex>.tmp" sibling is 19 // created with O_EXCL|O_NOFOLLOW, then renamed onto dest — a reader never 20 // sees a half-written file, and a symlink at the leaf is replaced, not 21 // followed. Best-effort temp cleanup on failure; never masks the original 22 // error. 23 func replaceViaTemp(dest string, data []byte, isExecutable bool) error { 24 mode := ownerOnlyFileMode 25 if isExecutable { 26 mode = ownerOnlyExecMode 27 } 28 var rnd [8]byte 29 _, _ = rand.Read(rnd[:]) 30 tmp := filepath.Join(filepath.Dir(dest), ".fs-"+hex.EncodeToString(rnd[:])+".tmp") 31 f, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_EXCL|syscall.O_NOFOLLOW, mode) 32 if err != nil { 33 return err 34 } 35 if _, err := f.Write(data); err != nil { 36 _ = f.Close() 37 _ = os.Remove(tmp) 38 return err 39 } 40 if err := f.Close(); err != nil { 41 _ = os.Remove(tmp) 42 return err 43 } 44 if err := os.Rename(tmp, dest); err != nil { 45 _ = os.Remove(tmp) 46 return err 47 } 48 return nil 49 } 50 51 // openRegularFile opens dest for reading without following a symlink leaf; 52 // only a regular file comes back. O_NONBLOCK so a FIFO fails the stat check 53 // below instead of blocking the open forever. 54 func openRegularFile(rel, dest string) (*os.File, error) { 55 f, err := os.OpenFile(dest, os.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_NONBLOCK, 0) 56 if err != nil { 57 // FreeBSD reports EMLINK rather than ELOOP for O_NOFOLLOW. 58 if errors.Is(err, syscall.ELOOP) || errors.Is(err, syscall.EMLINK) { 59 return nil, &FileStoreError{Reason: ReasonIsASymlink, RelPath: rel} 60 } 61 return nil, err 62 } 63 st, err := f.Stat() 64 if err != nil { 65 _ = f.Close() 66 return nil, err 67 } 68 if !st.Mode().IsRegular() { 69 _ = f.Close() 70 return nil, &FileStoreError{Reason: ReasonNotAFile, RelPath: rel} 71 } 72 return f, nil 73 }