Path: blob/main/vendor/github.com/spf13/afero/memmap.go
2875 views
// Copyright © 2014 Steve Francia <[email protected]>.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5// http://www.apache.org/licenses/LICENSE-2.06//7// Unless required by applicable law or agreed to in writing, software8// distributed under the License is distributed on an "AS IS" BASIS,9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.10// See the License for the specific language governing permissions and11// limitations under the License.1213package afero1415import (16"fmt"17"io"18"log"19"os"20"path/filepath"21"sort"22"strings"23"sync"24"time"2526"github.com/spf13/afero/mem"27)2829const chmodBits = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky // Only a subset of bits are allowed to be changed. Documented under os.Chmod()3031type MemMapFs struct {32mu sync.RWMutex33data map[string]*mem.FileData34init sync.Once35}3637func NewMemMapFs() Fs {38return &MemMapFs{}39}4041func (m *MemMapFs) getData() map[string]*mem.FileData {42m.init.Do(func() {43m.data = make(map[string]*mem.FileData)44// Root should always exist, right?45// TODO: what about windows?46root := mem.CreateDir(FilePathSeparator)47mem.SetMode(root, os.ModeDir|0o755)48m.data[FilePathSeparator] = root49})50return m.data51}5253func (*MemMapFs) Name() string { return "MemMapFS" }5455func (m *MemMapFs) Create(name string) (File, error) {56name = normalizePath(name)57m.mu.Lock()58file := mem.CreateFile(name)59m.getData()[name] = file60m.registerWithParent(file, 0)61m.mu.Unlock()62return mem.NewFileHandle(file), nil63}6465func (m *MemMapFs) unRegisterWithParent(fileName string) error {66f, err := m.lockfreeOpen(fileName)67if err != nil {68return err69}70parent := m.findParent(f)71if parent == nil {72log.Panic("parent of ", f.Name(), " is nil")73}7475parent.Lock()76mem.RemoveFromMemDir(parent, f)77parent.Unlock()78return nil79}8081func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {82pdir, _ := filepath.Split(f.Name())83pdir = filepath.Clean(pdir)84pfile, err := m.lockfreeOpen(pdir)85if err != nil {86return nil87}88return pfile89}9091func (m *MemMapFs) findDescendants(name string) []*mem.FileData {92fData := m.getData()93descendants := make([]*mem.FileData, 0, len(fData))94for p, dFile := range fData {95if strings.HasPrefix(p, name+FilePathSeparator) {96descendants = append(descendants, dFile)97}98}99100sort.Slice(descendants, func(i, j int) bool {101cur := len(strings.Split(descendants[i].Name(), FilePathSeparator))102next := len(strings.Split(descendants[j].Name(), FilePathSeparator))103return cur < next104})105106return descendants107}108109func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) {110if f == nil {111return112}113parent := m.findParent(f)114if parent == nil {115pdir := filepath.Dir(filepath.Clean(f.Name()))116err := m.lockfreeMkdir(pdir, perm)117if err != nil {118// log.Println("Mkdir error:", err)119return120}121parent, err = m.lockfreeOpen(pdir)122if err != nil {123// log.Println("Open after Mkdir error:", err)124return125}126}127128parent.Lock()129mem.InitializeDir(parent)130mem.AddToMemDir(parent, f)131parent.Unlock()132}133134func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {135name = normalizePath(name)136x, ok := m.getData()[name]137if ok {138// Only return ErrFileExists if it's a file, not a directory.139i := mem.FileInfo{FileData: x}140if !i.IsDir() {141return ErrFileExists142}143} else {144item := mem.CreateDir(name)145mem.SetMode(item, os.ModeDir|perm)146m.getData()[name] = item147m.registerWithParent(item, perm)148}149return nil150}151152func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {153perm &= chmodBits154name = normalizePath(name)155156m.mu.RLock()157_, ok := m.getData()[name]158m.mu.RUnlock()159if ok {160return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}161}162163m.mu.Lock()164// Dobule check that it doesn't exist.165if _, ok := m.getData()[name]; ok {166m.mu.Unlock()167return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}168}169item := mem.CreateDir(name)170mem.SetMode(item, os.ModeDir|perm)171m.getData()[name] = item172m.registerWithParent(item, perm)173m.mu.Unlock()174175return m.setFileMode(name, perm|os.ModeDir)176}177178func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error {179err := m.Mkdir(path, perm)180if err != nil {181if err.(*os.PathError).Err == ErrFileExists {182return nil183}184return err185}186return nil187}188189// Handle some relative paths190func normalizePath(path string) string {191path = filepath.Clean(path)192193switch path {194case ".":195return FilePathSeparator196case "..":197return FilePathSeparator198default:199return path200}201}202203func (m *MemMapFs) Open(name string) (File, error) {204f, err := m.open(name)205if f != nil {206return mem.NewReadOnlyFileHandle(f), err207}208return nil, err209}210211func (m *MemMapFs) openWrite(name string) (File, error) {212f, err := m.open(name)213if f != nil {214return mem.NewFileHandle(f), err215}216return nil, err217}218219func (m *MemMapFs) open(name string) (*mem.FileData, error) {220name = normalizePath(name)221222m.mu.RLock()223f, ok := m.getData()[name]224m.mu.RUnlock()225if !ok {226return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound}227}228return f, nil229}230231func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {232name = normalizePath(name)233f, ok := m.getData()[name]234if ok {235return f, nil236} else {237return nil, ErrFileNotFound238}239}240241func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {242perm &= chmodBits243chmod := false244file, err := m.openWrite(name)245if err == nil && (flag&os.O_EXCL > 0) {246return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileExists}247}248if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {249file, err = m.Create(name)250chmod = true251}252if err != nil {253return nil, err254}255if flag == os.O_RDONLY {256file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data())257}258if flag&os.O_APPEND > 0 {259_, err = file.Seek(0, io.SeekEnd)260if err != nil {261file.Close()262return nil, err263}264}265if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 {266err = file.Truncate(0)267if err != nil {268file.Close()269return nil, err270}271}272if chmod {273return file, m.setFileMode(name, perm)274}275return file, nil276}277278func (m *MemMapFs) Remove(name string) error {279name = normalizePath(name)280281m.mu.Lock()282defer m.mu.Unlock()283284if _, ok := m.getData()[name]; ok {285err := m.unRegisterWithParent(name)286if err != nil {287return &os.PathError{Op: "remove", Path: name, Err: err}288}289delete(m.getData(), name)290} else {291return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}292}293return nil294}295296func (m *MemMapFs) RemoveAll(path string) error {297path = normalizePath(path)298m.mu.Lock()299m.unRegisterWithParent(path)300m.mu.Unlock()301302m.mu.RLock()303defer m.mu.RUnlock()304305for p := range m.getData() {306if p == path || strings.HasPrefix(p, path+FilePathSeparator) {307m.mu.RUnlock()308m.mu.Lock()309delete(m.getData(), p)310m.mu.Unlock()311m.mu.RLock()312}313}314return nil315}316317func (m *MemMapFs) Rename(oldname, newname string) error {318oldname = normalizePath(oldname)319newname = normalizePath(newname)320321if oldname == newname {322return nil323}324325m.mu.RLock()326defer m.mu.RUnlock()327if _, ok := m.getData()[oldname]; ok {328m.mu.RUnlock()329m.mu.Lock()330err := m.unRegisterWithParent(oldname)331if err != nil {332return err333}334335fileData := m.getData()[oldname]336mem.ChangeFileName(fileData, newname)337m.getData()[newname] = fileData338339err = m.renameDescendants(oldname, newname)340if err != nil {341return err342}343344delete(m.getData(), oldname)345346m.registerWithParent(fileData, 0)347m.mu.Unlock()348m.mu.RLock()349} else {350return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}351}352return nil353}354355func (m *MemMapFs) renameDescendants(oldname, newname string) error {356descendants := m.findDescendants(oldname)357removes := make([]string, 0, len(descendants))358for _, desc := range descendants {359descNewName := strings.Replace(desc.Name(), oldname, newname, 1)360err := m.unRegisterWithParent(desc.Name())361if err != nil {362return err363}364365removes = append(removes, desc.Name())366mem.ChangeFileName(desc, descNewName)367m.getData()[descNewName] = desc368369m.registerWithParent(desc, 0)370}371for _, r := range removes {372delete(m.getData(), r)373}374375return nil376}377378func (m *MemMapFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {379fileInfo, err := m.Stat(name)380return fileInfo, false, err381}382383func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {384f, err := m.Open(name)385if err != nil {386return nil, err387}388fi := mem.GetFileInfo(f.(*mem.File).Data())389return fi, nil390}391392func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {393mode &= chmodBits394395m.mu.RLock()396f, ok := m.getData()[name]397m.mu.RUnlock()398if !ok {399return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}400}401prevOtherBits := mem.GetFileInfo(f).Mode() & ^chmodBits402403mode = prevOtherBits | mode404return m.setFileMode(name, mode)405}406407func (m *MemMapFs) setFileMode(name string, mode os.FileMode) error {408name = normalizePath(name)409410m.mu.RLock()411f, ok := m.getData()[name]412m.mu.RUnlock()413if !ok {414return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}415}416417m.mu.Lock()418mem.SetMode(f, mode)419m.mu.Unlock()420421return nil422}423424func (m *MemMapFs) Chown(name string, uid, gid int) error {425name = normalizePath(name)426427m.mu.RLock()428f, ok := m.getData()[name]429m.mu.RUnlock()430if !ok {431return &os.PathError{Op: "chown", Path: name, Err: ErrFileNotFound}432}433434mem.SetUID(f, uid)435mem.SetGID(f, gid)436437return nil438}439440func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error {441name = normalizePath(name)442443m.mu.RLock()444f, ok := m.getData()[name]445m.mu.RUnlock()446if !ok {447return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound}448}449450m.mu.Lock()451mem.SetModTime(f, mtime)452m.mu.Unlock()453454return nil455}456457func (m *MemMapFs) List() {458for _, x := range m.data {459y := mem.FileInfo{FileData: x}460fmt.Println(x.Name(), y.Size())461}462}463464465