* 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
"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"
)
}
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
}
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
// 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
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.
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)
--- /dev/null
+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...)
+}