Skip to content

Commit ee6eef7

Browse files
authored
Fix rename mem fs with descendants
Fixes #141
1 parent 45ef346 commit ee6eef7

File tree

2 files changed

+139
-11
lines changed

2 files changed

+139
-11
lines changed

memmap.go

Lines changed: 54 additions & 11 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
@@ -309,29 +330,51 @@ func (m *MemMapFs) Rename(oldname, newname string) error {
309330
if _, ok := m.getData()[oldname]; ok {
310331
m.mu.RUnlock()
311332
m.mu.Lock()
312-
m.unRegisterWithParent(oldname)
333+
err := m.unRegisterWithParent(oldname)
334+
if err != nil {
335+
return err
336+
}
337+
313338
fileData := m.getData()[oldname]
314-
delete(m.getData(), oldname)
315339
mem.ChangeFileName(fileData, newname)
316340
m.getData()[newname] = fileData
341+
342+
err = m.renameDescendants(oldname, newname)
343+
if err != nil {
344+
return err
345+
}
346+
347+
delete(m.getData(), oldname)
348+
317349
m.registerWithParent(fileData, 0)
318350
m.mu.Unlock()
319351
m.mu.RLock()
320352
} else {
321353
return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
322354
}
355+
return nil
356+
}
323357

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()
358+
func (m *MemMapFs) renameDescendants(oldname, newname string) error {
359+
descendants := m.findDescendants(oldname)
360+
removes := make([]string, 0, len(descendants))
361+
for _, desc := range descendants {
362+
descNewName := strings.Replace(desc.Name(), oldname, newname, 1)
363+
err := m.unRegisterWithParent(desc.Name())
364+
if err != nil {
365+
return err
333366
}
367+
368+
removes = append(removes, desc.Name())
369+
mem.ChangeFileName(desc, descNewName)
370+
m.getData()[descNewName] = desc
371+
372+
m.registerWithParent(desc, 0)
373+
}
374+
for _, r := range removes {
375+
delete(m.getData(), r)
334376
}
377+
335378
return nil
336379
}
337380

memmap_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,88 @@ func TestMemFsRenameDir(t *testing.T) {
833833
t.Errorf("Cannot recreate the subdir in the source dir: %s", err)
834834
}
835835
}
836+
837+
func TestMemMapFsRename(t *testing.T) {
838+
t.Parallel()
839+
840+
fs := &MemMapFs{}
841+
tDir := testDir(fs)
842+
rFrom := "/renamefrom"
843+
rTo := "/renameto"
844+
rExists := "/renameexists"
845+
846+
type test struct {
847+
dirs []string
848+
from string
849+
to string
850+
exists string
851+
}
852+
853+
parts := strings.Split(tDir, "/")
854+
root := "/"
855+
if len(parts) > 1 {
856+
root = filepath.Join("/", parts[1])
857+
}
858+
859+
testData := make([]test, 0, len(parts))
860+
861+
i := len(parts)
862+
for i > 0 {
863+
prefix := strings.Join(parts[:i], "/")
864+
suffix := strings.Join(parts[i:], "/")
865+
testData = append(testData, test{
866+
dirs: []string{
867+
filepath.Join(prefix, rFrom, suffix),
868+
filepath.Join(prefix, rExists, suffix),
869+
},
870+
from: filepath.Join(prefix, rFrom),
871+
to: filepath.Join(prefix, rTo),
872+
exists: filepath.Join(prefix, rExists),
873+
})
874+
i--
875+
}
876+
877+
for _, data := range testData {
878+
err := fs.RemoveAll(root)
879+
if err != nil {
880+
t.Fatalf("%s: RemoveAll %q failed: %v", fs.Name(), root, err)
881+
}
882+
883+
for _, dir := range data.dirs {
884+
err = fs.MkdirAll(dir, os.FileMode(0775))
885+
if err != nil {
886+
t.Fatalf("%s: MkdirAll %q failed: %v", fs.Name(), dir, err)
887+
}
888+
}
889+
890+
dataCnt := len(fs.getData())
891+
err = fs.Rename(data.from, data.to)
892+
if err != nil {
893+
t.Fatalf("%s: rename %q, %q failed: %v", fs.Name(), data.from, data.to, err)
894+
}
895+
err = fs.Mkdir(data.from, os.FileMode(0775))
896+
if err != nil {
897+
t.Fatalf("%s: Mkdir %q failed: %v", fs.Name(), data.from, err)
898+
}
899+
900+
err = fs.Rename(data.from, data.exists)
901+
if err != nil {
902+
t.Errorf("%s: rename %q, %q failed: %v", fs.Name(), data.from, data.exists, err)
903+
}
904+
905+
for p := range fs.getData() {
906+
if strings.Contains(p, data.from) {
907+
t.Errorf("File was not renamed to renameto: %v", p)
908+
}
909+
}
910+
911+
_, err = fs.Stat(data.to)
912+
if err != nil {
913+
t.Errorf("%s: stat %q failed: %v", fs.Name(), data.to, err)
914+
}
915+
916+
if dataCnt != len(fs.getData()) {
917+
t.Errorf("invalid data len: expected %v, get %v", dataCnt, len(fs.getData()))
918+
}
919+
}
920+
}

0 commit comments

Comments
 (0)