tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/memfs/file.go (about) 1 // Copyright © 2014 Steve Francia <spf@spf13.com>. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package memfs 15 16 import ( 17 "bytes" 18 "errors" 19 "io" 20 "io/fs" 21 "os" 22 "path/filepath" 23 "sync" 24 "sync/atomic" 25 "time" 26 ) 27 28 var ErrOutOfRange = errors.New("Out of range") 29 30 type File struct { 31 // atomic requires 64-bit alignment for struct field access 32 at int64 33 readDirCount int64 34 closed bool 35 readOnly bool 36 fileData *FileData 37 } 38 39 func NewFileHandle(data *FileData) *File { 40 return &File{fileData: data} 41 } 42 43 func NewROFileHandle(data *FileData) *File { 44 return &File{fileData: data, readOnly: true} 45 } 46 47 func (f File) Data() *FileData { 48 return f.fileData 49 } 50 51 type FileData struct { 52 sync.Mutex 53 name string 54 data []byte 55 memDir Dir 56 dir bool 57 mode fs.FileMode 58 modtime time.Time 59 uid int 60 gid int 61 } 62 63 func (d *FileData) Name() string { 64 d.Lock() 65 defer d.Unlock() 66 return d.name 67 } 68 69 func CreateFile(name string) *FileData { 70 return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()} 71 } 72 73 func CreateDir(name string) *FileData { 74 return &FileData{name: name, memDir: &DirMap{}, dir: true} 75 } 76 77 func ChangeFileName(f *FileData, newname string) { 78 f.Lock() 79 f.name = newname 80 f.Unlock() 81 } 82 83 func SetMode(f *FileData, mode fs.FileMode) { 84 f.Lock() 85 f.mode = mode 86 f.Unlock() 87 } 88 89 func SetModTime(f *FileData, mtime time.Time) { 90 f.Lock() 91 setModTime(f, mtime) 92 f.Unlock() 93 } 94 95 func setModTime(f *FileData, mtime time.Time) { 96 f.modtime = mtime 97 } 98 99 func SetUID(f *FileData, uid int) { 100 f.Lock() 101 f.uid = uid 102 f.Unlock() 103 } 104 105 func SetGID(f *FileData, gid int) { 106 f.Lock() 107 f.gid = gid 108 f.Unlock() 109 } 110 111 func GetFileInfo(f *FileData) *FileInfo { 112 return &FileInfo{f} 113 } 114 115 func (f *File) Open() error { 116 atomic.StoreInt64(&f.at, 0) 117 atomic.StoreInt64(&f.readDirCount, 0) 118 f.fileData.Lock() 119 f.closed = false 120 f.fileData.Unlock() 121 return nil 122 } 123 124 func (f *File) Close() error { 125 f.fileData.Lock() 126 f.closed = true 127 if !f.readOnly { 128 setModTime(f.fileData, time.Now()) 129 } 130 f.fileData.Unlock() 131 return nil 132 } 133 134 func (f *File) Name() string { 135 return f.fileData.Name() 136 } 137 138 func (f *File) Stat() (fs.FileInfo, error) { 139 return &FileInfo{f.fileData}, nil 140 } 141 142 func (f *File) Sync() error { 143 return nil 144 } 145 146 func (f *File) ReadDir(n int) ([]fs.DirEntry, error) { 147 var entries []fs.DirEntry 148 dirs, err := f.Readdir(n) 149 if err != nil { 150 return nil, err 151 } 152 for _, dir := range dirs { 153 entries = append(entries, dirEntry{ 154 name: dir.Name(), 155 isDir: dir.IsDir(), 156 typ: dir.Mode().Type(), 157 info: dir, 158 }) 159 } 160 return entries, nil 161 } 162 163 func (f *File) Readdir(count int) (res []fs.FileInfo, err error) { 164 if !f.fileData.dir { 165 return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")} 166 } 167 var outLength int64 168 169 f.fileData.Lock() 170 files := f.fileData.memDir.Files()[f.readDirCount:] 171 if count > 0 { 172 if len(files) < count { 173 outLength = int64(len(files)) 174 } else { 175 outLength = int64(count) 176 } 177 if len(files) == 0 { 178 err = io.EOF 179 } 180 } else { 181 outLength = int64(len(files)) 182 } 183 f.readDirCount += outLength 184 f.fileData.Unlock() 185 186 res = make([]fs.FileInfo, outLength) 187 for i := range res { 188 res[i] = &FileInfo{files[i]} 189 } 190 191 return res, err 192 } 193 194 func (f *File) Readdirnames(n int) (names []string, err error) { 195 fi, err := f.Readdir(n) 196 names = make([]string, len(fi)) 197 for i, f := range fi { 198 _, names[i] = filepath.Split(f.Name()) 199 } 200 return names, err 201 } 202 203 func (f *File) Read(b []byte) (n int, err error) { 204 f.fileData.Lock() 205 defer f.fileData.Unlock() 206 if f.closed == true { 207 return 0, fs.ErrClosed 208 } 209 if len(b) > 0 && int(f.at) == len(f.fileData.data) { 210 return 0, io.EOF 211 } 212 if int(f.at) > len(f.fileData.data) { 213 return 0, io.ErrUnexpectedEOF 214 } 215 if len(f.fileData.data)-int(f.at) >= len(b) { 216 n = len(b) 217 } else { 218 n = len(f.fileData.data) - int(f.at) 219 } 220 copy(b, f.fileData.data[f.at:f.at+int64(n)]) 221 atomic.AddInt64(&f.at, int64(n)) 222 return 223 } 224 225 func (f *File) ReadAt(b []byte, off int64) (n int, err error) { 226 prev := atomic.LoadInt64(&f.at) 227 atomic.StoreInt64(&f.at, off) 228 n, err = f.Read(b) 229 atomic.StoreInt64(&f.at, prev) 230 return 231 } 232 233 func (f *File) Truncate(size int64) error { 234 if f.closed == true { 235 return fs.ErrClosed 236 } 237 if f.readOnly { 238 return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")} 239 } 240 if size < 0 { 241 return ErrOutOfRange 242 } 243 f.fileData.Lock() 244 defer f.fileData.Unlock() 245 if size > int64(len(f.fileData.data)) { 246 diff := size - int64(len(f.fileData.data)) 247 f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...) 248 } else { 249 f.fileData.data = f.fileData.data[0:size] 250 } 251 setModTime(f.fileData, time.Now()) 252 return nil 253 } 254 255 func (f *File) Seek(offset int64, whence int) (int64, error) { 256 if f.closed == true { 257 return 0, fs.ErrClosed 258 } 259 switch whence { 260 case io.SeekStart: 261 atomic.StoreInt64(&f.at, offset) 262 case io.SeekCurrent: 263 atomic.AddInt64(&f.at, offset) 264 case io.SeekEnd: 265 atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset) 266 } 267 return f.at, nil 268 } 269 270 func (f *File) Write(b []byte) (n int, err error) { 271 if f.closed == true { 272 return 0, fs.ErrClosed 273 } 274 if f.readOnly { 275 return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")} 276 } 277 n = len(b) 278 cur := atomic.LoadInt64(&f.at) 279 f.fileData.Lock() 280 defer f.fileData.Unlock() 281 diff := cur - int64(len(f.fileData.data)) 282 var tail []byte 283 if n+int(cur) < len(f.fileData.data) { 284 tail = f.fileData.data[n+int(cur):] 285 } 286 if diff > 0 { 287 f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{00}, int(diff)), b...)...) 288 f.fileData.data = append(f.fileData.data, tail...) 289 } else { 290 f.fileData.data = append(f.fileData.data[:cur], b...) 291 f.fileData.data = append(f.fileData.data, tail...) 292 } 293 setModTime(f.fileData, time.Now()) 294 295 atomic.AddInt64(&f.at, int64(n)) 296 return 297 } 298 299 func (f *File) WriteAt(b []byte, off int64) (n int, err error) { 300 atomic.StoreInt64(&f.at, off) 301 return f.Write(b) 302 } 303 304 func (f *File) WriteString(s string) (ret int, err error) { 305 return f.Write([]byte(s)) 306 } 307 308 func (f *File) Info() *FileInfo { 309 return &FileInfo{f.fileData} 310 } 311 312 type FileInfo struct { 313 *FileData 314 } 315 316 // Implements fs.FileInfo 317 func (s *FileInfo) Name() string { 318 s.Lock() 319 _, name := filepath.Split(s.name) 320 s.Unlock() 321 return name 322 } 323 func (s *FileInfo) Mode() fs.FileMode { 324 s.Lock() 325 defer s.Unlock() 326 return s.mode 327 } 328 func (s *FileInfo) ModTime() time.Time { 329 s.Lock() 330 defer s.Unlock() 331 return s.modtime 332 } 333 func (s *FileInfo) IsDir() bool { 334 s.Lock() 335 defer s.Unlock() 336 return s.dir 337 } 338 func (s *FileInfo) Sys() interface{} { return nil } 339 func (s *FileInfo) Size() int64 { 340 if s.IsDir() { 341 return int64(42) 342 } 343 s.Lock() 344 defer s.Unlock() 345 return int64(len(s.data)) 346 }