-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
smart removal of hard linked (all in client)
- Loading branch information
SweetMnM
committed
Nov 11, 2022
1 parent
36a69e0
commit b597b31
Showing
8 changed files
with
246 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//go:build !windows && !plan9 | ||
|
||
package hardlinkfilemap | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
"strconv" | ||
"syscall" | ||
) | ||
|
||
func FileIdentifier(fi os.FileInfo) (string, error) { | ||
sys, ok := fi.Sys().(*syscall.Stat_t) | ||
if !ok { | ||
return "", errors.New("failed to get file identifier") | ||
} | ||
|
||
return strconv.FormatUint(sys.Dev, 10) + "|" + strconv.FormatUint(sys.Ino, 10), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package hardlinkfilemap | ||
|
||
import ( | ||
"os" | ||
"reflect" | ||
"strconv" | ||
) | ||
|
||
func FileIdentifier(fi os.FileInfo) (string, error) { | ||
// This is extreme hackies to get os to load the vol, idxhi and idxlo fields | ||
ok := os.SameFile(fi, fi) | ||
if !ok { | ||
return "", errors.New("error while getting file identifier") | ||
} | ||
|
||
v := reflect.Indirect(reflect.ValueOf(fi)) | ||
vol := v.FieldByName("vol").Uint() | ||
idxhi := v.FieldByName("idxhi").Uint() | ||
idxlo := v.FieldByName("idxlo").Uint() | ||
|
||
return strconv.FormatUint(vol, 10) + "|" + strconv.FormatUint(idxhi, 10) + "|" + strconv.FormatUint(idxlo, 10), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package hardlinkfilemap | ||
|
||
import ( | ||
"os" | ||
"strings" | ||
|
||
"github.com/l3uddz/tqm/config" | ||
"github.com/l3uddz/tqm/logger" | ||
"github.com/l3uddz/tqm/sliceutils" | ||
) | ||
|
||
func New(torrents map[string]config.Torrent, torrentPathMapping map[string]string) *HardlinkFileMap { | ||
tfm := &HardlinkFileMap{ | ||
hardlinkFileMap: make(map[string][]string), | ||
log: logger.GetLogger("hardlinkfilemap"), | ||
torrentPathMapping: torrentPathMapping, | ||
} | ||
|
||
for _, torrent := range torrents { | ||
tfm.AddByTorrent(torrent) | ||
} | ||
|
||
return tfm | ||
} | ||
|
||
func (t *HardlinkFileMap) ConsiderPathMapping(path string) string { | ||
for mapFrom, mapTo := range t.torrentPathMapping { | ||
if strings.HasPrefix(path, mapFrom) { | ||
return strings.Replace(path, mapFrom, mapTo, 1) | ||
} | ||
} | ||
|
||
return path | ||
} | ||
|
||
func (t *HardlinkFileMap) FileIdentifierByPath(path string) (string, bool) { | ||
stat, err1 := os.Stat(path) | ||
if err1 != nil { | ||
t.log.Warnf("Failed to stat file: %s - %s", path, err1) | ||
return "", false | ||
} | ||
|
||
id, err2 := FileIdentifier(stat) | ||
if err2 != nil { | ||
t.log.Warnf("Failed to get file identifier: %s - %s", path, err2) | ||
return "", false | ||
} | ||
|
||
return id, true | ||
} | ||
|
||
func (t *HardlinkFileMap) AddByTorrent(torrent config.Torrent) { | ||
for _, f := range torrent.Files { | ||
f = t.ConsiderPathMapping(f) | ||
|
||
id, ok := t.FileIdentifierByPath(f) | ||
|
||
if !ok { | ||
continue | ||
} | ||
|
||
if _, exists := t.hardlinkFileMap[id]; exists { | ||
// file id already associated with other paths | ||
t.hardlinkFileMap[id] = append(t.hardlinkFileMap[id], f) | ||
continue | ||
} | ||
|
||
// file id has not been seen before, create id entry | ||
t.hardlinkFileMap[id] = []string{f} | ||
} | ||
} | ||
|
||
func (t *HardlinkFileMap) RemoveByTorrent(torrent config.Torrent) { | ||
for _, f := range torrent.Files { | ||
f = t.ConsiderPathMapping(f) | ||
|
||
id, ok := t.FileIdentifierByPath(f) | ||
|
||
if !ok { | ||
continue | ||
} | ||
|
||
if _, exists := t.hardlinkFileMap[id]; exists { | ||
// remove this path from the id entry | ||
i := sliceutils.IndexOfString(t.hardlinkFileMap[id], f) | ||
if i != -1 { | ||
t.hardlinkFileMap[id] = sliceutils.FastDelete(t.hardlinkFileMap[id], i) | ||
} | ||
|
||
// remove id entry if no more paths | ||
if len(t.hardlinkFileMap[id]) == 0 { | ||
delete(t.hardlinkFileMap, id) | ||
} | ||
|
||
continue | ||
} | ||
} | ||
} | ||
|
||
func (t *HardlinkFileMap) IsTorrentUnique(torrent config.Torrent) bool { | ||
for _, f := range torrent.Files { | ||
f = t.ConsiderPathMapping(f) | ||
|
||
id, ok := t.FileIdentifierByPath(f) | ||
|
||
if !ok { | ||
return false | ||
} | ||
|
||
t.log.Infof("File: %s - ID: %s", f, id) | ||
// preview the file id entry | ||
t.log.Infof("File ID Entry: %v", t.hardlinkFileMap[id]) | ||
|
||
if paths, exists := t.hardlinkFileMap[id]; exists && len(paths) > 1 { | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (t *HardlinkFileMap) NoInstances(torrent config.Torrent) bool { | ||
for _, f := range torrent.Files { | ||
f = t.ConsiderPathMapping(f) | ||
|
||
id, ok := t.FileIdentifierByPath(f) | ||
|
||
if !ok { | ||
return false | ||
} | ||
|
||
if paths, exists := t.hardlinkFileMap[id]; exists && len(paths) != 0 { | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (t *HardlinkFileMap) Length() int { | ||
return len(t.hardlinkFileMap) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package hardlinkfilemap | ||
|
||
import ( | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type HardlinkFileMap struct { | ||
// hardlinkFileMap map[string]map[string]config.Torrent | ||
hardlinkFileMap map[string][]string | ||
log *logrus.Entry | ||
torrentPathMapping map[string]string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package sliceutils | ||
|
||
func FastDelete[S ~[]E, E any](s S, i int) S { | ||
_ = s[i] // bounds check | ||
|
||
s[i] = s[len(s)-1] | ||
return s[:len(s)-1] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package sliceutils | ||
|
||
func IndexOfString(s []string, e string) int { | ||
for i, v := range s { | ||
if v == e { | ||
return i | ||
} | ||
} | ||
|
||
return -1 | ||
} |