Skip to content

Commit

Permalink
detect hard links
Browse files Browse the repository at this point in the history
  • Loading branch information
dundee committed Feb 12, 2021
1 parent 653bcd1 commit 26580b8
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ Gdu has two modes: interactive (default) and non-interactive.

Non-interactive mode is started automtically when TTY is not detected (using [go-isatty](https://github.com/mattn/go-isatty)), for example if the output is being piped to a file, or it can be started explicitly by using a flag.

Hard links are counted only once.

## Running tests

make test
Expand Down
7 changes: 5 additions & 2 deletions analyze/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ func ProcessDir(path string, progress *CurrentProgress, ignore ShouldDirBeIgnore
dir := processDir(path, progress, concurrencyLimitChannel, &wait, ignore)
dir.BasePath = filepath.Dir(path)
wait.Wait()
dir.UpdateStats()

links := make(AlreadyCountedHardlinks, 10)
dir.UpdateStats(links)

progress.Mutex.Lock()
progress.Done = true
Expand Down Expand Up @@ -114,11 +116,12 @@ func processDir(path string, progress *CurrentProgress, concurrencyLimitChannel
Name: f.Name(),
Flag: flag,
Size: f.Size(),
Usage: getUsage(f),
ItemCount: 1,
Parent: &dir,
}

setPlatformSpecificAttrs(file, f)

totalSize += f.Size()

mutex.Lock()
Expand Down
4 changes: 1 addition & 3 deletions analyze/dir_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ import (
"os"
)

func getUsage(f os.FileInfo) int64 {
return int64(0)
}
func setPlatformSpecificAttrs(file *File, f os.FileInfo) {}
19 changes: 19 additions & 0 deletions analyze/dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ func TestFlags(t *testing.T) {
assert.Equal(t, '@', dir.Files[0].Files[1].Flag)
}

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

os.Link("test_dir/nested/file2", "test_dir/nested/file3")

dir := ProcessDir("test_dir", &CurrentProgress{Mutex: &sync.Mutex{}}, func(_ string) bool { return false })

assert.Equal(t, int64(7+4096*3), dir.Size) // file2 and file3 are counted just once for size
assert.Equal(t, int64(4096*5), dir.Usage) // and usage
assert.Equal(t, 6, dir.ItemCount) // but twice for item count

// test file3
assert.Equal(t, "file3", dir.Files[0].Files[1].Name)
assert.Equal(t, int64(2), dir.Files[0].Files[1].Size)
assert.Equal(t, int64(4096), dir.Files[0].Files[1].Usage)
assert.Equal(t, 'H', dir.Files[0].Files[1].Flag)
}

func BenchmarkProcessDir(b *testing.B) {
fin := testdir.CreateTestDir()
defer fin()
Expand Down
11 changes: 6 additions & 5 deletions analyze/dir_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import (

const devBSize = 512

func getUsage(f os.FileInfo) int64 {
var usage int64 = 0

func setPlatformSpecificAttrs(file *File, f os.FileInfo) {
switch stat := f.Sys().(type) {
case *syscall.Stat_t:
usage = stat.Blocks * devBSize
file.Usage = stat.Blocks * devBSize

if stat.Nlink > 1 {
file.MutliLinkInode = stat.Ino
}
}
return usage
}
22 changes: 19 additions & 3 deletions analyze/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ type File struct {
IsDir bool
Files Files
Parent *File
MutliLinkInode uint64 // Inode number of file with multiple links (hard link)
}

// AlreadyCountedHardlinks holds all files with hardlinks that have already been counted
type AlreadyCountedHardlinks map[uint64]bool

// Path retruns absolute path of the file
func (f *File) Path() string {
if f.BasePath != "" {
Expand Down Expand Up @@ -50,18 +54,30 @@ func (f *File) RemoveFile(file *File) error {
}

// UpdateStats recursively updates size and item count
func (f *File) UpdateStats() {
func (f *File) UpdateStats(links AlreadyCountedHardlinks) {
if !f.IsDir {
return
}
totalSize := int64(4096)
totalUsage := int64(4096)
var itemCount int
for _, entry := range f.Files {
entry.UpdateStats()
if entry.IsDir {
entry.UpdateStats(links)
}

itemCount += entry.ItemCount

if entry.MutliLinkInode > 0 {
if !links[entry.MutliLinkInode] {
links[entry.MutliLinkInode] = true
} else {
entry.Flag = 'H'
continue
}
}
totalSize += entry.Size
totalUsage += entry.Usage
itemCount += entry.ItemCount
}
f.ItemCount = itemCount + 1
f.Size = totalSize
Expand Down
4 changes: 3 additions & 1 deletion tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ func (ui *UI) AnalyzePath(path string, analyzer analyze.Analyzer, parentDir *ana
ui.currentDir.Parent = parentDir
parentDir.Files = parentDir.Files.RemoveByName(ui.currentDir.Name)
parentDir.Files = append(parentDir.Files, ui.currentDir)
ui.topDir.UpdateStats()

links := make(analyze.AlreadyCountedHardlinks, 10)
ui.topDir.UpdateStats(links)
} else {
ui.topDirPath = abspath
ui.topDir = ui.currentDir
Expand Down

0 comments on commit 26580b8

Please sign in to comment.