vendor/github.com/spf13/afero/memmap.go
changeset 242 2a9ec03fe5a1
child 256 6d9efbef00a9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/github.com/spf13/afero/memmap.go	Sat Sep 29 18:09:54 2018 +0200
@@ -0,0 +1,365 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/spf13/afero/mem"
+)
+
+type MemMapFs struct {
+	mu   sync.RWMutex
+	data map[string]*mem.FileData
+	init sync.Once
+}
+
+func NewMemMapFs() Fs {
+	return &MemMapFs{}
+}
+
+func (m *MemMapFs) getData() map[string]*mem.FileData {
+	m.init.Do(func() {
+		m.data = make(map[string]*mem.FileData)
+		// Root should always exist, right?
+		// TODO: what about windows?
+		m.data[FilePathSeparator] = mem.CreateDir(FilePathSeparator)
+	})
+	return m.data
+}
+
+func (*MemMapFs) Name() string { return "MemMapFS" }
+
+func (m *MemMapFs) Create(name string) (File, error) {
+	name = normalizePath(name)
+	m.mu.Lock()
+	file := mem.CreateFile(name)
+	m.getData()[name] = file
+	m.registerWithParent(file)
+	m.mu.Unlock()
+	return mem.NewFileHandle(file), nil
+}
+
+func (m *MemMapFs) unRegisterWithParent(fileName string) error {
+	f, err := m.lockfreeOpen(fileName)
+	if err != nil {
+		return err
+	}
+	parent := m.findParent(f)
+	if parent == nil {
+		log.Panic("parent of ", f.Name(), " is nil")
+	}
+
+	parent.Lock()
+	mem.RemoveFromMemDir(parent, f)
+	parent.Unlock()
+	return nil
+}
+
+func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
+	pdir, _ := filepath.Split(f.Name())
+	pdir = filepath.Clean(pdir)
+	pfile, err := m.lockfreeOpen(pdir)
+	if err != nil {
+		return nil
+	}
+	return pfile
+}
+
+func (m *MemMapFs) registerWithParent(f *mem.FileData) {
+	if f == nil {
+		return
+	}
+	parent := m.findParent(f)
+	if parent == nil {
+		pdir := filepath.Dir(filepath.Clean(f.Name()))
+		err := m.lockfreeMkdir(pdir, 0777)
+		if err != nil {
+			//log.Println("Mkdir error:", err)
+			return
+		}
+		parent, err = m.lockfreeOpen(pdir)
+		if err != nil {
+			//log.Println("Open after Mkdir error:", err)
+			return
+		}
+	}
+
+	parent.Lock()
+	mem.InitializeDir(parent)
+	mem.AddToMemDir(parent, f)
+	parent.Unlock()
+}
+
+func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
+	name = normalizePath(name)
+	x, ok := m.getData()[name]
+	if ok {
+		// Only return ErrFileExists if it's a file, not a directory.
+		i := mem.FileInfo{FileData: x}
+		if !i.IsDir() {
+			return ErrFileExists
+		}
+	} else {
+		item := mem.CreateDir(name)
+		m.getData()[name] = item
+		m.registerWithParent(item)
+	}
+	return nil
+}
+
+func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	_, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if ok {
+		return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}
+	}
+
+	m.mu.Lock()
+	item := mem.CreateDir(name)
+	m.getData()[name] = item
+	m.registerWithParent(item)
+	m.mu.Unlock()
+
+	m.Chmod(name, perm|os.ModeDir)
+
+	return nil
+}
+
+func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error {
+	err := m.Mkdir(path, perm)
+	if err != nil {
+		if err.(*os.PathError).Err == ErrFileExists {
+			return nil
+		}
+		return err
+	}
+	return nil
+}
+
+// Handle some relative paths
+func normalizePath(path string) string {
+	path = filepath.Clean(path)
+
+	switch path {
+	case ".":
+		return FilePathSeparator
+	case "..":
+		return FilePathSeparator
+	default:
+		return path
+	}
+}
+
+func (m *MemMapFs) Open(name string) (File, error) {
+	f, err := m.open(name)
+	if f != nil {
+		return mem.NewReadOnlyFileHandle(f), err
+	}
+	return nil, err
+}
+
+func (m *MemMapFs) openWrite(name string) (File, error) {
+	f, err := m.open(name)
+	if f != nil {
+		return mem.NewFileHandle(f), err
+	}
+	return nil, err
+}
+
+func (m *MemMapFs) open(name string) (*mem.FileData, error) {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	f, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if !ok {
+		return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound}
+	}
+	return f, nil
+}
+
+func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {
+	name = normalizePath(name)
+	f, ok := m.getData()[name]
+	if ok {
+		return f, nil
+	} else {
+		return nil, ErrFileNotFound
+	}
+}
+
+func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	chmod := false
+	file, err := m.openWrite(name)
+	if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {
+		file, err = m.Create(name)
+		chmod = true
+	}
+	if err != nil {
+		return nil, err
+	}
+	if flag == os.O_RDONLY {
+		file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data())
+	}
+	if flag&os.O_APPEND > 0 {
+		_, err = file.Seek(0, os.SEEK_END)
+		if err != nil {
+			file.Close()
+			return nil, err
+		}
+	}
+	if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 {
+		err = file.Truncate(0)
+		if err != nil {
+			file.Close()
+			return nil, err
+		}
+	}
+	if chmod {
+		m.Chmod(name, perm)
+	}
+	return file, nil
+}
+
+func (m *MemMapFs) Remove(name string) error {
+	name = normalizePath(name)
+
+	m.mu.Lock()
+	defer m.mu.Unlock()
+
+	if _, ok := m.getData()[name]; ok {
+		err := m.unRegisterWithParent(name)
+		if err != nil {
+			return &os.PathError{Op: "remove", Path: name, Err: err}
+		}
+		delete(m.getData(), name)
+	} else {
+		return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
+	}
+	return nil
+}
+
+func (m *MemMapFs) RemoveAll(path string) error {
+	path = normalizePath(path)
+	m.mu.Lock()
+	m.unRegisterWithParent(path)
+	m.mu.Unlock()
+
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+
+	for p, _ := range m.getData() {
+		if strings.HasPrefix(p, path) {
+			m.mu.RUnlock()
+			m.mu.Lock()
+			delete(m.getData(), p)
+			m.mu.Unlock()
+			m.mu.RLock()
+		}
+	}
+	return nil
+}
+
+func (m *MemMapFs) Rename(oldname, newname string) error {
+	oldname = normalizePath(oldname)
+	newname = normalizePath(newname)
+
+	if oldname == newname {
+		return nil
+	}
+
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if _, ok := m.getData()[oldname]; ok {
+		m.mu.RUnlock()
+		m.mu.Lock()
+		m.unRegisterWithParent(oldname)
+		fileData := m.getData()[oldname]
+		delete(m.getData(), oldname)
+		mem.ChangeFileName(fileData, newname)
+		m.getData()[newname] = fileData
+		m.registerWithParent(fileData)
+		m.mu.Unlock()
+		m.mu.RLock()
+	} else {
+		return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
+	}
+	return nil
+}
+
+func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {
+	f, err := m.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	fi := mem.GetFileInfo(f.(*mem.File).Data())
+	return fi, nil
+}
+
+func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	f, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if !ok {
+		return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}
+	}
+
+	m.mu.Lock()
+	mem.SetMode(f, mode)
+	m.mu.Unlock()
+
+	return nil
+}
+
+func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	f, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if !ok {
+		return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound}
+	}
+
+	m.mu.Lock()
+	mem.SetModTime(f, mtime)
+	m.mu.Unlock()
+
+	return nil
+}
+
+func (m *MemMapFs) List() {
+	for _, x := range m.data {
+		y := mem.FileInfo{FileData: x}
+		fmt.Println(x.Name(), y.Size())
+	}
+}
+
+// func debugMemMapList(fs Fs) {
+// 	if x, ok := fs.(*MemMapFs); ok {
+// 		x.List()
+// 	}
+// }