tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/memfs/file.go (about)

     1  // Copyright © 2014 Steve Francia <spf@spf13.com>.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package memfs
    15  
    16  import (
    17  	"bytes"
    18  	"errors"
    19  	"io"
    20  	"io/fs"
    21  	"os"
    22  	"path/filepath"
    23  	"sync"
    24  	"sync/atomic"
    25  	"time"
    26  )
    27  
    28  var ErrOutOfRange = errors.New("Out of range")
    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 NewROFileHandle(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    fs.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}
    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 fs.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() (fs.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(n int) ([]fs.DirEntry, error) {
   147  	var entries []fs.DirEntry
   148  	dirs, err := f.Readdir(n)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	for _, dir := range dirs {
   153  		entries = append(entries, dirEntry{
   154  			name:  dir.Name(),
   155  			isDir: dir.IsDir(),
   156  			typ:   dir.Mode().Type(),
   157  			info:  dir,
   158  		})
   159  	}
   160  	return entries, nil
   161  }
   162  
   163  func (f *File) Readdir(count int) (res []fs.FileInfo, err error) {
   164  	if !f.fileData.dir {
   165  		return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")}
   166  	}
   167  	var outLength int64
   168  
   169  	f.fileData.Lock()
   170  	files := f.fileData.memDir.Files()[f.readDirCount:]
   171  	if count > 0 {
   172  		if len(files) < count {
   173  			outLength = int64(len(files))
   174  		} else {
   175  			outLength = int64(count)
   176  		}
   177  		if len(files) == 0 {
   178  			err = io.EOF
   179  		}
   180  	} else {
   181  		outLength = int64(len(files))
   182  	}
   183  	f.readDirCount += outLength
   184  	f.fileData.Unlock()
   185  
   186  	res = make([]fs.FileInfo, outLength)
   187  	for i := range res {
   188  		res[i] = &FileInfo{files[i]}
   189  	}
   190  
   191  	return res, err
   192  }
   193  
   194  func (f *File) Readdirnames(n int) (names []string, err error) {
   195  	fi, err := f.Readdir(n)
   196  	names = make([]string, len(fi))
   197  	for i, f := range fi {
   198  		_, names[i] = filepath.Split(f.Name())
   199  	}
   200  	return names, err
   201  }
   202  
   203  func (f *File) Read(b []byte) (n int, err error) {
   204  	f.fileData.Lock()
   205  	defer f.fileData.Unlock()
   206  	if f.closed == true {
   207  		return 0, fs.ErrClosed
   208  	}
   209  	if len(b) > 0 && int(f.at) == len(f.fileData.data) {
   210  		return 0, io.EOF
   211  	}
   212  	if int(f.at) > len(f.fileData.data) {
   213  		return 0, io.ErrUnexpectedEOF
   214  	}
   215  	if len(f.fileData.data)-int(f.at) >= len(b) {
   216  		n = len(b)
   217  	} else {
   218  		n = len(f.fileData.data) - int(f.at)
   219  	}
   220  	copy(b, f.fileData.data[f.at:f.at+int64(n)])
   221  	atomic.AddInt64(&f.at, int64(n))
   222  	return
   223  }
   224  
   225  func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
   226  	prev := atomic.LoadInt64(&f.at)
   227  	atomic.StoreInt64(&f.at, off)
   228  	n, err = f.Read(b)
   229  	atomic.StoreInt64(&f.at, prev)
   230  	return
   231  }
   232  
   233  func (f *File) Truncate(size int64) error {
   234  	if f.closed == true {
   235  		return fs.ErrClosed
   236  	}
   237  	if f.readOnly {
   238  		return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
   239  	}
   240  	if size < 0 {
   241  		return ErrOutOfRange
   242  	}
   243  	f.fileData.Lock()
   244  	defer f.fileData.Unlock()
   245  	if size > int64(len(f.fileData.data)) {
   246  		diff := size - int64(len(f.fileData.data))
   247  		f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...)
   248  	} else {
   249  		f.fileData.data = f.fileData.data[0:size]
   250  	}
   251  	setModTime(f.fileData, time.Now())
   252  	return nil
   253  }
   254  
   255  func (f *File) Seek(offset int64, whence int) (int64, error) {
   256  	if f.closed == true {
   257  		return 0, fs.ErrClosed
   258  	}
   259  	switch whence {
   260  	case io.SeekStart:
   261  		atomic.StoreInt64(&f.at, offset)
   262  	case io.SeekCurrent:
   263  		atomic.AddInt64(&f.at, offset)
   264  	case io.SeekEnd:
   265  		atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset)
   266  	}
   267  	return f.at, nil
   268  }
   269  
   270  func (f *File) Write(b []byte) (n int, err error) {
   271  	if f.closed == true {
   272  		return 0, fs.ErrClosed
   273  	}
   274  	if f.readOnly {
   275  		return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")}
   276  	}
   277  	n = len(b)
   278  	cur := atomic.LoadInt64(&f.at)
   279  	f.fileData.Lock()
   280  	defer f.fileData.Unlock()
   281  	diff := cur - int64(len(f.fileData.data))
   282  	var tail []byte
   283  	if n+int(cur) < len(f.fileData.data) {
   284  		tail = f.fileData.data[n+int(cur):]
   285  	}
   286  	if diff > 0 {
   287  		f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{00}, int(diff)), b...)...)
   288  		f.fileData.data = append(f.fileData.data, tail...)
   289  	} else {
   290  		f.fileData.data = append(f.fileData.data[:cur], b...)
   291  		f.fileData.data = append(f.fileData.data, tail...)
   292  	}
   293  	setModTime(f.fileData, time.Now())
   294  
   295  	atomic.AddInt64(&f.at, int64(n))
   296  	return
   297  }
   298  
   299  func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
   300  	atomic.StoreInt64(&f.at, off)
   301  	return f.Write(b)
   302  }
   303  
   304  func (f *File) WriteString(s string) (ret int, err error) {
   305  	return f.Write([]byte(s))
   306  }
   307  
   308  func (f *File) Info() *FileInfo {
   309  	return &FileInfo{f.fileData}
   310  }
   311  
   312  type FileInfo struct {
   313  	*FileData
   314  }
   315  
   316  // Implements fs.FileInfo
   317  func (s *FileInfo) Name() string {
   318  	s.Lock()
   319  	_, name := filepath.Split(s.name)
   320  	s.Unlock()
   321  	return name
   322  }
   323  func (s *FileInfo) Mode() fs.FileMode {
   324  	s.Lock()
   325  	defer s.Unlock()
   326  	return s.mode
   327  }
   328  func (s *FileInfo) ModTime() time.Time {
   329  	s.Lock()
   330  	defer s.Unlock()
   331  	return s.modtime
   332  }
   333  func (s *FileInfo) IsDir() bool {
   334  	s.Lock()
   335  	defer s.Unlock()
   336  	return s.dir
   337  }
   338  func (s *FileInfo) Sys() interface{} { return nil }
   339  func (s *FileInfo) Size() int64 {
   340  	if s.IsDir() {
   341  		return int64(42)
   342  	}
   343  	s.Lock()
   344  	defer s.Unlock()
   345  	return int64(len(s.data))
   346  }