Skip to content

Commit 34fac9d

Browse files
committed
remove mem fs with descendants
1 parent 45ef346 commit 34fac9d

File tree

2 files changed

+106
-2
lines changed

2 files changed

+106
-2
lines changed

memmap.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ package afero
1616
import (
1717
"fmt"
1818
"io"
19+
1920
"log"
2021
"os"
2122
"path/filepath"
23+
24+
"sort"
2225
"strings"
2326
"sync"
2427
"time"
@@ -88,6 +91,24 @@ func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
8891
return pfile
8992
}
9093

94+
func (m *MemMapFs) findDescendants(name string) []*mem.FileData {
95+
fData := m.getData()
96+
descendants := make([]*mem.FileData, 0, len(fData))
97+
for p, dFile := range fData {
98+
if strings.HasPrefix(p, name+FilePathSeparator) {
99+
descendants = append(descendants, dFile)
100+
}
101+
}
102+
103+
sort.Slice(descendants, func(i, j int) bool {
104+
cur := len(strings.Split(descendants[i].Name(), FilePathSeparator))
105+
next := len(strings.Split(descendants[j].Name(), FilePathSeparator))
106+
return cur < next
107+
})
108+
109+
return descendants
110+
}
111+
91112
func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) {
92113
if f == nil {
93114
return
@@ -264,10 +285,22 @@ func (m *MemMapFs) Remove(name string) error {
264285
defer m.mu.Unlock()
265286

266287
if _, ok := m.getData()[name]; ok {
288+
descendants := m.findDescendants(name)
289+
for i := 1; i <= len(descendants); i++ {
290+
descendant := descendants[len(descendants)-i]
291+
descName := descendant.Name()
292+
err := m.unRegisterWithParent(descName)
293+
if err != nil {
294+
return &os.PathError{Op: "descendant remove", Path: name, Err: err}
295+
}
296+
delete(m.getData(), descName)
297+
}
298+
267299
err := m.unRegisterWithParent(name)
268300
if err != nil {
269301
return &os.PathError{Op: "remove", Path: name, Err: err}
270302
}
303+
271304
delete(m.getData(), name)
272305
} else {
273306
return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
@@ -278,14 +311,18 @@ func (m *MemMapFs) Remove(name string) error {
278311
func (m *MemMapFs) RemoveAll(path string) error {
279312
path = normalizePath(path)
280313
m.mu.Lock()
281-
m.unRegisterWithParent(path)
314+
_ = m.unRegisterWithParent(path)
282315
m.mu.Unlock()
283316

284317
m.mu.RLock()
285318
defer m.mu.RUnlock()
286319

287320
for p := range m.getData() {
288-
if p == path || strings.HasPrefix(p, path+FilePathSeparator) {
321+
separator := FilePathSeparator
322+
if path == FilePathSeparator {
323+
separator = ""
324+
}
325+
if p == path || strings.HasPrefix(p, path+separator) {
289326
m.mu.RUnlock()
290327
m.mu.Lock()
291328
delete(m.getData(), p)

memmap_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,70 @@ func TestMemFsRenameDir(t *testing.T) {
833833
t.Errorf("Cannot recreate the subdir in the source dir: %s", err)
834834
}
835835
}
836+
837+
func TestMemMapFsRemove(t *testing.T) {
838+
t.Parallel()
839+
840+
testData := map[string]struct {
841+
dirsToCreate []string
842+
dirsToRemove []string
843+
expectedErrMsg string
844+
}{
845+
"Remove child before - success": {
846+
dirsToCreate: []string{"/parent1/parent2/fileForDelete1.txt"},
847+
dirsToRemove: []string{
848+
"/parent1/parent2/fileForDelete1.txt",
849+
"/parent1/parent2",
850+
},
851+
},
852+
"Remove parent before - should return error": {
853+
dirsToCreate: []string{"/parent1/parent2/fileForDelete1.txt"},
854+
dirsToRemove: []string{
855+
"/parent1/parent2",
856+
"/parent1/parent2/fileForDelete1.txt",
857+
},
858+
expectedErrMsg: "remove /parent1/parent2/fileForDelete1.txt: file does not exist",
859+
},
860+
"Remove root and then parent1 - should return error": {
861+
dirsToCreate: []string{"/root/parent1/parent2/fileForDelete1.txt"},
862+
dirsToRemove: []string{
863+
"/root",
864+
"/root/parent1",
865+
},
866+
expectedErrMsg: "remove /root/parent1: file does not exist",
867+
},
868+
"Remove parent2 and then parent 1 - success": {
869+
dirsToCreate: []string{"/parent1/parent2/fileForDelete1.txt"},
870+
dirsToRemove: []string{
871+
"/parent1/parent2",
872+
"/parent1",
873+
},
874+
},
875+
}
876+
877+
fs := &MemMapFs{}
878+
879+
for caseName, td := range testData {
880+
_, err := fs.Stat("/")
881+
if err == nil {
882+
err = fs.RemoveAll("/")
883+
if err != nil {
884+
t.Fatalf("%s: RemoveAll %q failed: %v", fs.Name(), "/", err)
885+
}
886+
}
887+
888+
for _, toCreate := range td.dirsToCreate {
889+
err = fs.MkdirAll(toCreate, os.FileMode(0775))
890+
if err != nil && err.Error() != td.expectedErrMsg {
891+
t.Fatalf("#CASE %v %s: Mkdir %q failed: %v", caseName, fs.Name(), toCreate, err)
892+
}
893+
}
894+
895+
for _, toRemove := range td.dirsToRemove {
896+
err = fs.Remove(toRemove)
897+
if err != nil && err.Error() != td.expectedErrMsg {
898+
t.Fatalf("#CASE %v %s: Remove %q failed: %v", caseName, fs.Name(), toRemove, err)
899+
}
900+
}
901+
}
902+
}

0 commit comments

Comments
 (0)