Skip to content

Commit

Permalink
override storage provider in config
Browse files Browse the repository at this point in the history
  • Loading branch information
cenkalti committed Feb 2, 2025
1 parent 267743b commit 8bcf2d6
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 17 deletions.
6 changes: 6 additions & 0 deletions internal/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ type File interface {
io.WriterAt
io.Closer
}

// Provider is a torrent storage provider.
type Provider interface {
// GetStorage returns a storage for a torrent.
GetStorage(torrentID string) (Storage, error)
}
9 changes: 8 additions & 1 deletion torrent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/cenkalti/log"
"github.com/cenkalti/rain/internal/metainfo"
"github.com/cenkalti/rain/internal/storage"
)

var (
Expand All @@ -23,9 +24,11 @@ type Config struct {
// Database file to save resume data.
Database string `yaml:"database"`
// DataDir is where files are downloaded.
// Effective only when default storage provider is used.
DataDir string `yaml:"data-dir"`
// If true, torrent files are saved into <data_dir>/<torrent_id>/<torrent_name>.
// Useful if downloading the same torrent from multiple sources.
// Effective only when default storage provider is used.
DataDirIncludesTorrentID bool `yaml:"data-dir-includes-torrent-id"`
// Host to listen for TCP Acceptor. Port is computed automatically
Host string `yaml:"host"`
Expand Down Expand Up @@ -78,7 +81,8 @@ type Config struct {
HealthCheckInterval time.Duration `yaml:"health-check-interval"`
// If torrent loop is stuck for more than this duration. Program crashes with stacktrace.
HealthCheckTimeout time.Duration `yaml:"health-check-timeout"`
// The unix permission of created files, execute bit is removed for files
// The unix permission of created files, execute bit is removed for files.
// Effective only when default storage provider is used.
FilePermissions fs.FileMode `yaml:"file-permissions"`

// Enable RPC server
Expand Down Expand Up @@ -198,6 +202,9 @@ type Config struct {

// Replace default log handler
CustomLogHandler log.Handler `yaml:"-"`
// Replace default storage provider
CustomStorage storage.Provider `yaml:"-"`

// Enable debugging
Debug bool `yaml:"debug"`
}
Expand Down
18 changes: 6 additions & 12 deletions torrent/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/cenkalti/rain/internal/resumer/boltdbresumer"
"github.com/cenkalti/rain/internal/semaphore"
"github.com/cenkalti/rain/internal/storage"
"github.com/cenkalti/rain/internal/storage/filestorage"
"github.com/cenkalti/rain/internal/tracker"
"github.com/cenkalti/rain/internal/trackermanager"
"github.com/juju/ratelimit"
Expand All @@ -44,6 +43,7 @@ var (
type Session struct {
config Config
db *bbolt.DB
storage storage.Provider
resumer *boltdbresumer.Resumer
log logger.Logger
extensions [8]byte
Expand Down Expand Up @@ -204,6 +204,11 @@ func NewSession(cfg Config) (*Session, error) {
},
},
}
if cfg.CustomStorage != nil {
c.storage = cfg.CustomStorage
} else {
c.storage = newFileStorageProvider(&cfg)
}
dlSpeed := cfg.SpeedLimitDownload * 1024
if cfg.SpeedLimitDownload > 0 {
c.bucketDownload = ratelimit.NewBucketWithRate(float64(dlSpeed), dlSpeed)
Expand Down Expand Up @@ -447,14 +452,3 @@ func (s *Session) StopAll() error {
}
return nil
}

func (s *Session) newStorage(id string) (storage.Storage, error) {
return filestorage.New(s.getDataDir(id), s.config.FilePermissions)
}

func (s *Session) getDataDir(torrentID string) string {
if s.config.DataDirIncludesTorrentID {
return filepath.Join(s.config.DataDir, torrentID)
}
return s.config.DataDir
}
2 changes: 1 addition & 1 deletion torrent/session_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func (s *Session) add(opt *AddTorrentOptions) (id string, port int, sto storage.
}
id = base64.RawURLEncoding.EncodeToString(u1[:])
}
sto, err = s.newStorage(id)
sto, err = s.storage.GetStorage(id)
if err != nil {
return
}
Expand Down
2 changes: 1 addition & 1 deletion torrent/session_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (s *Session) loadExistingTorrent(id string) (tt *Torrent, hasStarted bool,
bf = bf3
}
}
sto, err := s.newStorage(id)
sto, err := s.storage.GetStorage(id)
if err != nil {
return
}
Expand Down
11 changes: 10 additions & 1 deletion torrent/session_rpc_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,15 @@ func (h *rpcHandler) MoveTorrent(args *rpctypes.MoveTorrentRequest, reply *rpcty
}

func (h *rpcHandler) handleMoveTorrent(w http.ResponseWriter, r *http.Request) {
var provider *fileStorageProvider
var ok bool
if provider, ok = h.session.storage.(*fileStorageProvider); !ok {
err := errors.New("session is not using file storage")
h.session.log.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

port, err := h.session.getPort()
if err != nil {
h.session.log.Error(err)
Expand Down Expand Up @@ -561,7 +570,7 @@ func (h *rpcHandler) handleMoveTorrent(w http.ResponseWriter, r *http.Request) {
http.Error(w, "data expected in multipart form", http.StatusBadRequest)
return
}
err = readData(p, h.session.getDataDir(id), h.session.config.FilePermissions)
err = readData(p, provider.getDataDir(id), h.session.config.FilePermissions)
if err != nil {
h.session.log.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down
34 changes: 34 additions & 0 deletions torrent/session_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package torrent

import (
"io/fs"
"path/filepath"

"github.com/cenkalti/rain/internal/storage"
"github.com/cenkalti/rain/internal/storage/filestorage"
)

type fileStorageProvider struct {
DataDir string
DataDirIncludesTorrentID bool
FilePermissions fs.FileMode
}

func newFileStorageProvider(cfg *Config) *fileStorageProvider {
return &fileStorageProvider{
DataDir: cfg.DataDir,
DataDirIncludesTorrentID: cfg.DataDirIncludesTorrentID,
FilePermissions: cfg.FilePermissions,
}
}

func (p *fileStorageProvider) GetStorage(torrentID string) (storage.Storage, error) {
return filestorage.New(p.getDataDir(torrentID), p.FilePermissions)
}

func (p *fileStorageProvider) getDataDir(torrentID string) string {
if p.DataDirIncludesTorrentID {
return filepath.Join(p.DataDir, torrentID)
}
return p.DataDir
}
10 changes: 9 additions & 1 deletion torrent/session_torrent.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"archive/tar"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
Expand Down Expand Up @@ -290,8 +291,15 @@ func (t *Torrent) generateTar(pw *io.PipeWriter) {
var err error
defer func() { _ = pw.CloseWithError(err) }()

var root string
if provider, ok := t.torrent.session.storage.(*fileStorageProvider); !ok {
err = errors.New("session is not using file storage")
return
} else {
root = provider.getDataDir(t.torrent.id)
}

tw := tar.NewWriter(pw)
root := t.torrent.session.getDataDir(t.torrent.id)
walkFunc := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
Expand Down

0 comments on commit 8bcf2d6

Please sign in to comment.