Skip to content

Commit d287f00

Browse files
committed
RemoveAll fix, 96% coverage
1 parent 6d7b032 commit d287f00

File tree

2 files changed

+464
-8
lines changed

2 files changed

+464
-8
lines changed

httpfs.go

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,25 @@ func (filer *Httpfs) Remove(name string) error {
6262
return filer.fs.Remove(name)
6363
}
6464

65+
// RemoveAller is an optional interface that filesystems can implement
66+
// to provide optimized recursive directory removal.
67+
type RemoveAller interface {
68+
RemoveAll(path string) error
69+
}
70+
6571
// RemoveAll removes a directory after removing all children of that directory.
66-
func (filer *Httpfs) RemoveAll(path string) (err error) {
72+
// If the underlying filesystem implements RemoveAller, it delegates to that.
73+
// Returns nil for non-existent paths (matching os.RemoveAll behavior).
74+
func (filer *Httpfs) RemoveAll(path string) error {
75+
// Check if the underlying filesystem implements RemoveAll
76+
if ra, ok := filer.fs.(RemoveAller); ok {
77+
err := ra.RemoveAll(path)
78+
if os.IsNotExist(err) {
79+
return nil
80+
}
81+
return err
82+
}
83+
6784
info, err := filer.Stat(path)
6885
if err != nil {
6986
if os.IsNotExist(err) {
@@ -72,34 +89,46 @@ func (filer *Httpfs) RemoveAll(path string) (err error) {
7289
return err
7390
}
7491

75-
// if it's not a directory remove it and return
92+
// If it's not a directory, just remove it
7693
if !info.IsDir() {
7794
return filer.Remove(path)
7895
}
7996

80-
f, err := filer.OpenFile(path, os.O_RDWR, 0700)
97+
// Open directory read-only to list entries
98+
f, err := filer.OpenFile(path, os.O_RDONLY, 0)
8199
if err != nil {
82100
if os.IsNotExist(err) {
83101
return nil
84102
}
85103
return err
86104
}
87105

88-
// get and loop through each directory entry calling remove all recursively
89106
infos, err := f.Readdir(0)
107+
f.Close()
90108
if err != nil {
91109
return err
92110
}
93-
f.Close()
94111

95112
for _, info := range infos {
96-
err = filer.RemoveAll(filepath.Join(path, info.Name()))
97-
if err != nil {
113+
name := info.Name()
114+
// Skip . and .. to avoid infinite recursion
115+
if name == "." || name == ".." {
116+
continue
117+
}
118+
if err := filer.RemoveAll(filepath.Join(path, name)); err != nil {
98119
return err
99120
}
100121
}
101122

102-
return filer.Remove(path)
123+
err = filer.Remove(path)
124+
// Some filesystems (e.g., memfs) return "directory not empty" even when
125+
// only . and .. remain. Check if the directory was actually removed.
126+
if err != nil {
127+
if _, statErr := filer.Stat(path); os.IsNotExist(statErr) {
128+
return nil
129+
}
130+
}
131+
return err
103132
}
104133

105134
// Stat returns the FileInfo structure describing file. If there is an error, it will be of type *PathError.

0 commit comments

Comments
 (0)