cmd/ncdu: highlight read errors instead of aborting - fixes #4014

When a directory cannot be walk-ed because of a permissions error
- or any error for that matter -, ncdu mode keeps track of the error
and highlights directories that could not be read.

Previously, the error would cause ncdu to abort.

Now, directories with unreadable sub-directories are displayed in yellow and
a message warns that the total may be underestimated.

Unreadable directories themselves are displayed in red along with the error message
This commit is contained in:
Claudio Bantaloukas 2020-12-28 14:08:12 +00:00 committed by GitHub
parent 55aec19389
commit f7fe1d766b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 24 deletions

View File

@ -281,7 +281,7 @@ func (u *UI) biggestEntry() (biggest int64) {
return
}
for i := range u.entries {
size, _, _, _ := u.d.AttrI(u.sortPerm[i])
size, _, _, _, _, _ := u.d.AttrI(u.sortPerm[i])
if size > biggest {
biggest = size
}
@ -326,12 +326,18 @@ func (u *UI) Draw() error {
if y >= h-1 {
break
}
size, count, isDir, readable, entriesHaveErrors, err := u.d.AttrI(u.sortPerm[n])
fg := termbox.ColorWhite
if entriesHaveErrors {
fg = termbox.ColorYellow
}
if err != nil {
fg = termbox.ColorRed
}
bg := termbox.ColorBlack
if n == dirPos.entry {
fg, bg = bg, fg
}
size, count, isDir, readable := u.d.AttrI(u.sortPerm[n])
mark := ' '
if isDir {
mark = '/'
@ -340,6 +346,12 @@ func (u *UI) Draw() error {
if !readable {
message = " [not read yet]"
}
if entriesHaveErrors {
message = " [some subdirectories could not be read, size may be underestimated]"
}
if err != nil {
message = fmt.Sprintf(" [%s]", err)
}
extras := ""
if u.showCounts {
if count > 0 {
@ -512,8 +524,8 @@ type ncduSort struct {
// Less is part of sort.Interface.
func (ds *ncduSort) Less(i, j int) bool {
var iAvgSize, jAvgSize float64
isize, icount, _, _ := ds.d.AttrI(ds.sortPerm[i])
jsize, jcount, _, _ := ds.d.AttrI(ds.sortPerm[j])
isize, icount, _, _, _, _ := ds.d.AttrI(ds.sortPerm[i])
jsize, jcount, _, _, _, _ := ds.d.AttrI(ds.sortPerm[j])
iname, jname := ds.entries[ds.sortPerm[i]].Remote(), ds.entries[ds.sortPerm[j]].Remote()
if icount > 0 {
iAvgSize = float64(isize / icount)

View File

@ -13,13 +13,15 @@ import (
// Dir represents a directory found in the remote
type Dir struct {
parent *Dir
path string
mu sync.Mutex
count int64
size int64
entries fs.DirEntries
dirs map[string]*Dir
parent *Dir
path string
mu sync.Mutex
count int64
size int64
entries fs.DirEntries
dirs map[string]*Dir
readError error
entriesHaveErrors bool
}
// Parent returns the directory above this one
@ -35,12 +37,13 @@ func (d *Dir) Path() string {
}
// make a new directory
func newDir(parent *Dir, dirPath string, entries fs.DirEntries) *Dir {
func newDir(parent *Dir, dirPath string, entries fs.DirEntries, err error) *Dir {
d := &Dir{
parent: parent,
path: dirPath,
entries: entries,
dirs: make(map[string]*Dir),
parent: parent,
path: dirPath,
entries: entries,
dirs: make(map[string]*Dir),
readError: err,
}
// Count size in this dir
for _, entry := range entries {
@ -61,6 +64,9 @@ func newDir(parent *Dir, dirPath string, entries fs.DirEntries) *Dir {
parent.mu.Lock()
parent.count += d.count
parent.size += d.size
if d.readError != nil {
parent.entriesHaveErrors = true
}
parent.mu.Unlock()
}
return d
@ -145,18 +151,19 @@ func (d *Dir) Attr() (size int64, count int64) {
}
// AttrI returns the size, count and flags for the i-th directory entry
func (d *Dir) AttrI(i int) (size int64, count int64, isDir bool, readable bool) {
func (d *Dir) AttrI(i int) (size int64, count int64, isDir bool, readable bool, entriesHaveErrors bool, err error) {
d.mu.Lock()
defer d.mu.Unlock()
subDir, isDir := d.getDir(i)
if !isDir {
return d.entries[i].Size(), 0, false, true
return d.entries[i].Size(), 0, false, true, d.entriesHaveErrors, d.readError
}
if subDir == nil {
return 0, 0, true, false
return 0, 0, true, false, false, nil
}
size, count = subDir.Attr()
return size, count, true, true
return size, count, true, true, subDir.entriesHaveErrors, subDir.readError
}
// Scan the Fs passed in, returning a root directory channel and an
@ -169,9 +176,6 @@ func Scan(ctx context.Context, f fs.Fs) (chan *Dir, chan error, chan struct{}) {
go func() {
parents := map[string]*Dir{}
err := walk.Walk(ctx, f, "", false, ci.MaxDepth, func(dirPath string, entries fs.DirEntries, err error) error {
if err != nil {
return err // FIXME mark directory as errored instead of aborting
}
var parent *Dir
if dirPath != "" {
parentPath := path.Dir(dirPath)
@ -184,7 +188,7 @@ func Scan(ctx context.Context, f fs.Fs) (chan *Dir, chan error, chan struct{}) {
errChan <- errors.Errorf("couldn't find parent for %q", dirPath)
}
}
d := newDir(parent, dirPath, entries)
d := newDir(parent, dirPath, entries, err)
parents[dirPath] = d
if dirPath == "" {
root <- d