From 381d4a7159fa9cec8f22b83615d9cb5cb4d15f33 Mon Sep 17 00:00:00 2001 From: "John K. Luebs" Date: Thu, 5 Oct 2023 12:00:30 -0500 Subject: [PATCH] Factor out hard link code for cross platform build Move system specific code out of entry so the build continues to work on Windows. Hard links are not currently supported on Windows, the behavior is a no-op. --- src/duplicacy_entry.go | 58 ++++++++++++++-------------------- src/duplicacy_utils_others.go | 19 +++++++++++ src/duplicacy_utils_windows.go | 6 ++++ 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/duplicacy_entry.go b/src/duplicacy_entry.go index 39fe09a..0742131 100644 --- a/src/duplicacy_entry.go +++ b/src/duplicacy_entry.go @@ -18,7 +18,6 @@ import ( "sort" "strconv" "strings" - "syscall" "time" "github.com/vmihailenco/msgpack" @@ -752,11 +751,6 @@ func (files FileInfoCompare) Less(i, j int) bool { } } -type listEntryLinkKey struct { - dev uint64 - ino uint64 -} - type ListingState struct { linkIndex int linkTable map[listEntryLinkKey]int // map unique inode details to initially found path @@ -816,34 +810,30 @@ func ListEntries(top string, path string, patterns []string, nobackupFile string continue } - var linkKey *listEntryLinkKey - - if runtime.GOOS != "windows" && !entry.IsDir() { - if stat := f.Sys().(*syscall.Stat_t); stat != nil && stat.Nlink > 1 { - k := listEntryLinkKey{dev: uint64(stat.Dev), ino: uint64(stat.Ino)} - if linkIndex, seen := listingState.linkTable[k]; seen { - if linkIndex == -1 { - LOG_DEBUG("LIST_EXCLUDE", "%s is excluded by attribute (hard link)", entry.Path) - continue - } - entry.Size = 0 - if entry.IsFile() { - entry.Link = strconv.FormatInt(int64(linkIndex), 16) - } else { - entry.EndChunk = entryHardLinkTargetChunkMarker - entry.EndOffset = linkIndex - } - listingChannel <- entry + linkKey, isHardLinked := entry.getHardLinkKey(f) + if isHardLinked { + if linkIndex, seen := listingState.linkTable[linkKey]; seen { + if linkIndex == -1 { + LOG_DEBUG("LIST_EXCLUDE", "%s was excluded or skipped (hard link)", entry.Path) continue - } else { - if entry.IsFile() { - entry.Link = "/" - } else { - entry.EndChunk = entryHardLinkRootChunkMarker - } - listingState.linkTable[k] = -1 - linkKey = &k } + + entry.Size = 0 + if entry.IsFile() { + entry.Link = strconv.FormatInt(int64(linkIndex), 16) + } else { + entry.EndChunk = entryHardLinkTargetChunkMarker + entry.EndOffset = linkIndex + } + listingChannel <- entry + continue + } else { + if entry.IsFile() { + entry.Link = "/" + } else { + entry.EndChunk = entryHardLinkRootChunkMarker + } + listingState.linkTable[linkKey] = -1 } } @@ -892,8 +882,8 @@ func ListEntries(top string, path string, patterns []string, nobackupFile string continue } - if linkKey != nil { - listingState.linkTable[*linkKey] = listingState.linkIndex + if isHardLinked { + listingState.linkTable[linkKey] = listingState.linkIndex listingState.linkIndex++ } diff --git a/src/duplicacy_utils_others.go b/src/duplicacy_utils_others.go index edbd748..3447b62 100644 --- a/src/duplicacy_utils_others.go +++ b/src/duplicacy_utils_others.go @@ -47,6 +47,25 @@ func SetOwner(fullPath string, entry *Entry, fileInfo *os.FileInfo) bool { return true } +type listEntryLinkKey struct { + dev uint64 + ino uint64 +} + +func (entry *Entry) getHardLinkKey(f os.FileInfo) (key listEntryLinkKey, linked bool) { + if entry.IsDir() { + return + } + stat := f.Sys().(*syscall.Stat_t) + if stat == nil || stat.Nlink < 2 { + return + } + key.dev = uint64(stat.Dev) + key.ino = uint64(stat.Ino) + linked = true + return +} + func (entry *Entry) ReadSpecial(fileInfo os.FileInfo) bool { if fileInfo.Mode()&(os.ModeDevice|os.ModeCharDevice) == 0 { return true diff --git a/src/duplicacy_utils_windows.go b/src/duplicacy_utils_windows.go index 990c257..cf6414e 100644 --- a/src/duplicacy_utils_windows.go +++ b/src/duplicacy_utils_windows.go @@ -110,6 +110,12 @@ func SetOwner(fullPath string, entry *Entry, fileInfo *os.FileInfo) bool { return true } +type listEntryLinkKey struct{} + +func (entry *Entry) getHardLinkKey(f os.FileInfo) (key listEntryLinkKey, linked bool) { + return +} + func (entry *Entry) ReadAttributes(top string) { }