Skip to content

tsix404/torrentfs

Repository files navigation

torrentfs

A Rust library for parsing torrent files using libtorrent via FFI.

Features

  • Parse .torrent files and extract metadata
  • Extract torrent name, files, piece layout, and total size
  • Support for both single-file and multi-file torrents
  • Proper error handling for invalid or corrupted torrent files

Requirements

  • Rust 1.70 or later
  • libtorrent-rasterbar 2.0.x
  • OpenSSL
  • C++17 compiler
  • FUSE 3.x (libfuse3-dev)

FUSE Configuration

TorrentFS uses FUSE to mount the virtual filesystem. To allow non-root users to access the mount point, you need to configure FUSE:

  1. Edit /etc/fuse.conf:

    sudo sed -i 's/#user_allow_other/user_allow_other/' /etc/fuse.conf

    Or manually uncomment the user_allow_other line in /etc/fuse.conf:

    user_allow_other
    
  2. Ensure your user is in the fuse group:

    sudo usermod -aG fuse $USER

    (Log out and back in for group changes to take effect)

  3. On some systems, you may need to install FUSE development headers:

    # Ubuntu/Debian
    sudo apt-get install libfuse3-dev
    
    # Fedora
    sudo dnf install fuse3-devel
    
    # Arch Linux
    sudo pacman -S fuse3

FUSE Virtual Filesystem Structure

TorrentFS presents a layered virtual filesystem through FUSE.

Root Layout

/
├── metadata/       # Subdirectory for managing .torrent files (copy them here)
├── data/           # Subdirectory for browsing and reading torrent contents
└── .stats          # Virtual file with system statistics

How Multi-file Torrents Appear in /data

When a multi-file torrent (a torrent containing more than one file) is added, its top-level entry in /data/ is represented as a directory — not a regular file. This is the correct and expected FUSE behaviour for multi-file torrents, not a bug.

For example, listing a torrent entry with ls -la:

dr-xr-xr-x  2 user user    0 Jan  1  1970  ubuntu-22.04.iso.torrent

Observed behaviour for multi-file torrent entries:

  • File type: d (directory), not - (regular file)
  • Permissions: r-xr-xr-x (read-only directory)
  • Size: 0 (directories in FUSE report size 0)
  • Listing the directory: reveals the torrent's internal file/directory tree

Why? Multi-file torrents internally contain a directory tree of files. TorrentFS mirrors this structure faithfully in the FUSE filesystem — the torrent entry itself becomes a directory node, and its contents (subdirectories and files) appear as children within it.

Single-file vs Multi-file

Torrent Type Top-level Entry in /data Contents
Single-file Directory (contains one file) The single data file
Multi-file Directory Subdirectories + files from torrent

Both single-file and multi-file torrents always appear as directory entries. A single-file torrent's directory contains exactly one child (the torrent's data file); a multi-file torrent's directory contains the full file tree as defined in the .torrent metadata.

Inside a Torrent Directory

For a multi-file torrent like archlinux.torrent:

/data/archlinux.torrent/
├── arch/
│   ├── x86_64/
│   │   ├── core.db        (regular file, readable)
│   │   └── extra.db       (regular file, readable)
│   └── aarch64/
│       └── core.db        (regular file, readable)
└── README.md               (regular file, readable)

Each subdirectory and file within the torrent is accessible by reading through the FUSE mount — content is downloaded on demand as needed.

Source Path Organisation

Torrent files added under metadata/<source_path>/ are grouped in data/<source_path>/:

metadata/
└── os/
    └── linux/
        └── archlinux.torrent      # ← copy .torrent here
        
data/
└── os/
    └── linux/
        └── archlinux.torrent/     # ← appears as directory (multi-file) or directory with single file

Installation

Add this to your Cargo.toml:

[dependencies]
torrentfs = "0.1"

Usage

use torrentfs::{TorrentInfo, TorrentError};

fn main() -> Result<(), TorrentError> {
    let info = TorrentInfo::from_file("example.torrent")?;
    
    println!("Torrent: {}", info.name());
    println!("Total Size: {} bytes", info.total_size());
    println!("Piece Length: {} bytes", info.piece_length());
    println!("Number of Pieces: {}", info.num_pieces());
    println!("Number of Files: {}", info.num_files());
    
    let files = info.files()?;
    for file in files {
        println!("  {} ({} bytes)", file.path, file.size);
    }
    
    let hash = info.info_hash()?;
    print!("Info Hash: ");
    for byte in &hash {
        print!("{:02x}", byte);
    }
    println!();
    
    Ok(())
}

API

TorrentInfo

The main type for working with torrent metadata.

Methods

  • from_file(path) - Load a torrent file from disk
  • name() - Get the torrent name
  • total_size() - Get total size in bytes
  • piece_length() - Get piece length in bytes
  • num_pieces() - Get number of pieces
  • num_files() - Get number of files
  • files() - Get list of files with paths and sizes
  • info_hash() - Get the 20-byte SHA1 info hash
  • metadata() - Get all metadata in one struct

Error Handling

The library uses thiserror for error handling:

pub enum TorrentError {
    InvalidFile(String),
    ParseError(String),
    IoError(std::io::Error),
    NullPointer,
    Unknown { code: i32, message: String },
}

Building from Source

# Ensure libtorrent is installed
# On Ubuntu/Debian:
sudo apt-get install libtorrent-rasterbar-dev

# Build
cargo build

# Run tests
cargo test

# Run example
cargo run --example torrent_info -- /path/to/file.torrent

Docker

Pull from GitHub Container Registry

Pre-built images are available at ghcr.io/tsix404/torrentfs.

# Pull the latest stable release
docker pull ghcr.io/tsix404/torrentfs:latest

# Pull the development build (latest main branch)
docker pull ghcr.io/tsix404/torrentfs:dev

# Pull a specific version
docker pull ghcr.io/tsix404/torrentfs:v0.1.0

Image Tags

Tag Description
dev Latest push to main branch
latest Latest Git tag starting with v* (stable release)
vX.Y.Z Specific semver release
vX.Y Minor version track (e.g., v0.1)

Build Locally

docker build -t torrentfs .

Run with FUSE

TorrentFS requires FUSE kernel access inside the container. Use the following flags when running:

# Create a mount point on the host
mkdir -p /tmp/torrentfs

# Run with FUSE support
docker run --rm \
  --cap-add SYS_ADMIN \
  --device /dev/fuse \
  -v /tmp/torrentfs:/mnt:shared \
  ghcr.io/tsix404/torrentfs:latest

Required flags:

  • --cap-add SYS_ADMIN — grants the container capability to mount FUSE filesystems
  • --device /dev/fuse — passes the FUSE device into the container
  • -v /host/path:/mnt:shared — mounts a host directory for the virtual filesystem (use :shared for bidirectional access)

Note: On systems with AppArmor or SELinux, you may need additional security profiles. See your distribution's documentation for details.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages