From 29af44518270efb3b625bcae44166583c014e10e Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Tue, 29 Nov 2022 14:21:35 +0300 Subject: [PATCH] Shortened long filenames --- cmd/btrtrc/README | 1 + cmd/btrtrc/fifos.go | 3 ++- storage/file.go | 11 ++++++----- storage/shortener.go | 30 ++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 storage/shortener.go diff --git a/cmd/btrtrc/README b/cmd/btrtrc/README index d485409d..8e7b15c2 100644 --- a/cmd/btrtrc/README +++ b/cmd/btrtrc/README @@ -9,6 +9,7 @@ the file to delete. But what advantages does it have? * Optimized file-based storage: * linearized I/O operations prevent creation of huge quantity of threads * cached file descriptors save a lot of syscalls +* Shortened long filenames in file-based storage * Ability to specify both IPv4 and IPv6 addresses to announce * Ability to specify DHT bootstrap nodes * Dynamic addition and removing of the torrents diff --git a/cmd/btrtrc/fifos.go b/cmd/btrtrc/fifos.go index f9aa0117..508f6c09 100644 --- a/cmd/btrtrc/fifos.go +++ b/cmd/btrtrc/fifos.go @@ -18,6 +18,7 @@ import ( "github.com/anacrolix/dht/v2" "github.com/anacrolix/torrent" "github.com/anacrolix/torrent/metainfo" + "github.com/anacrolix/torrent/storage" "github.com/anacrolix/torrent/types/infohash" "github.com/dustin/go-humanize" ) @@ -304,7 +305,7 @@ func readLinesFromFIFO(pth string) []string { } func saveTorrent(t *torrent.Torrent) error { - pth := t.Name() + TorrentExt + pth := storage.PathShortener(t.Name()) + TorrentExt if _, err := os.Stat(pth); err == nil { return nil } diff --git a/storage/file.go b/storage/file.go index aa67df15..6871cad3 100644 --- a/storage/file.go +++ b/storage/file.go @@ -115,7 +115,7 @@ func (fs fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash length: fileInfo.Length, } if f.length == 0 { - err = CreateNativeZeroLengthFile(f.path) + err = CreateNativeZeroLengthFile(PathShortener(f.path)) if err != nil { err = fmt.Errorf("creating zero length file: %w", err) return @@ -185,10 +185,11 @@ type fileTorrentImplIO struct { // Returns EOF on short or missing file. func (fst *fileTorrentImplIO) readFileAt(file file, b []byte, off int64) (n int, err error) { fdRCacheM.Lock() - centry := fdRCache[file.path] + pth := PathShortener(file.path) + centry := fdRCache[pth] if centry == nil { var fd *os.File - fd, err = os.Open(file.path) + fd, err = os.Open(pth) if os.IsNotExist(err) { // File missing is treated the same as a short file. err = io.EOF @@ -198,7 +199,7 @@ func (fst *fileTorrentImplIO) readFileAt(file file, b []byte, off int64) (n int, return } centry = &fdCacheEntry{fd: fd} - fdRCache[file.path] = centry + fdRCache[pth] = centry } fdRCacheM.Unlock() // Limit the read to within the expected bounds of this file. @@ -229,7 +230,7 @@ func (fst fileTorrentImplIO) ReadAt(b []byte, off int64) (n int, err error) { func (fst fileTorrentImplIO) WriteAt(p []byte, off int64) (n int, err error) { fst.fts.segmentLocater.Locate(segments.Extent{off, int64(len(p))}, func(i int, e segments.Extent) bool { - name := fst.fts.files[i].path + name := PathShortener(fst.fts.files[i].path) _, ok := fdMkdirAllCache[filepath.Dir(name)] if !ok { os.MkdirAll(filepath.Dir(name), 0o777) diff --git a/storage/shortener.go b/storage/shortener.go new file mode 100644 index 00000000..e3ed5df9 --- /dev/null +++ b/storage/shortener.go @@ -0,0 +1,30 @@ +package storage + +import ( + "crypto/sha1" + "encoding/hex" + "path" + "strings" + "unicode/utf8" +) + +const MaxFilenameLen = 200 + +func PathShortener(pth string) string { + parts := strings.Split(pth, "/") + for i, part := range parts { + if len(part) <= MaxFilenameLen { + continue + } + n := 0 + var short []rune + for (n < len(part)) && (len(string(short)) <= MaxFilenameLen) { + r, w := utf8.DecodeRuneInString(part[n:]) + n += w + short = append(short, r) + } + h := sha1.Sum([]byte(part)) + parts[i] = string(short[:len(short)-1]) + "-" + hex.EncodeToString(h[:]) + } + return path.Join(parts...) +} -- 2.44.0