class QuartzTorrent::PieceIO

Can read and write pieces and blocks of a torrent.

Attributes

torrentDataLength[R]

Get the overall length of the torrent data

Public Class Methods

new(baseDirectory, torrinfo, ioManager = IOManager.new) click to toggle source

Create a new PieceIO that will map to files inside ‘baseDirectory’. Parameter ‘torrinfo’ should be a Metainfo::Info object (the info part of the metainfo).

# File lib/quartz_torrent/filemanager.rb, line 148
def initialize(baseDirectory, torrinfo, ioManager = IOManager.new)
  @baseDirectory = baseDirectory
  @torrinfo = torrinfo
  @pieceMapper = PieceMapper.new(baseDirectory, torrinfo)
  @ioManager = ioManager
  @logger = LogManager.getLogger("pieceio")
  @torrentDataLength = torrinfo.dataLength
end

Public Instance Methods

flush() click to toggle source
# File lib/quartz_torrent/filemanager.rb, line 214
def flush
  @ioManager.flush
end
readBlock(pieceIndex, offset, length) click to toggle source

Read a block from a completed piece. Returns nil if the block doesn’t exist yet. Throws exceptions on error (for example, opening a file failed)

# File lib/quartz_torrent/filemanager.rb, line 203
def readBlock(pieceIndex, offset, length)
  readRegions @pieceMapper.findBlock(pieceIndex, offset, length)
end
readPiece(pieceIndex) click to toggle source

Read a piece. Returns nil if the piece is not yet present. NOTE: this method expects that if the ioManager is a reactor iomanager, that the io was set with errorHandler=false so that we get the EOF errors.

# File lib/quartz_torrent/filemanager.rb, line 210
def readPiece(pieceIndex)
  readRegions @pieceMapper.findPiece(pieceIndex)
end
writeBlock(pieceIndex, offset, block) click to toggle source

Write a block to an in-progress piece. The block is written to piece ‘peiceIndex’, at offset ‘offset’. The block data is in block. Throws exceptions on failure.

# File lib/quartz_torrent/filemanager.rb, line 163
def writeBlock(pieceIndex, offset, block)
  regions = @pieceMapper.findBlock(pieceIndex, offset, block.length)
  indexInBlock = 0
  regions.each do |region|
    # Get the IO for the file with path 'path'. If we are being used in a reactor, this is the IO facade. If we
    # are not then this is a real IO.
    io = @ioManager.get(region.path)
    if ! io
      # No IO for this file.
      raise "This process doesn't have write permission for the file #{region.path}" if File.exists?(region.path) && ! File.writable?(region.path)

      # Ensure parent directories exist.
      dir = File.dirname region.path
      FileUtils.mkdir_p dir if ! File.directory?(dir)

      begin
        io = @ioManager.open(region.path)
      rescue
        @logger.error "Opening file #{region.path} failed: #{$!}"
        raise "Opening file #{region.path} failed"
      end
    end

    io.seek region.offset, IO::SEEK_SET
    begin
      io.write(block[indexInBlock, region.length])
      indexInBlock += region.length
    rescue
      # Error when writing...
      @logger.error "Writing block to file #{region.path} failed: #{$!}"
      piece = nil
      break
    end

    break if indexInBlock >= block.length
  end
end

Private Instance Methods

readRegions(regions) click to toggle source

Pass an ordered list of FileRegions to load.

# File lib/quartz_torrent/filemanager.rb, line 220
def readRegions(regions)
  piece = ""
  regions.each do |region|
    # Get the IO for the file with path 'path'. If we are being used in a reactor, this is the IO facade. If we
    # are not then this is a real IO.
    io = @ioManager.get(region.path)
    if ! io
      # No IO for this file.
      if ! File.exists?(region.path)
        # This file hasn't been created yet by having blocks written to it.
        piece = nil
        break
      end

      raise "This process doesn't have read permission for the file #{region.path}" if ! File.readable?(region.path)

      begin
        io = @ioManager.open(region.path)
      rescue
        @logger.error "Opening file #{region.path} failed: #{$!}"
        raise "Opening file #{region.path} failed"
      end
    end
    io.seek region.offset, IO::SEEK_SET
    begin
      piece << io.read(region.length)
    rescue
      # Error when reading. Likely EOF, meaning this peice isn't all there yet.
      piece = nil
      break
    end
  end
  piece
end