tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/memfs/fs.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  	"io"
    18  	"io/fs"
    19  	"log"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  const filePathSeparator = string(filepath.Separator)
    28  const chmodBits = fs.ModePerm | fs.ModeSetuid | fs.ModeSetgid | fs.ModeSticky // Only a subset of bits are allowed to be changed. Documented under os.Chmod()
    29  
    30  type FS struct {
    31  	mu   sync.RWMutex
    32  	data map[string]*FileData
    33  	init sync.Once
    34  }
    35  
    36  func New() *FS {
    37  	return &FS{}
    38  }
    39  
    40  func (m *FS) getData() map[string]*FileData {
    41  	m.init.Do(func() {
    42  		m.data = make(map[string]*FileData)
    43  		// Root should always exist, right?
    44  		// TODO: what about windows?
    45  		root := CreateDir(filePathSeparator)
    46  		SetMode(root, fs.ModeDir|0755)
    47  		m.data[filePathSeparator] = root
    48  	})
    49  	return m.data
    50  }
    51  
    52  func (m *FS) Create(name string) (fs.File, error) {
    53  	name = normalizePath(name)
    54  	m.mu.Lock()
    55  	file := CreateFile(name)
    56  	m.getData()[name] = file
    57  	m.registerWithParent(file, 0)
    58  	m.mu.Unlock()
    59  	return NewFileHandle(file), nil
    60  }
    61  
    62  func (m *FS) unregisterWithParent(fileName string) error {
    63  	f, err := m.lockfreeOpen(fileName)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	parent := m.findParent(f)
    68  	if parent == nil {
    69  		log.Panic("parent of ", f.Name(), " is nil")
    70  	}
    71  
    72  	parent.Lock()
    73  	RemoveFromMemDir(parent, f)
    74  	parent.Unlock()
    75  	return nil
    76  }
    77  
    78  func (m *FS) findParent(f *FileData) *FileData {
    79  	pdir, _ := filepath.Split(f.Name())
    80  	pdir = filepath.Clean(pdir)
    81  	pfile, err := m.lockfreeOpen(pdir)
    82  	if err != nil {
    83  		return nil
    84  	}
    85  	return pfile
    86  }
    87  
    88  func (m *FS) registerWithParent(f *FileData, perm fs.FileMode) {
    89  	if f == nil {
    90  		return
    91  	}
    92  	parent := m.findParent(f)
    93  	if parent == nil {
    94  		pdir := filepath.Dir(filepath.Clean(f.Name()))
    95  		err := m.lockfreeMkdir(pdir, perm)
    96  		if err != nil {
    97  			//log.Println("Mkdir error:", err)
    98  			return
    99  		}
   100  		parent, err = m.lockfreeOpen(pdir)
   101  		if err != nil {
   102  			//log.Println("Open after Mkdir error:", err)
   103  			return
   104  		}
   105  	}
   106  
   107  	parent.Lock()
   108  	InitializeDir(parent)
   109  	AddToMemDir(parent, f)
   110  	parent.Unlock()
   111  }
   112  
   113  func (m *FS) lockfreeMkdir(name string, perm fs.FileMode) error {
   114  	name = normalizePath(name)
   115  	x, ok := m.getData()[name]
   116  	if ok {
   117  		// Only return fs.ErrExist if it's a file, not a directory.
   118  		i := FileInfo{FileData: x}
   119  		if !i.IsDir() {
   120  			return fs.ErrExist
   121  		}
   122  	} else {
   123  		item := CreateDir(name)
   124  		SetMode(item, fs.ModeDir|perm)
   125  		m.getData()[name] = item
   126  		m.registerWithParent(item, perm)
   127  	}
   128  	return nil
   129  }
   130  
   131  func (m *FS) Mkdir(name string, perm fs.FileMode) error {
   132  	perm &= chmodBits
   133  	name = normalizePath(name)
   134  
   135  	m.mu.RLock()
   136  	_, ok := m.getData()[name]
   137  	m.mu.RUnlock()
   138  	if ok {
   139  		return &os.PathError{Op: "mkdir", Path: name, Err: fs.ErrExist}
   140  	}
   141  
   142  	m.mu.Lock()
   143  	item := CreateDir(name)
   144  	SetMode(item, fs.ModeDir|perm)
   145  	m.getData()[name] = item
   146  	m.registerWithParent(item, perm)
   147  	m.mu.Unlock()
   148  
   149  	return m.setFileMode(name, perm|fs.ModeDir)
   150  }
   151  
   152  func (m *FS) MkdirAll(path string, perm fs.FileMode) error {
   153  	err := m.Mkdir(path, perm)
   154  	if err != nil {
   155  		if err.(*os.PathError).Err == fs.ErrExist {
   156  			return nil
   157  		}
   158  		return err
   159  	}
   160  	return nil
   161  }
   162  
   163  // Handle some relative paths
   164  func normalizePath(path string) string {
   165  	path = filepath.Clean(path)
   166  
   167  	switch path {
   168  	case ".":
   169  		return filePathSeparator
   170  	case "..":
   171  		return filePathSeparator
   172  	default:
   173  		return path
   174  	}
   175  }
   176  
   177  func (m *FS) Open(name string) (fs.File, error) {
   178  	f, err := m.open(name)
   179  	if f != nil {
   180  		return NewROFileHandle(f), err
   181  	}
   182  	return nil, err
   183  }
   184  
   185  func (m *FS) openWrite(name string) (fs.File, error) {
   186  	f, err := m.open(name)
   187  	if f != nil {
   188  		return NewFileHandle(f), err
   189  	}
   190  	return nil, err
   191  }
   192  
   193  func (m *FS) open(name string) (*FileData, error) {
   194  	name = normalizePath(name)
   195  
   196  	m.mu.RLock()
   197  	f, ok := m.getData()[name]
   198  	m.mu.RUnlock()
   199  	if !ok {
   200  		return nil, &os.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
   201  	}
   202  	return f, nil
   203  }
   204  
   205  func (m *FS) lockfreeOpen(name string) (*FileData, error) {
   206  	name = normalizePath(name)
   207  	f, ok := m.getData()[name]
   208  	if ok {
   209  		return f, nil
   210  	} else {
   211  		return nil, fs.ErrNotExist
   212  	}
   213  }
   214  
   215  func (m *FS) OpenFile(name string, flag int, perm fs.FileMode) (fs.File, error) {
   216  	perm &= chmodBits
   217  	chmod := false
   218  	file, err := m.openWrite(name)
   219  	if err == nil && (flag&os.O_EXCL > 0) {
   220  		return nil, &os.PathError{Op: "open", Path: name, Err: fs.ErrExist}
   221  	}
   222  	if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {
   223  		file, err = m.Create(name)
   224  		chmod = true
   225  	}
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	if flag == os.O_RDONLY {
   230  		file = NewROFileHandle(file.(*File).Data())
   231  	}
   232  	if flag&os.O_APPEND > 0 {
   233  		fseek, ok := file.(io.Seeker)
   234  		if !ok {
   235  			return nil, fs.ErrPermission
   236  		}
   237  		_, err = fseek.Seek(0, os.SEEK_END)
   238  		if err != nil {
   239  			file.Close()
   240  			return nil, err
   241  		}
   242  	}
   243  	if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 {
   244  		ftrunc, ok := file.(interface {
   245  			Truncate(size int64) error
   246  		})
   247  		if !ok {
   248  			return nil, fs.ErrPermission
   249  		}
   250  		err = ftrunc.Truncate(0)
   251  		if err != nil {
   252  			file.Close()
   253  			return nil, err
   254  		}
   255  	}
   256  	if chmod {
   257  		return file, m.setFileMode(name, perm)
   258  	}
   259  	return file, nil
   260  }
   261  
   262  func (m *FS) Remove(name string) error {
   263  	name = normalizePath(name)
   264  
   265  	m.mu.Lock()
   266  	defer m.mu.Unlock()
   267  
   268  	if _, ok := m.getData()[name]; ok {
   269  		err := m.unregisterWithParent(name)
   270  		if err != nil {
   271  			return &os.PathError{Op: "remove", Path: name, Err: err}
   272  		}
   273  		delete(m.getData(), name)
   274  	} else {
   275  		return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
   276  	}
   277  	return nil
   278  }
   279  
   280  func (m *FS) RemoveAll(path string) error {
   281  	path = normalizePath(path)
   282  	m.mu.Lock()
   283  	m.unregisterWithParent(path)
   284  	m.mu.Unlock()
   285  
   286  	m.mu.RLock()
   287  	defer m.mu.RUnlock()
   288  
   289  	for p := range m.getData() {
   290  		if strings.HasPrefix(p, path) {
   291  			m.mu.RUnlock()
   292  			m.mu.Lock()
   293  			delete(m.getData(), p)
   294  			m.mu.Unlock()
   295  			m.mu.RLock()
   296  		}
   297  	}
   298  	return nil
   299  }
   300  
   301  func (m *FS) Rename(oldname, newname string) error {
   302  	oldname = normalizePath(oldname)
   303  	newname = normalizePath(newname)
   304  
   305  	if oldname == newname {
   306  		return nil
   307  	}
   308  
   309  	m.mu.RLock()
   310  	defer m.mu.RUnlock()
   311  	if _, ok := m.getData()[oldname]; ok {
   312  		m.mu.RUnlock()
   313  		m.mu.Lock()
   314  		m.unregisterWithParent(oldname)
   315  		fileData := m.getData()[oldname]
   316  		delete(m.getData(), oldname)
   317  		ChangeFileName(fileData, newname)
   318  		m.getData()[newname] = fileData
   319  		m.registerWithParent(fileData, 0)
   320  		m.mu.Unlock()
   321  		m.mu.RLock()
   322  	} else {
   323  		return &os.PathError{Op: "rename", Path: oldname, Err: fs.ErrNotExist}
   324  	}
   325  	return nil
   326  }
   327  
   328  func (m *FS) Stat(name string) (fs.FileInfo, error) {
   329  	f, err := m.Open(name)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	fi := GetFileInfo(f.(*File).Data())
   334  	return fi, nil
   335  }
   336  
   337  func (m *FS) Chmod(name string, mode fs.FileMode) error {
   338  	mode &= chmodBits
   339  
   340  	m.mu.RLock()
   341  	f, ok := m.getData()[name]
   342  	m.mu.RUnlock()
   343  	if !ok {
   344  		return &os.PathError{Op: "chmod", Path: name, Err: fs.ErrNotExist}
   345  	}
   346  	prevOtherBits := GetFileInfo(f).Mode() & ^chmodBits
   347  
   348  	mode = prevOtherBits | mode
   349  	return m.setFileMode(name, mode)
   350  }
   351  
   352  func (m *FS) setFileMode(name string, mode fs.FileMode) error {
   353  	name = normalizePath(name)
   354  
   355  	m.mu.RLock()
   356  	f, ok := m.getData()[name]
   357  	m.mu.RUnlock()
   358  	if !ok {
   359  		return &os.PathError{Op: "chmod", Path: name, Err: fs.ErrNotExist}
   360  	}
   361  
   362  	m.mu.Lock()
   363  	SetMode(f, mode)
   364  	m.mu.Unlock()
   365  
   366  	return nil
   367  }
   368  
   369  func (m *FS) Chown(name string, uid, gid int) error {
   370  	name = normalizePath(name)
   371  
   372  	m.mu.RLock()
   373  	f, ok := m.getData()[name]
   374  	m.mu.RUnlock()
   375  	if !ok {
   376  		return &os.PathError{Op: "chown", Path: name, Err: fs.ErrNotExist}
   377  	}
   378  
   379  	SetUID(f, uid)
   380  	SetGID(f, gid)
   381  
   382  	return nil
   383  }
   384  
   385  func (m *FS) Chtimes(name string, atime time.Time, mtime time.Time) error {
   386  	name = normalizePath(name)
   387  
   388  	m.mu.RLock()
   389  	f, ok := m.getData()[name]
   390  	m.mu.RUnlock()
   391  	if !ok {
   392  		return &os.PathError{Op: "chtimes", Path: name, Err: fs.ErrNotExist}
   393  	}
   394  
   395  	m.mu.Lock()
   396  	SetModTime(f, mtime)
   397  	m.mu.Unlock()
   398  
   399  	return nil
   400  }
   401  
   402  // func (m *FS) List() {
   403  // 	for _, x := range m.data {
   404  // 		y := mem.FileInfo{FileData: x}
   405  // 		fmt.Println(x.Name(), y.Size())
   406  // 	}
   407  // }