tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/util.go (about) 1 package fs 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 "strconv" 9 "sync" 10 "time" 11 ) 12 13 // todo: Remove 14 // todo: MkDir 15 16 func OpenFile(fsys FS, name string, flag int, perm os.FileMode) (File, error) { 17 fsopenfile, ok := fsys.(interface { 18 OpenFile(name string, flag int, perm os.FileMode) (File, error) 19 }) 20 if !ok { 21 return nil, fmt.Errorf("unable to openfile on fs") 22 } 23 return fsopenfile.OpenFile(name, flag, perm) 24 } 25 26 func MkdirAll(fsys FS, path string, perm FileMode) error { 27 fsmkdir, ok := fsys.(interface { 28 MkdirAll(path string, perm FileMode) error 29 }) 30 if !ok { 31 return fmt.Errorf("unable to mkdirall on fs") 32 } 33 return fsmkdir.MkdirAll(path, perm) 34 } 35 36 func DirExists(fsys FS, path string) (bool, error) { 37 fi, err := Stat(fsys, path) 38 if err == nil && fi.IsDir() { 39 return true, nil 40 } 41 if os.IsNotExist(err) { 42 return false, nil 43 } 44 return false, err 45 } 46 47 func IsDir(fsys FS, path string) (bool, error) { 48 fi, err := Stat(fsys, path) 49 if err != nil { 50 return false, err 51 } 52 return fi.IsDir(), nil 53 } 54 55 func IsEmpty(fsys FS, path string) (bool, error) { 56 if b, _ := Exists(fsys, path); !b { 57 return false, fmt.Errorf("path does not exist: %q", path) 58 } 59 fi, err := Stat(fsys, path) 60 if err != nil { 61 return false, err 62 } 63 if fi.IsDir() { 64 f, err := fsys.Open(path) 65 if err != nil { 66 return false, err 67 } 68 defer f.Close() 69 list, err := ReadDir(fsys, path) 70 return len(list) == 0, nil 71 } 72 return fi.Size() == 0, nil 73 } 74 75 func Exists(fsys FS, path string) (bool, error) { 76 _, err := Stat(fsys, path) 77 if err == nil { 78 return true, nil 79 } 80 if os.IsNotExist(err) { 81 return false, nil 82 } 83 return false, err 84 } 85 86 func WriteFile(fsys FS, filename string, data []byte, perm FileMode) error { 87 of, ok := fsys.(interface { 88 OpenFile(name string, flag int, perm FileMode) (File, error) 89 }) 90 if !ok { 91 return ErrPermission 92 } 93 f, err := of.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) 94 if err != nil { 95 return err 96 } 97 fw, ok := f.(io.WriteCloser) 98 if !ok { 99 f.Close() 100 return ErrPermission 101 } 102 n, err := fw.Write(data) 103 if err == nil && n < len(data) { 104 err = io.ErrShortWrite 105 } 106 if err1 := fw.Close(); err == nil { 107 err = err1 108 } 109 return err 110 } 111 112 // Random number state. 113 // We generate random temporary file names so that there's a good 114 // chance the file doesn't exist yet - keeps the number of tries in 115 // TempFile to a minimum. 116 var rand uint32 117 var randmu sync.Mutex 118 119 func reseed() uint32 { 120 return uint32(time.Now().UnixNano() + int64(os.Getpid())) 121 } 122 123 func nextRandom() string { 124 randmu.Lock() 125 r := rand 126 if r == 0 { 127 r = reseed() 128 } 129 r = r*1664525 + 1013904223 // constants from Numerical Recipes 130 rand = r 131 randmu.Unlock() 132 return strconv.Itoa(int(1e9 + r%1e9))[1:] 133 } 134 135 func TempDir(fsys FS, dir, prefix string) (name string, err error) { 136 if dir == "" { 137 dir = os.TempDir() 138 } 139 140 nconflict := 0 141 for i := 0; i < 10000; i++ { 142 try := filepath.Join(dir, prefix+nextRandom()) 143 fmkd, ok := fsys.(interface { 144 Mkdir(name string, perm FileMode) error 145 }) 146 if !ok { 147 return name, ErrPermission 148 } 149 err = fmkd.Mkdir(try, 0700) 150 if os.IsExist(err) { 151 if nconflict++; nconflict > 10 { 152 randmu.Lock() 153 rand = reseed() 154 randmu.Unlock() 155 } 156 continue 157 } 158 if err == nil { 159 name = try 160 } 161 break 162 } 163 return 164 }