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 15package mem 16 17import ( 18 "bytes" 19 "errors" 20 "io" 21 "os" 22 "path/filepath" 23 "sync" 24 "sync/atomic" 25 "time" 26) 27 28const FilePathSeparator = string(filepath.Separator) 29 30type 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 39func NewFileHandle(data *FileData) *File { 40 return &File{fileData: data} 41} 42 43func NewReadOnlyFileHandle(data *FileData) *File { 44 return &File{fileData: data, readOnly: true} 45} 46 47func (f File) Data() *FileData { 48 return f.fileData 49} 50 51type 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 63func (d *FileData) Name() string { 64 d.Lock() 65 defer d.Unlock() 66 return d.name 67} 68 69func CreateFile(name string) *FileData { 70 return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()} 71} 72 73func CreateDir(name string) *FileData { 74 return &FileData{name: name, memDir: &DirMap{}, dir: true} 75} 76 77func ChangeFileName(f *FileData, newname string) { 78 f.Lock() 79 f.name = newname 80 f.Unlock() 81} 82 83func SetMode(f *FileData, mode os.FileMode) { 84 f.Lock() 85 f.mode = mode 86 f.Unlock() 87} 88 89func SetModTime(f *FileData, mtime time.Time) { 90 f.Lock() 91 setModTime(f, mtime) 92 f.Unlock() 93} 94 95func setModTime(f *FileData, mtime time.Time) { 96 f.modtime = mtime 97} 98 99func SetUID(f *FileData, uid int) { 100 f.Lock() 101 f.uid = uid 102 f.Unlock() 103} 104 105func SetGID(f *FileData, gid int) { 106 f.Lock() 107 f.gid = gid 108 f.Unlock() 109} 110 111func GetFileInfo(f *FileData) *FileInfo { 112 return &FileInfo{f} 113} 114 115func (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 124func (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 134func (f *File) Name() string { 135 return f.fileData.Name() 136} 137 138func (f *File) Stat() (os.FileInfo, error) { 139 return &FileInfo{f.fileData}, nil 140} 141 142func (f *File) Sync() error { 143 return nil 144} 145 146func (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 177func (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 186func (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 208func (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 216func (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 238func (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 253func (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(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 282func (f *File) WriteAt(b []byte, off int64) (n int, err error) { 283 atomic.StoreInt64(&f.at, off) 284 return f.Write(b) 285} 286 287func (f *File) WriteString(s string) (ret int, err error) { 288 return f.Write([]byte(s)) 289} 290 291func (f *File) Info() *FileInfo { 292 return &FileInfo{f.fileData} 293} 294 295type FileInfo struct { 296 *FileData 297} 298 299// Implements os.FileInfo 300func (s *FileInfo) Name() string { 301 s.Lock() 302 _, name := filepath.Split(s.name) 303 s.Unlock() 304 return name 305} 306func (s *FileInfo) Mode() os.FileMode { 307 s.Lock() 308 defer s.Unlock() 309 return s.mode 310} 311func (s *FileInfo) ModTime() time.Time { 312 s.Lock() 313 defer s.Unlock() 314 return s.modtime 315} 316func (s *FileInfo) IsDir() bool { 317 s.Lock() 318 defer s.Unlock() 319 return s.dir 320} 321func (s *FileInfo) Sys() interface{} { return nil } 322func (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 331var ( 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) 339