github.com/mferrell/afero@v1.8.3-0.20220319163648-1d8d1d1d8040/mem/file.go (about) 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 "os" 22 "path/filepath" 23 "sync" 24 "sync/atomic" 25 "time" 26 ) 27 28 const FilePathSeparator = string(filepath.Separator) 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 NewReadOnlyFileHandle(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 os.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, modtime: time.Now()} 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 os.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() (os.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(count int) (res []os.FileInfo, err error) { 147 if !f.fileData.dir { 148 return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")} 149 } 150 var outLength int64 151 152 f.fileData.Lock() 153 files := f.fileData.memDir.Files()[f.readDirCount:] 154 if count > 0 { 155 if len(files) < count { 156 outLength = int64(len(files)) 157 } else { 158 outLength = int64(count) 159 } 160 if len(files) == 0 { 161 err = io.EOF 162 } 163 } else { 164 outLength = int64(len(files)) 165 } 166 f.readDirCount += outLength 167 f.fileData.Unlock() 168 169 res = make([]os.FileInfo, outLength) 170 for i := range res { 171 res[i] = &FileInfo{files[i]} 172 } 173 174 return res, err 175 } 176 177 func (f *File) Readdirnames(n int) (names []string, err error) { 178 fi, err := f.Readdir(n) 179 names = make([]string, len(fi)) 180 for i, f := range fi { 181 _, names[i] = filepath.Split(f.Name()) 182 } 183 return names, err 184 } 185 186 func (f *File) Read(b []byte) (n int, err error) { 187 f.fileData.Lock() 188 defer f.fileData.Unlock() 189 if f.closed == true { 190 return 0, ErrFileClosed 191 } 192 if len(b) > 0 && int(f.at) == len(f.fileData.data) { 193 return 0, io.EOF 194 } 195 if int(f.at) > len(f.fileData.data) { 196 return 0, io.ErrUnexpectedEOF 197 } 198 if len(f.fileData.data)-int(f.at) >= len(b) { 199 n = len(b) 200 } else { 201 n = len(f.fileData.data) - int(f.at) 202 } 203 copy(b, f.fileData.data[f.at:f.at+int64(n)]) 204 atomic.AddInt64(&f.at, int64(n)) 205 return 206 } 207 208 func (f *File) ReadAt(b []byte, off int64) (n int, err error) { 209 prev := atomic.LoadInt64(&f.at) 210 atomic.StoreInt64(&f.at, off) 211 n, err = f.Read(b) 212 atomic.StoreInt64(&f.at, prev) 213 return 214 } 215 216 func (f *File) Truncate(size int64) error { 217 if f.closed == true { 218 return ErrFileClosed 219 } 220 if f.readOnly { 221 return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")} 222 } 223 if size < 0 { 224 return ErrOutOfRange 225 } 226 f.fileData.Lock() 227 defer f.fileData.Unlock() 228 if size > int64(len(f.fileData.data)) { 229 diff := size - int64(len(f.fileData.data)) 230 f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...) 231 } else { 232 f.fileData.data = f.fileData.data[0:size] 233 } 234 setModTime(f.fileData, time.Now()) 235 return nil 236 } 237 238 func (f *File) Seek(offset int64, whence int) (int64, error) { 239 if f.closed == true { 240 return 0, ErrFileClosed 241 } 242 switch whence { 243 case io.SeekStart: 244 atomic.StoreInt64(&f.at, offset) 245 case io.SeekCurrent: 246 atomic.AddInt64(&f.at, offset) 247 case io.SeekEnd: 248 atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset) 249 } 250 return f.at, nil 251 } 252 253 func (f *File) Write(b []byte) (n int, err error) { 254 if f.closed == true { 255 return 0, ErrFileClosed 256 } 257 if f.readOnly { 258 return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")} 259 } 260 n = len(b) 261 cur := atomic.LoadInt64(&f.at) 262 f.fileData.Lock() 263 defer f.fileData.Unlock() 264 diff := cur - int64(len(f.fileData.data)) 265 var tail []byte 266 if n+int(cur) < len(f.fileData.data) { 267 tail = f.fileData.data[n+int(cur):] 268 } 269 if diff > 0 { 270 f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{00}, int(diff)), b...)...) 271 f.fileData.data = append(f.fileData.data, tail...) 272 } else { 273 f.fileData.data = append(f.fileData.data[:cur], b...) 274 f.fileData.data = append(f.fileData.data, tail...) 275 } 276 setModTime(f.fileData, time.Now()) 277 278 atomic.AddInt64(&f.at, int64(n)) 279 return 280 } 281 282 func (f *File) WriteAt(b []byte, off int64) (n int, err error) { 283 atomic.StoreInt64(&f.at, off) 284 return f.Write(b) 285 } 286 287 func (f *File) WriteString(s string) (ret int, err error) { 288 return f.Write([]byte(s)) 289 } 290 291 func (f *File) Info() *FileInfo { 292 return &FileInfo{f.fileData} 293 } 294 295 type FileInfo struct { 296 *FileData 297 } 298 299 // Implements os.FileInfo 300 func (s *FileInfo) Name() string { 301 s.Lock() 302 _, name := filepath.Split(s.name) 303 s.Unlock() 304 return name 305 } 306 func (s *FileInfo) Mode() os.FileMode { 307 s.Lock() 308 defer s.Unlock() 309 return s.mode 310 } 311 func (s *FileInfo) ModTime() time.Time { 312 s.Lock() 313 defer s.Unlock() 314 return s.modtime 315 } 316 func (s *FileInfo) IsDir() bool { 317 s.Lock() 318 defer s.Unlock() 319 return s.dir 320 } 321 func (s *FileInfo) Sys() interface{} { return nil } 322 func (s *FileInfo) Size() int64 { 323 if s.IsDir() { 324 return int64(42) 325 } 326 s.Lock() 327 defer s.Unlock() 328 return int64(len(s.data)) 329 } 330 331 var ( 332 ErrFileClosed = errors.New("File is closed") 333 ErrOutOfRange = errors.New("Out of range") 334 ErrTooLarge = errors.New("Too large") 335 ErrFileNotFound = os.ErrNotExist 336 ErrFileExists = os.ErrExist 337 ErrDestinationExists = os.ErrExist 338 )