Reapply the fix from #1054, but without getting into an infinite look in case of SMB fs errors. See #1164

This commit is contained in:
Deluan
2021-07-17 21:06:53 -04:00
parent 03ad6e972a
commit e61cf3217d
2 changed files with 105 additions and 15 deletions
+26 -15
View File
@@ -66,11 +66,14 @@ func loadDir(ctx context.Context, dirPath string) ([]string, *dirStats, error) {
}
stats.ModTime = dirInfo.ModTime()
dirEntries, err := fullReadDir(dirPath)
dir, err := os.Open(dirPath)
if err != nil {
log.Error(ctx, "Error in ReadDir", "path", dirPath, err)
log.Error(ctx, "Error in Opening directory", "path", dirPath, err)
return children, stats, err
}
defer dir.Close()
dirEntries := fullReadDir(ctx, dir)
for _, entry := range dirEntries {
isDir, err := isDirOrSymlinkToDir(dirPath, entry)
// Skip invalid symlinks
@@ -100,20 +103,28 @@ func loadDir(ctx context.Context, dirPath string) ([]string, *dirStats, error) {
return children, stats, nil
}
func fullReadDir(name string) ([]os.DirEntry, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
// fullReadDir reads all files in the folder, skipping the ones with errors.
// It also detects when it is "stuck" with an error in the same directory over and over.
// In this case, it and returns whatever it was able to read until it got stuck.
// See discussion here: https://github.com/navidrome/navidrome/issues/1164#issuecomment-881922850
func fullReadDir(ctx context.Context, dir fs.ReadDirFile) []os.DirEntry {
var allDirs []os.DirEntry
var prevErrStr = ""
for {
dirs, err := dir.ReadDir(-1)
allDirs = append(allDirs, dirs...)
if err == nil {
break
}
log.Warn(ctx, "Skipping DirEntry", err)
if prevErrStr == err.Error() {
log.Error(ctx, "Duplicate DirEntry failure, bailing", err)
break
}
prevErrStr = err.Error()
}
defer f.Close()
dirs, err := f.ReadDir(-1)
if err != nil {
log.Warn("Skipping DirEntry", err)
return nil, nil
}
sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
return dirs, nil
sort.Slice(allDirs, func(i, j int) bool { return allDirs[i].Name() < allDirs[j].Name() })
return allDirs
}
// isDirOrSymlinkToDir returns true if and only if the dirEnt represents a file