Preserve directory traversal failure phases
This commit is contained in:
@@ -141,40 +141,59 @@ func (a *filesystemAccess) open(name string) (fs.File, error) {
|
||||
return a.root.Open(relative)
|
||||
}
|
||||
|
||||
type directoryReadError struct {
|
||||
opened bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *directoryReadError) Error() string { return "directory could not be read" }
|
||||
func (e *directoryReadError) Unwrap() error { return e.err }
|
||||
|
||||
func missingDirectoryOpen(err error) bool {
|
||||
var failure *directoryReadError
|
||||
return errors.As(err, &failure) && !failure.opened && errors.Is(err, fs.ErrNotExist)
|
||||
}
|
||||
|
||||
func (a *filesystemAccess) readDirectory(name string, visit func(fs.DirEntry) bool) error {
|
||||
file, err := a.open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
return &directoryReadError{err: err}
|
||||
}
|
||||
failure := func(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &directoryReadError{opened: true, err: err}
|
||||
}
|
||||
if reader, ok := file.(fs.ReadDirFile); ok {
|
||||
for {
|
||||
entries, readErr := reader.ReadDir(globReadBatch)
|
||||
for _, entry := range entries {
|
||||
if !visit(entry) {
|
||||
return file.Close()
|
||||
return failure(file.Close())
|
||||
}
|
||||
}
|
||||
if errors.Is(readErr, io.EOF) {
|
||||
return file.Close()
|
||||
return failure(file.Close())
|
||||
}
|
||||
if readErr != nil {
|
||||
_ = file.Close()
|
||||
return readErr
|
||||
return failure(readErr)
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
_ = file.Close()
|
||||
return io.ErrNoProgress
|
||||
return failure(io.ErrNoProgress)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return err
|
||||
return failure(err)
|
||||
}
|
||||
// fs.ReadDir provides the standard fallback for implementations whose opened
|
||||
// files do not implement fs.ReadDirFile. It may allocate all entries itself.
|
||||
entries, err := fs.ReadDir(a.native, name)
|
||||
if err != nil {
|
||||
return err
|
||||
return failure(err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !visit(entry) {
|
||||
|
||||
Reference in New Issue
Block a user