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