transmission/libtransmission/inout.c

292 lines
7.8 KiB
C
Raw Normal View History

/*
2008-01-01 17:20:20 +00:00
* This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
2006-07-16 19:39:23 +00:00
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
2007-08-18 17:19:49 +00:00
*
* $Id$
*/
2006-07-16 19:39:23 +00:00
2007-11-09 20:07:52 +00:00
#include <assert.h>
2008-02-19 04:16:04 +00:00
#include <errno.h>
#include <stdlib.h> /* realloc */
2008-02-19 18:39:49 +00:00
#include <string.h> /* memcmp */
2007-07-12 17:51:45 +00:00
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <openssl/sha.h>
2006-07-16 19:39:23 +00:00
#include "transmission.h"
#include "crypto.h"
2007-07-09 20:10:42 +00:00
#include "fdlimit.h"
#include "inout.h"
#include "platform.h"
2007-11-26 20:37:07 +00:00
#include "stats.h"
#include "torrent.h"
#include "utils.h"
2006-07-16 19:39:23 +00:00
/****
***** Low-level IO functions
****/
2006-07-16 19:39:23 +00:00
2007-09-09 01:32:59 +00:00
#ifdef WIN32
#define lseek _lseeki64
#if defined(read)
#undef read
2008-10-14 20:31:16 +00:00
#endif
#define read _read
#if defined(write)
#undef write
2008-10-14 20:31:16 +00:00
#endif
#define write _write
2007-09-09 01:32:59 +00:00
#endif
enum { TR_IO_READ, TR_IO_WRITE };
2008-10-03 04:49:06 +00:00
/* returns 0 on success, or an errno on failure */
static int
readOrWriteBytes( const tr_torrent * tor,
int ioMode,
tr_file_index_t fileIndex,
uint64_t fileOffset,
void * buf,
size_t buflen )
{
const tr_info * info = &tor->info;
const tr_file * file = &info->files[fileIndex];
typedef size_t ( *iofunc )( int, void *, size_t );
iofunc func = ioMode ==
TR_IO_READ ? (iofunc)read : (iofunc)write;
char * path;
struct stat sb;
int fd = -1;
int err;
int fileExists;
2006-07-16 19:39:23 +00:00
assert( fileIndex < info->fileCount );
assert( !file->length || ( fileOffset < file->length ) );
assert( fileOffset + buflen <= file->length );
2006-07-16 19:39:23 +00:00
path = tr_buildPath( tor->downloadDir, file->name, NULL );
2007-11-26 20:37:07 +00:00
fileExists = !stat( path, &sb );
tr_free( path );
if( !file->length )
2008-10-03 04:49:06 +00:00
return 0;
if( ( ioMode == TR_IO_READ ) && !fileExists ) /* does file exist? */
2008-10-03 04:49:06 +00:00
err = errno;
else if( ( fd = tr_fdFileCheckout ( tor->downloadDir,
file->name,
ioMode == TR_IO_WRITE ) ) < 0 )
2008-10-03 04:49:06 +00:00
err = errno;
else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ( (off_t)-1 ) )
2008-10-03 04:49:06 +00:00
err = errno;
else if( func( fd, buf, buflen ) != buflen )
2008-10-03 04:49:06 +00:00
err = errno;
else
2008-10-03 04:49:06 +00:00
err = 0;
2007-11-26 20:37:07 +00:00
2008-10-03 04:49:06 +00:00
if( ( !err ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE ) )
tr_statsFileCreated( tor->session );
if( fd >= 0 )
tr_fdFileReturn( fd );
2006-07-16 19:39:23 +00:00
return err;
}
static int
compareOffsetToFile( const void * a,
const void * b )
{
const uint64_t offset = *(const uint64_t*)a;
const tr_file * file = b;
if( offset < file->offset ) return -1;
2008-04-20 22:06:05 +00:00
if( offset >= file->offset + file->length ) return 1;
return 0;
}
void
tr_ioFindFileLocation( const tr_torrent * tor,
tr_piece_index_t pieceIndex,
uint32_t pieceOffset,
tr_file_index_t * fileIndex,
uint64_t * fileOffset )
2006-07-16 19:39:23 +00:00
{
const uint64_t offset = tr_pieceOffset( tor, pieceIndex, pieceOffset,
0 );
const tr_file * file;
file = bsearch( &offset,
tor->info.files, tor->info.fileCount, sizeof( tr_file ),
compareOffsetToFile );
*fileIndex = file - tor->info.files;
*fileOffset = offset - file->offset;
assert( *fileIndex < tor->info.fileCount );
assert( *fileOffset < file->length );
assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
2006-07-16 19:39:23 +00:00
}
#ifdef WIN32
2008-10-03 04:49:06 +00:00
/* return 0 on success, or an errno on failure */
static int
ensureMinimumFileSize( const tr_torrent * tor,
tr_file_index_t fileIndex,
uint64_t minBytes )
2006-07-16 19:39:23 +00:00
{
int fd;
2008-10-03 04:49:06 +00:00
int err;
struct stat sb;
const tr_file * file = &tor->info.files[fileIndex];
assert( 0 <= fileIndex && fileIndex < tor->info.fileCount );
assert( minBytes <= file->length );
2008-05-18 16:44:30 +00:00
fd = tr_fdFileCheckout( tor->downloadDir, file->name, TRUE );
if( fd < 0 ) /* bad fd */
2008-10-03 04:49:06 +00:00
err = errno;
else if( fstat ( fd, &sb ) ) /* how big is the file? */
2008-10-03 04:49:06 +00:00
err = errno;
else if( sb.st_size >= (off_t)minBytes ) /* already big enough */
2008-10-03 04:49:06 +00:00
err = 0;
else if( !ftruncate( fd, minBytes ) ) /* grow it */
2008-10-03 04:49:06 +00:00
err = 0;
else /* couldn't grow it */
2008-10-03 04:49:06 +00:00
err = errno;
if( fd >= 0 )
tr_fdFileReturn( fd );
return err;
}
#endif
2007-05-27 23:32:26 +00:00
2008-10-03 04:49:06 +00:00
/* returns 0 on success, or an errno on failure */
static int
readOrWritePiece( const tr_torrent * tor,
int ioMode,
tr_piece_index_t pieceIndex,
uint32_t pieceOffset,
uint8_t * buf,
size_t buflen )
{
2008-10-03 04:49:06 +00:00
int err = 0;
tr_file_index_t fileIndex;
uint64_t fileOffset;
const tr_info * info = &tor->info;
2007-05-27 23:32:26 +00:00
if( pieceIndex >= tor->info.pieceCount )
2008-10-03 04:49:06 +00:00
return EINVAL;
if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
2008-10-03 04:49:06 +00:00
return EINVAL;
2007-05-27 23:32:26 +00:00
tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
&fileIndex, &fileOffset );
2006-07-16 19:39:23 +00:00
while( buflen && !err )
2006-07-16 19:39:23 +00:00
{
const tr_file * file = &info->files[fileIndex];
const uint64_t bytesThisPass = MIN( buflen,
file->length - fileOffset );
#ifdef WIN32
if( ioMode == TR_IO_WRITE )
err = ensureMinimumFileSize( tor, fileIndex,
fileOffset + bytesThisPass );
if( !err )
#endif
err = readOrWriteBytes( tor, ioMode,
fileIndex, fileOffset, buf, bytesThisPass );
buf += bytesThisPass;
buflen -= bytesThisPass;
++fileIndex;
fileOffset = 0;
2006-07-16 19:39:23 +00:00
}
return err;
2006-07-16 19:39:23 +00:00
}
2008-10-03 04:49:06 +00:00
int
tr_ioRead( const tr_torrent * tor,
tr_piece_index_t pieceIndex,
uint32_t begin,
uint32_t len,
uint8_t * buf )
2006-07-16 19:39:23 +00:00
{
return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
2006-07-16 19:39:23 +00:00
}
2008-10-03 04:49:06 +00:00
int
tr_ioWrite( const tr_torrent * tor,
tr_piece_index_t pieceIndex,
uint32_t begin,
uint32_t len,
const uint8_t * buf )
2006-07-16 19:39:23 +00:00
{
return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
(uint8_t*)buf,
len );
2006-07-16 19:39:23 +00:00
}
/****
*****
****/
2006-07-16 19:39:23 +00:00
static int
recalculateHash( const tr_torrent * tor,
tr_piece_index_t pieceIndex,
uint8_t * setme )
{
size_t bytesLeft;
size_t n;
uint32_t offset = 0;
int success = TRUE;
SHA_CTX sha;
2006-07-16 19:39:23 +00:00
2008-08-01 16:43:22 +00:00
assert( tor );
assert( setme );
assert( pieceIndex < tor->info.pieceCount );
2006-07-16 19:39:23 +00:00
SHA1_Init( &sha );
n = bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
2006-07-16 19:39:23 +00:00
while( bytesLeft )
{
uint8_t buf[8192];
const int len = MIN( bytesLeft, sizeof( buf ) );
success = !tr_ioRead( tor, pieceIndex, offset, len, buf );
if( !success )
break;
SHA1_Update( &sha, buf, len );
offset += len;
bytesLeft -= len;
}
if( success )
SHA1_Final( setme, &sha );
2006-07-16 19:39:23 +00:00
return success;
2006-07-16 19:39:23 +00:00
}
int
tr_ioTestPiece( const tr_torrent * tor,
int pieceIndex )
2006-07-16 19:39:23 +00:00
{
uint8_t hash[SHA_DIGEST_LENGTH];
const int recalculated = recalculateHash( tor, pieceIndex, hash );
return recalculated && !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
}