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