github.com/landontclipp/afero@v1.1.1/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 ) 26 27 import "time" 28 29 const FilePathSeparator = string(filepath.Separator) 30 31 type File struct { 32 // atomic requires 64-bit alignment for struct field access 33 at int64 34 readDirCount int64 35 closed bool 36 readOnly bool 37 fileData *FileData 38 } 39 40 func NewFileHandle(data *FileData) *File { 41 return &File{fileData: data} 42 } 43 44 func NewReadOnlyFileHandle(data *FileData) *File { 45 return &File{fileData: data, readOnly: true} 46 } 47 48 func (f File) Data() *FileData { 49 return f.fileData 50 } 51 52 type FileData struct { 53 sync.Mutex 54 name string 55 data []byte 56 memDir Dir 57 dir bool 58 mode os.FileMode 59 modtime time.Time 60 } 61 62 func (d *FileData) Name() string { 63 d.Lock() 64 defer d.Unlock() 65 return d.name 66 } 67 68 func CreateFile(name string) *FileData { 69 return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()} 70 } 71 72 func CreateDir(name string) *FileData { 73 return &FileData{name: name, memDir: &DirMap{}, dir: true} 74 } 75 76 func ChangeFileName(f *FileData, newname string) { 77 f.Lock() 78 f.name = newname 79 f.Unlock() 80 } 81 82 func SetMode(f *FileData, mode os.FileMode) { 83 f.Lock() 84 f.mode = mode 85 f.Unlock() 86 } 87 88 func SetModTime(f *FileData, mtime time.Time) { 89 f.Lock() 90 setModTime(f, mtime) 91 f.Unlock() 92 } 93 94 func setModTime(f *FileData, mtime time.Time) { 95 f.modtime = mtime 96 } 97 98 func GetFileInfo(f *FileData) *FileInfo { 99 return &FileInfo{f} 100 } 101 102 func (f *File) Open() error { 103 atomic.StoreInt64(&f.at, 0) 104 atomic.StoreInt64(&f.readDirCount, 0) 105 f.fileData.Lock() 106 f.closed = false 107 f.fileData.Unlock() 108 return nil 109 } 110 111 func (f *File) Close() error { 112 f.fileData.Lock() 113 f.closed = true 114 if !f.readOnly { 115 setModTime(f.fileData, time.Now()) 116 } 117 f.fileData.Unlock() 118 return nil 119 } 120 121 func (f *File) Name() string { 122 return f.fileData.Name() 123 } 124 125 func (f *File) Stat() (os.FileInfo, error) { 126 return &FileInfo{f.fileData}, nil 127 } 128 129 func (f *File) Sync() error { 130 return nil 131 } 132 133 func (f *File) Readdir(count int) (res []os.FileInfo, err error) { 134 if !f.fileData.dir { 135 return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")} 136 } 137 var outLength int64 138 139 f.fileData.Lock() 140 files := f.fileData.memDir.Files()[f.readDirCount:] 141 if count > 0 { 142 if len(files) < count { 143 outLength = int64(len(files)) 144 } else { 145 outLength = int64(count) 146 } 147 if len(files) == 0 { 148 err = io.EOF 149 } 150 } else { 151 outLength = int64(len(files)) 152 } 153 f.readDirCount += outLength 154 f.fileData.Unlock() 155 156 res = make([]os.FileInfo, outLength) 157 for i := range res { 158 res[i] = &FileInfo{files[i]} 159 } 160 161 return res, err 162 } 163 164 func (f *File) Readdirnames(n int) (names []string, err error) { 165 fi, err := f.Readdir(n) 166 names = make([]string, len(fi)) 167 for i, f := range fi { 168 _, names[i] = filepath.Split(f.Name()) 169 } 170 return names, err 171 } 172 173 func (f *File) Read(b []byte) (n int, err error) { 174 f.fileData.Lock() 175 defer f.fileData.Unlock() 176 if f.closed == true { 177 return 0, ErrFileClosed 178 } 179 if len(b) > 0 && int(f.at) == len(f.fileData.data) { 180 return 0, io.EOF 181 } 182 if int(f.at) > len(f.fileData.data) { 183 return 0, io.ErrUnexpectedEOF 184 } 185 if len(f.fileData.data)-int(f.at) >= len(b) { 186 n = len(b) 187 } else { 188 n = len(f.fileData.data) - int(f.at) 189 } 190 copy(b, f.fileData.data[f.at:f.at+int64(n)]) 191 atomic.AddInt64(&f.at, int64(n)) 192 return 193 } 194 195 func (f *File) ReadAt(b []byte, off int64) (n int, err error) { 196 atomic.StoreInt64(&f.at, off) 197 return f.Read(b) 198 } 199 200 func (f *File) Truncate(size int64) error { 201 if f.closed == true { 202 return ErrFileClosed 203 } 204 if f.readOnly { 205 return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")} 206 } 207 if size < 0 { 208 return ErrOutOfRange 209 } 210 if size > int64(len(f.fileData.data)) { 211 diff := size - int64(len(f.fileData.data)) 212 f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...) 213 } else { 214 f.fileData.data = f.fileData.data[0:size] 215 } 216 setModTime(f.fileData, time.Now()) 217 return nil 218 } 219 220 func (f *File) Seek(offset int64, whence int) (int64, error) { 221 if f.closed == true { 222 return 0, ErrFileClosed 223 } 224 switch whence { 225 case 0: 226 atomic.StoreInt64(&f.at, offset) 227 case 1: 228 atomic.AddInt64(&f.at, int64(offset)) 229 case 2: 230 atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset) 231 } 232 return f.at, nil 233 } 234 235 func (f *File) Write(b []byte) (n int, err error) { 236 if f.readOnly { 237 return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")} 238 } 239 n = len(b) 240 cur := atomic.LoadInt64(&f.at) 241 f.fileData.Lock() 242 defer f.fileData.Unlock() 243 diff := cur - int64(len(f.fileData.data)) 244 var tail []byte 245 if n+int(cur) < len(f.fileData.data) { 246 tail = f.fileData.data[n+int(cur):] 247 } 248 if diff > 0 { 249 f.fileData.data = append(bytes.Repeat([]byte{00}, int(diff)), b...) 250 f.fileData.data = append(f.fileData.data, tail...) 251 } else { 252 f.fileData.data = append(f.fileData.data[:cur], b...) 253 f.fileData.data = append(f.fileData.data, tail...) 254 } 255 setModTime(f.fileData, time.Now()) 256 257 atomic.StoreInt64(&f.at, int64(len(f.fileData.data))) 258 return 259 } 260 261 func (f *File) WriteAt(b []byte, off int64) (n int, err error) { 262 atomic.StoreInt64(&f.at, off) 263 return f.Write(b) 264 } 265 266 func (f *File) WriteString(s string) (ret int, err error) { 267 return f.Write([]byte(s)) 268 } 269 270 func (f *File) Info() *FileInfo { 271 return &FileInfo{f.fileData} 272 } 273 274 type FileInfo struct { 275 *FileData 276 } 277 278 // Implements os.FileInfo 279 func (s *FileInfo) Name() string { 280 s.Lock() 281 _, name := filepath.Split(s.name) 282 s.Unlock() 283 return name 284 } 285 func (s *FileInfo) Mode() os.FileMode { 286 s.Lock() 287 defer s.Unlock() 288 return s.mode 289 } 290 func (s *FileInfo) ModTime() time.Time { 291 s.Lock() 292 defer s.Unlock() 293 return s.modtime 294 } 295 func (s *FileInfo) IsDir() bool { 296 s.Lock() 297 defer s.Unlock() 298 return s.dir 299 } 300 func (s *FileInfo) Sys() interface{} { return nil } 301 func (s *FileInfo) Size() int64 { 302 if s.IsDir() { 303 return int64(42) 304 } 305 s.Lock() 306 defer s.Unlock() 307 return int64(len(s.data)) 308 } 309 310 var ( 311 ErrFileClosed = errors.New("File is closed") 312 ErrOutOfRange = errors.New("Out of range") 313 ErrTooLarge = errors.New("Too large") 314 ErrFileNotFound = os.ErrNotExist 315 ErrFileExists = os.ErrExist 316 ErrDestinationExists = os.ErrExist 317 )