Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/gdu/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type UI interface {
SetIgnoreFromFile(ignoreFile string) error
SetIgnoreHidden(value bool)
SetFollowSymlinks(value bool)
SetShowAnnexedSize(value bool)
SetAnalyzer(analyzer common.Analyzer)
StartUILoop() error
}
Expand All @@ -54,6 +55,7 @@ type Flags struct {
ShowDisks bool `yaml:"-"`
ShowApparentSize bool `yaml:"show-apparent-size"`
ShowRelativeSize bool `yaml:"show-relative-size"`
ShowAnnexedSize bool `yaml:"show-annexed-size"`
ShowVersion bool `yaml:"-"`
ShowItemCount bool `yaml:"show-item-count"`
ShowMTime bool `yaml:"show-mtime"`
Expand Down Expand Up @@ -183,6 +185,9 @@ func (a *App) Run() error {
if a.Flags.FollowSymlinks {
ui.SetFollowSymlinks(true)
}
if a.Flags.ShowAnnexedSize {
ui.SetShowAnnexedSize(true)
}
if err := a.setNoCross(path); err != nil {
return err
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/gdu/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,21 @@ func TestFollowSymlinks(t *testing.T) {
assert.Nil(t, err)
}

func TestShowAnnexedSize(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()

out, err := runApp(
&Flags{LogFile: "/dev/null", ShowAnnexedSize: true},
[]string{"test_dir"},
false,
testdev.DevicesInfoGetterMock{},
)

assert.Contains(t, out, "nested")
assert.Nil(t, err)
}

func TestAnalyzePathProfiling(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()
Expand Down
4 changes: 4 additions & 0 deletions cmd/gdu/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func init() {
&af.FollowSymlinks, "follow-symlinks", "L", false,
"Follow symlinks for files, i.e. show the size of the file to which symlink points to (symlinks to directories are not followed)",
)
flags.BoolVarP(
&af.ShowAnnexedSize, "show-annexed-size", "A", false,
"Use apparent size of git-annex'ed files in case files are not present locally (real usage is zero)",
)
flags.BoolVarP(&af.NoCross, "no-cross", "x", false, "Do not cross filesystem boundaries")
flags.BoolVarP(&af.ConstGC, "const-gc", "g", false, "Enable memory garbage collection during analysis with constant level set by GOGC")
flags.BoolVar(&af.Profiling, "enable-profiling", false, "Enable collection of profiling data and provide it on http://localhost:6060/debug/pprof/")
Expand Down
1 change: 1 addition & 0 deletions internal/common/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ShouldDirBeIgnored func(name, path string) bool
type Analyzer interface {
AnalyzeDir(path string, ignore ShouldDirBeIgnored, constGC bool) fs.Item
SetFollowSymlinks(bool)
SetShowAnnexedSize(bool)
GetProgressChan() chan CurrentProgress
GetDone() SignalGroup
ResetProgress()
Expand Down
5 changes: 5 additions & 0 deletions internal/common/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ func (ui *UI) SetFollowSymlinks(v bool) {
ui.Analyzer.SetFollowSymlinks(v)
}

// SetShowAnnexedSize sets whether to use annexed size of git-annex files
func (ui *UI) SetShowAnnexedSize(v bool) {
ui.Analyzer.SetShowAnnexedSize(v)
}

// binary multiplies prefixes (IEC)
const (
_ float64 = 1 << (10 * iota)
Expand Down
17 changes: 16 additions & 1 deletion internal/common/ui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@ func TestSetFollowSymlinks(t *testing.T) {
assert.Equal(t, true, ui.Analyzer.(*MockedAnalyzer).FollowSymlinks)
}

func TestSetShowAnnexedSize(t *testing.T) {
ui := UI{
Analyzer: &MockedAnalyzer{},
}
ui.SetShowAnnexedSize(true)

assert.Equal(t, true, ui.Analyzer.(*MockedAnalyzer).ShowAnnexedSize)
}

type MockedAnalyzer struct {
FollowSymlinks bool
FollowSymlinks bool
ShowAnnexedSize bool
}

// AnalyzeDir returns dir with files with different size exponents
Expand Down Expand Up @@ -51,3 +61,8 @@ func (a *MockedAnalyzer) ResetProgress() {}
func (a *MockedAnalyzer) SetFollowSymlinks(v bool) {
a.FollowSymlinks = v
}

// SetShowAnnexedSize does nothing
func (a *MockedAnalyzer) SetShowAnnexedSize(v bool) {
a.ShowAnnexedSize = v
}
3 changes: 3 additions & 0 deletions internal/testanalyze/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ func (a *MockedAnalyzer) ResetProgress() {}
// SetFollowSymlinks does nothing
func (a *MockedAnalyzer) SetFollowSymlinks(v bool) {}

// SetShowAnnexedSize does nothing
func (a *MockedAnalyzer) SetShowAnnexedSize(v bool) {}

// ItemFromDirWithErr returns error
func ItemFromDirWithErr(dir, file fs.Item) error {
return errors.New("Failed")
Expand Down
37 changes: 37 additions & 0 deletions pkg/analyze/dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,43 @@ func TestFollowSymlink(t *testing.T) {
assert.Equal(t, 'e', dir.Files[1].GetFlag())
}

func TestGitAnnexSymlink(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()

err := os.Mkdir("test_dir/empty", 0o644)
assert.Nil(t, err)

err = os.Symlink(
".git/annex/objects/qx/qX/SHA256E-s967858083--"+
"3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4/SHA256E-s967858083--"+
"3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4",
"test_dir/nested/file3")
assert.Nil(t, err)

analyzer := CreateAnalyzer()
analyzer.SetFollowSymlinks(true)
analyzer.SetShowAnnexedSize(true)
dir := analyzer.AnalyzeDir(
"test_dir", func(_, _ string) bool { return false }, false,
).(*Dir)
analyzer.GetDone().Wait()
dir.UpdateStats(make(fs.HardLinkedItems))

sort.Sort(sort.Reverse(dir.Files))

assert.Equal(t, int64(967858083+7+4096*4), dir.Size)
assert.Equal(t, 7, dir.ItemCount)

// test file3
assert.Equal(t, "nested", dir.Files[0].GetName())
assert.Equal(t, "file3", dir.Files[0].(*Dir).Files[1].GetName())
assert.Equal(t, int64(967858083), dir.Files[0].(*Dir).Files[1].GetSize())
assert.Equal(t, '@', dir.Files[0].(*Dir).Files[1].GetFlag())

assert.Equal(t, 'e', dir.Files[1].GetFlag())
}

func TestBrokenSymlinkSkipped(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()
Expand Down
23 changes: 7 additions & 16 deletions pkg/analyze/parallel.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type ParallelAnalyzer struct {
wait *WaitGroup
ignoreDir common.ShouldDirBeIgnored
followSymlinks bool
gitAnnexedSize bool
}

// CreateAnalyzer returns Analyzer
Expand All @@ -45,6 +46,11 @@ func (a *ParallelAnalyzer) SetFollowSymlinks(v bool) {
a.followSymlinks = v
}

// SetShowAnnexedSize sets whether to use annexed size of git-annex files
func (a *ParallelAnalyzer) SetShowAnnexedSize(v bool) {
a.gitAnnexedSize = v
}

// GetProgressChan returns channel for getting progress
func (a *ParallelAnalyzer) GetProgressChan() chan common.CurrentProgress {
return a.progressOutChan
Expand Down Expand Up @@ -140,7 +146,7 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir {
continue
}
if a.followSymlinks && info.Mode()&os.ModeSymlink != 0 {
infoF, err := followSymlink(entryPath)
infoF, err := followSymlink(entryPath, a.gitAnnexedSize)
if err != nil {
log.Print(err.Error())
dir.Flag = '!'
Expand Down Expand Up @@ -219,18 +225,3 @@ func getFlag(f os.FileInfo) rune {
}
return ' '
}

func followSymlink(path string) (os.FileInfo, error) {
target, err := filepath.EvalSymlinks(path)
if err != nil {
return nil, err
}
tInfo, err := os.Lstat(target)
if err != nil {
return nil, err
}
if !tInfo.IsDir() {
return tInfo, nil
}
return nil, nil
}
8 changes: 7 additions & 1 deletion pkg/analyze/sequential.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type SequentialAnalyzer struct {
wait *WaitGroup
ignoreDir common.ShouldDirBeIgnored
followSymlinks bool
gitAnnexedSize bool
}

// CreateSeqAnalyzer returns Analyzer
Expand All @@ -42,6 +43,11 @@ func (a *SequentialAnalyzer) SetFollowSymlinks(v bool) {
a.followSymlinks = v
}

// SetShowAnnexedSize sets whether to use annexed size of git-annex files
func (a *SequentialAnalyzer) SetShowAnnexedSize(v bool) {
a.gitAnnexedSize = v
}

// GetProgressChan returns channel for getting progress
func (a *SequentialAnalyzer) GetProgressChan() chan common.CurrentProgress {
return a.progressOutChan
Expand Down Expand Up @@ -127,7 +133,7 @@ func (a *SequentialAnalyzer) processDir(path string) *Dir {
continue
}
if a.followSymlinks && info.Mode()&os.ModeSymlink != 0 {
infoF, err := followSymlink(entryPath)
infoF, err := followSymlink(entryPath, a.gitAnnexedSize)
if err != nil {
log.Print(err.Error())
dir.Flag = '!'
Expand Down
5 changes: 5 additions & 0 deletions pkg/analyze/stored.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type StoredAnalyzer struct {
wait *WaitGroup
ignoreDir common.ShouldDirBeIgnored
followSymlinks bool
gitAnnexedSize bool
}

// CreateStoredAnalyzer returns Analyzer
Expand Down Expand Up @@ -57,6 +58,10 @@ func (a *StoredAnalyzer) SetFollowSymlinks(v bool) {
a.followSymlinks = v
}

func (a *StoredAnalyzer) SetShowAnnexedSize(v bool) {
a.gitAnnexedSize = v
}

// ResetProgress returns progress
func (a *StoredAnalyzer) ResetProgress() {
a.progress = &common.CurrentProgress{}
Expand Down
40 changes: 40 additions & 0 deletions pkg/analyze/symlink.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package analyze

import (
"os"
"path/filepath"
"strings"

"github.com/dundee/gdu/v5/pkg/annex"
)

func followSymlink(path string, gitAnnexedSize bool) (tInfo os.FileInfo, err error) {
target, err := filepath.EvalSymlinks(path)
if err != nil {
target, err = os.Readlink(path)
if err != nil {
return nil, err
}
if gitAnnexedSize && strings.Contains(target, ".git/annex/objects") {
tInfo, err = os.Lstat(path)
if err != nil {
return nil, err
}

name := filepath.Base(target)
tInfo = annex.AnnexedFileInfo(tInfo, name)
return tInfo, nil
}
}

tInfo, err = os.Lstat(target)
if err != nil {
return nil, err
}

if tInfo.IsDir() {
return nil, nil
}

return tInfo, nil
}
42 changes: 42 additions & 0 deletions pkg/analyze/symlink_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package analyze

import (
"os"
"testing"

"github.com/dundee/gdu/v5/internal/testdir"
"github.com/stretchr/testify/assert"
)

func TestFollowSymlinkErr(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()

err := os.Mkdir("test_dir/empty", 0o644)
assert.Nil(t, err)

err = os.Symlink(
".git/annex/objects/qx/qX/SHA256E-s967858083--"+
"3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4/SHA256E-s967858083--"+
"3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4",
"test_dir/nested/file3")
assert.Nil(t, err)

err = os.Symlink(
"test_dir/nested",
"test_dir/some_dir")
assert.Nil(t, err)

_, err = followSymlink("xxx", false)
assert.ErrorContains(t, err, "no such file or directory")

_, err = followSymlink("test_dir/nested/file3", false)
assert.ErrorContains(t, err, "no such file or directory")

_, err = followSymlink("test_dir/nested/file3", true)
assert.NoError(t, err)

res, err := followSymlink("test_dir/some_dir", true)
assert.Equal(t, nil, res)
assert.NoError(t, err)
}
Loading