github.com/quay/claircore@v1.5.28/libindex/tempfile_linux.go (about) 1 package libindex 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "sync" 8 9 "golang.org/x/sys/unix" 10 ) 11 12 var tmpMap sync.Map 13 14 func canTmp(dir string) (ok, loaded bool) { 15 v, loaded := tmpMap.Load(dir) 16 if v == nil { 17 return false, loaded 18 } 19 return v.(bool), loaded 20 } 21 22 func setTmp(dir string, ok bool) { 23 tmpMap.Store(dir, ok) 24 } 25 26 type tempFile struct { 27 *os.File 28 } 29 30 func openTemp(dir string) (*tempFile, error) { 31 var f *os.File 32 var err error 33 34 ok, loaded := canTmp(dir) 35 switch { 36 case loaded && ok: 37 f, err = os.OpenFile(dir, os.O_WRONLY|unix.O_TMPFILE, 0644) 38 case loaded && !ok: 39 f, err = os.CreateTemp(dir, "fetcher.*") 40 case !loaded: 41 f, err = os.OpenFile(dir, os.O_WRONLY|unix.O_TMPFILE, 0644) 42 if err == nil || !errors.Is(err, unix.ENOTSUP) { 43 ok = true 44 break 45 } 46 f, err = os.CreateTemp(dir, "fetcher.*") 47 default: 48 panic("unreachable") 49 } 50 if !loaded { 51 setTmp(dir, ok) 52 } 53 if !ok && err == nil { 54 // This is just a best-effort action to keep files from accumulating. 55 // The correct way is to use the kernel feature for this: the O_TMPFILE flag. 56 _ = os.Remove(f.Name()) 57 } 58 59 if err != nil { 60 return nil, err 61 } 62 return &tempFile{File: f}, nil 63 } 64 65 func (t *tempFile) Reopen() (*os.File, error) { 66 fd := int(t.Fd()) 67 if fd == -1 { 68 return nil, errStale 69 } 70 p := fmt.Sprintf("/proc/self/fd/%d", fd) 71 // Need to use OpenFile so that the symlink is not dereferenced. 72 // There's some proc magic so that opening that symlink itself copies the 73 // description. 74 return os.OpenFile(p, os.O_RDONLY, 0644) 75 }