RemoveChildren: use RefCountedFile instead of DupFile, skip seek for child dirs
Agent-Logs-Url: https://github.com/kovidgoyal/kitty/sessions/e648c02e-d4cc-4f6c-8188-17941e8a1dd8 Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
This commit is contained in:
parent
708cff7f4b
commit
7757d160bc
1 changed files with 25 additions and 37 deletions
|
|
@ -238,15 +238,23 @@ func RemoveChildren(dirFile *os.File) error {
|
|||
|
||||
// Each stack frame is one of two kinds:
|
||||
// expand: dir != nil – read dir's children, unlink files, push subdirs
|
||||
// rmdir: dir == nil – rmdir name from parent (a dup'd fd), then close parent
|
||||
// rmdir: dir == nil – rmdir name from parent, then Unref parent
|
||||
type frame struct {
|
||||
dir *os.File // non-nil: expand this directory
|
||||
name string // rmdir: child name to remove from parent
|
||||
parent *os.File // rmdir: dup'd parent fd (closed after use)
|
||||
close bool // expand: close dir when done (false only for the root)
|
||||
dir *RefCountedFile // non-nil: expand this directory (Unref when done)
|
||||
name string // rmdir sentinel: child name to remove from parent
|
||||
parent *RefCountedFile // rmdir sentinel: ref to parent dir (Unref after rmdir)
|
||||
}
|
||||
|
||||
stack := []frame{{dir: dirFile, close: false}}
|
||||
// Only the root directory needs rewinding; it was passed in from outside
|
||||
// and may have been read before. Child dirs are freshly opened by us.
|
||||
if _, err := dirFile.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
rcRoot := NewRefCountedFile(dirFile)
|
||||
// Extra ref so the expand frame's Unref doesn't close the caller's fd.
|
||||
rcRoot.NewRef()
|
||||
|
||||
stack := []frame{{dir: rcRoot}}
|
||||
|
||||
for len(stack) > 0 {
|
||||
f := stack[len(stack)-1]
|
||||
|
|
@ -254,65 +262,45 @@ func RemoveChildren(dirFile *os.File) error {
|
|||
|
||||
if f.dir == nil {
|
||||
// rmdir sentinel: the subdirectory is now empty, remove it.
|
||||
if err := RemoveDirAt(f.parent, f.name); err != nil && firstErr == nil {
|
||||
if err := RemoveDirAt(f.parent.File(), f.name); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
f.parent.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
// Rewind so we always start from the beginning of the directory.
|
||||
if _, err := f.dir.Seek(0, io.SeekStart); err != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
if f.close {
|
||||
f.dir.Close()
|
||||
}
|
||||
f.parent.Unref()
|
||||
continue
|
||||
}
|
||||
|
||||
for {
|
||||
// Read entries in small chunks to handle very large directories.
|
||||
entries, err := f.dir.ReadDir(64)
|
||||
entries, err := f.dir.File().ReadDir(64)
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if entry.IsDir() {
|
||||
childFile, openErr := OpenDirAt(f.dir, name)
|
||||
childFile, openErr := OpenDirAt(f.dir.File(), name)
|
||||
if openErr != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = openErr
|
||||
}
|
||||
continue
|
||||
}
|
||||
parentDup, dupErr := DupFile(f.dir)
|
||||
if dupErr != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = dupErr
|
||||
}
|
||||
childFile.Close()
|
||||
continue
|
||||
}
|
||||
// Push rmdir sentinel first; LIFO ensures the expand frame
|
||||
// below is processed before this rmdir sentinel.
|
||||
stack = append(stack, frame{name: name, parent: parentDup}, frame{dir: childFile, close: true})
|
||||
// Push rmdir sentinel first; LIFO ensures the child expand
|
||||
// frame is processed before this rmdir sentinel.
|
||||
// f.dir.NewRef() adds a ref to the parent for the sentinel.
|
||||
stack = append(stack, frame{name: name, parent: f.dir.NewRef()}, frame{dir: NewRefCountedFile(childFile)})
|
||||
} else {
|
||||
if unlinkErr := UnlinkAt(f.dir, name); unlinkErr != nil && firstErr == nil {
|
||||
if unlinkErr := UnlinkAt(f.dir.File(), name); unlinkErr != nil && firstErr == nil {
|
||||
firstErr = unlinkErr
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) && firstErr == nil {
|
||||
firstErr = &os.PathError{Op: "readdir", Path: f.dir.Name(), Err: err}
|
||||
firstErr = &os.PathError{Op: "readdir", Path: f.dir.File().Name(), Err: err}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if f.close {
|
||||
f.dir.Close()
|
||||
}
|
||||
f.dir.Unref()
|
||||
}
|
||||
|
||||
_, _ = dirFile.Seek(0, io.SeekStart)
|
||||
|
|
|
|||
Loading…
Reference in a new issue