file.go raw

   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  )
 360