if the torrent's download path doesn't exist, don't create it -- it's might be a removeable disk that got unplugged.
This commit is contained in:
parent
dd75a769c9
commit
de515ee339
|
@ -127,19 +127,29 @@ static struct tr_fd_s * gFd = NULL;
|
|||
****
|
||||
***/
|
||||
|
||||
static int
|
||||
TrOpenFile( int i, const char * filename, int write )
|
||||
static tr_errno
|
||||
TrOpenFile( int i,
|
||||
const char * folder,
|
||||
const char * torrentFile,
|
||||
int write )
|
||||
{
|
||||
struct tr_openfile * file = &gFd->open[i];
|
||||
int flags;
|
||||
char filename[MAX_PATH_LENGTH];
|
||||
struct stat sb;
|
||||
|
||||
/* confirm the parent folder exists */
|
||||
if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
|
||||
return TR_ERROR_IO_PARENT;
|
||||
|
||||
/* create subfolders, if any */
|
||||
tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL );
|
||||
if( write ) {
|
||||
char * tmp = tr_strdup( filename );
|
||||
const int val = tr_mkdirp( dirname(tmp), 0777 );
|
||||
const int err = tr_mkdirp( dirname(tmp), 0777 ) ? errno : 0;
|
||||
tr_free( tmp );
|
||||
if( val )
|
||||
return tr_ioErrorFromErrno( );
|
||||
if( err )
|
||||
return tr_ioErrorFromErrno( err );
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
|
@ -150,19 +160,14 @@ TrOpenFile( int i, const char * filename, int write )
|
|||
#ifdef WIN32
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
errno = 0;
|
||||
file->fd = open( filename, flags, 0666 );
|
||||
if( file->fd < 0 ) {
|
||||
if( errno ) {
|
||||
tr_err( "Couldn't open '%s': %s", filename, strerror(errno) );
|
||||
return tr_ioErrorFromErrno();
|
||||
} else {
|
||||
tr_err( "Couldn't open '%s'", filename );
|
||||
return TR_ERROR_IO_OTHER;
|
||||
}
|
||||
if( file->fd == -1 ) {
|
||||
const int err = errno;
|
||||
tr_err( "Couldn't open '%s': %s", filename, strerror(err) );
|
||||
return tr_ioErrorFromErrno( err );
|
||||
}
|
||||
|
||||
return TR_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -192,14 +197,19 @@ fileIsCheckedOut( const struct tr_openfile * o )
|
|||
}
|
||||
|
||||
int
|
||||
tr_fdFileCheckout( const char * filename, int write )
|
||||
tr_fdFileCheckout( const char * folder,
|
||||
const char * torrentFile,
|
||||
int write )
|
||||
{
|
||||
int i, winner = -1;
|
||||
struct tr_openfile * o;
|
||||
char filename[MAX_PATH_LENGTH];
|
||||
|
||||
assert( filename && *filename );
|
||||
assert( folder && *folder );
|
||||
assert( torrentFile && *torrentFile );
|
||||
assert( write==0 || write==1 );
|
||||
|
||||
tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL );
|
||||
dbgmsg( "looking for file '%s', writable %c", filename, write?'y':'n' );
|
||||
|
||||
tr_lockLock( gFd->lock );
|
||||
|
@ -274,10 +284,10 @@ tr_fdFileCheckout( const char * filename, int write )
|
|||
o = &gFd->open[winner];
|
||||
if( !fileIsOpen( o ) )
|
||||
{
|
||||
const int ret = TrOpenFile( winner, filename, write );
|
||||
if( ret ) {
|
||||
const tr_errno err = TrOpenFile( winner, folder, torrentFile, write );
|
||||
if( err ) {
|
||||
tr_lockUnlock( gFd->lock );
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
dbgmsg( "opened '%s' in slot %d, write %c", filename, winner, write?'y':'n' );
|
||||
|
|
|
@ -36,19 +36,26 @@ void tr_fdClose( void );
|
|||
/**
|
||||
* Returns an fd to the specified filename.
|
||||
*
|
||||
* A small repository of open files is kept to avoid the overhead of continually
|
||||
* opening and closing the same files when writing piece data during download.
|
||||
* It's also used to ensure that only one client uses the file at a time.
|
||||
* Clients must check out a file to use it, then return it, like a library, when done.
|
||||
* A small pool of open files is kept to avoid the overhead of
|
||||
* continually opening and closing the same files when downloading
|
||||
* piece data. It's also used to ensure only one caller can
|
||||
* write to the file at a time. Callers check out a file, use it,
|
||||
* and then check it back in via tr_fdFileReturn() when done.
|
||||
*
|
||||
* if write is nonzero and dirname(filename) doesn't exist, dirname is created.
|
||||
* if write is nonzero and filename doesn't exist, filename is created.
|
||||
* returns the fd if successful; otherwise, one of TR_ERROR_IO_*
|
||||
* - if `folder' doesn't exist, TR_ERROR_IO_PARENT is returned.
|
||||
* - if doWrite is true, subfolders in torrentFile are created if necessary.
|
||||
* - if doWrite is true, the target file is created if necessary.
|
||||
*
|
||||
* on success, a file descriptor >= 0 is returned.
|
||||
* on failure, a negative number corresponding to tr_errno is returned.
|
||||
*
|
||||
* @see tr_fdFileReturn
|
||||
* @see tr_fdFileClose
|
||||
* @see tr_errno
|
||||
*/
|
||||
int tr_fdFileCheckout( const char * filename, int write );
|
||||
int tr_fdFileCheckout( const char * folder,
|
||||
const char * torrentFile,
|
||||
int doWrite );
|
||||
|
||||
/**
|
||||
* Returns an fd from tr_fdFileCheckout() so that other clients may borrow it.
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
enum { TR_IO_READ, TR_IO_WRITE };
|
||||
|
||||
static int
|
||||
static tr_errno
|
||||
readOrWriteBytes( const tr_torrent * tor,
|
||||
int ioMode,
|
||||
int fileIndex,
|
||||
|
@ -56,7 +56,7 @@ readOrWriteBytes( const tr_torrent * tor,
|
|||
char path[MAX_PATH_LENGTH];
|
||||
struct stat sb;
|
||||
int fd = -1;
|
||||
int ret;
|
||||
int err;
|
||||
int fileExists;
|
||||
|
||||
assert( 0<=fileIndex && fileIndex<info->fileCount );
|
||||
|
@ -68,27 +68,28 @@ readOrWriteBytes( const tr_torrent * tor,
|
|||
|
||||
if( !file->length )
|
||||
return 0;
|
||||
else if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */
|
||||
ret = tr_ioErrorFromErrno ();
|
||||
else if ((fd = tr_fdFileCheckout ( path, ioMode==TR_IO_WRITE )) < 0)
|
||||
ret = fd;
|
||||
else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
|
||||
ret = TR_ERROR_IO_OTHER;
|
||||
else if( func( fd, buf, buflen ) != buflen )
|
||||
ret = tr_ioErrorFromErrno( );
|
||||
else
|
||||
ret = TR_OK;
|
||||
|
||||
if((ret==TR_OK) && (ioMode==TR_IO_WRITE) && !fileExists )
|
||||
if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */
|
||||
err = tr_ioErrorFromErrno( errno );
|
||||
else if ((fd = tr_fdFileCheckout ( tor->destination, file->name, ioMode==TR_IO_WRITE )) < 0)
|
||||
err = fd;
|
||||
else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
|
||||
err = tr_ioErrorFromErrno( errno );
|
||||
else if( func( fd, buf, buflen ) != buflen )
|
||||
err = tr_ioErrorFromErrno( errno );
|
||||
else
|
||||
err = 0;
|
||||
|
||||
if( !err && !fileExists && (ioMode==TR_IO_WRITE) )
|
||||
tr_statsFileCreated( tor->handle );
|
||||
|
||||
if( fd >= 0 )
|
||||
tr_fdFileReturn( fd );
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
static tr_errno
|
||||
findFileLocation( const tr_torrent * tor,
|
||||
int pieceIndex,
|
||||
int pieceOffset,
|
||||
|
@ -100,10 +101,12 @@ findFileLocation( const tr_torrent * tor,
|
|||
int i;
|
||||
uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
|
||||
|
||||
assert( 0<=pieceIndex && pieceIndex < info->pieceCount );
|
||||
assert( 0<=tor->info.pieceSize );
|
||||
assert( pieceOffset < tr_torPieceCountBytes( tor, pieceIndex ) );
|
||||
assert( piecePos < info->totalSize );
|
||||
if( pieceIndex < 0 || pieceIndex >= info->pieceCount )
|
||||
return TR_ERROR_ASSERT;
|
||||
if( pieceOffset >= tr_torPieceCountBytes( tor, pieceIndex ) )
|
||||
return TR_ERROR_ASSERT;
|
||||
if( piecePos >= info->totalSize )
|
||||
return TR_ERROR_ASSERT;
|
||||
|
||||
for( i=0; info->files[i].length<=piecePos; ++i )
|
||||
piecePos -= info->files[i].length;
|
||||
|
@ -113,45 +116,42 @@ findFileLocation( const tr_torrent * tor,
|
|||
|
||||
assert( 0<=*fileIndex && *fileIndex<info->fileCount );
|
||||
assert( *fileOffset < info->files[i].length );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
static int
|
||||
static tr_errno
|
||||
ensureMinimumFileSize( const tr_torrent * tor,
|
||||
int fileIndex,
|
||||
uint64_t minBytes )
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
tr_errno err;
|
||||
struct stat sb;
|
||||
const tr_file * file = &tor->info.files[fileIndex];
|
||||
char path[MAX_PATH_LENGTH];
|
||||
|
||||
assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
|
||||
assert( minBytes <= file->length );
|
||||
|
||||
tr_buildPath( path, sizeof(path), tor->destination, file->name, NULL );
|
||||
|
||||
fd = tr_fdFileCheckout( path, TRUE );
|
||||
if( fd < 0 ) /* bad fd */
|
||||
ret = fd;
|
||||
if(( fd = tr_fdFileCheckout( tor->destination, file->name, TRUE )) < 0 )
|
||||
err = fd;
|
||||
else if (fstat (fd, &sb) ) /* how big is the file? */
|
||||
ret = tr_ioErrorFromErrno ();
|
||||
err = tr_ioErrorFromErrno( errno );
|
||||
else if (sb.st_size >= (off_t)minBytes) /* already big enough */
|
||||
ret = TR_OK;
|
||||
else if (!ftruncate( fd, minBytes )) /* grow it */
|
||||
ret = TR_OK;
|
||||
err = 0;
|
||||
else if ( !ftruncate( fd, minBytes ) ) /* grow it */
|
||||
err = 0;
|
||||
else /* couldn't grow it */
|
||||
ret = tr_ioErrorFromErrno ();
|
||||
err = tr_ioErrorFromErrno( errno );
|
||||
|
||||
if( fd >= 0 )
|
||||
tr_fdFileReturn( fd );
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
static tr_errno
|
||||
readOrWritePiece( tr_torrent * tor,
|
||||
int ioMode,
|
||||
int pieceIndex,
|
||||
|
@ -159,28 +159,31 @@ readOrWritePiece( tr_torrent * tor,
|
|||
uint8_t * buf,
|
||||
size_t buflen )
|
||||
{
|
||||
int ret = 0;
|
||||
tr_errno err = 0;
|
||||
int fileIndex;
|
||||
uint64_t fileOffset;
|
||||
const tr_info * info = &tor->info;
|
||||
|
||||
assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
|
||||
assert( buflen <= (size_t) tr_torPieceCountBytes( tor, pieceIndex ) );
|
||||
if( pieceIndex < 0 || pieceIndex >= tor->info.pieceCount )
|
||||
err = TR_ERROR_ASSERT;
|
||||
else if( buflen > ( size_t ) tr_torPieceCountBytes( tor, pieceIndex ) )
|
||||
err = TR_ERROR_ASSERT;
|
||||
|
||||
findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
|
||||
if( !err )
|
||||
err = findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
|
||||
|
||||
while( buflen && !ret )
|
||||
while( buflen && !err )
|
||||
{
|
||||
const tr_file * file = &info->files[fileIndex];
|
||||
const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
|
||||
|
||||
#ifdef WIN32
|
||||
if( ioMode == TR_IO_WRITE )
|
||||
ret = ensureMinimumFileSize( tor, fileIndex,
|
||||
err = ensureMinimumFileSize( tor, fileIndex,
|
||||
fileOffset + bytesThisPass );
|
||||
if( !ret )
|
||||
if( !err )
|
||||
#endif
|
||||
ret = readOrWriteBytes( tor, ioMode,
|
||||
err = readOrWriteBytes( tor, ioMode,
|
||||
fileIndex, fileOffset, buf, bytesThisPass );
|
||||
buf += bytesThisPass;
|
||||
buflen -= bytesThisPass;
|
||||
|
@ -188,10 +191,10 @@ readOrWritePiece( tr_torrent * tor,
|
|||
fileOffset = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tr_errno
|
||||
tr_ioRead( tr_torrent * tor,
|
||||
int pieceIndex,
|
||||
int begin,
|
||||
|
@ -201,7 +204,7 @@ tr_ioRead( tr_torrent * tor,
|
|||
return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
|
||||
}
|
||||
|
||||
int
|
||||
tr_errno
|
||||
tr_ioWrite( tr_torrent * tor,
|
||||
int pieceIndex,
|
||||
int begin,
|
||||
|
|
|
@ -27,18 +27,19 @@
|
|||
|
||||
struct tr_torrent;
|
||||
|
||||
typedef struct tr_io tr_io;
|
||||
|
||||
/***********************************************************************
|
||||
* tr_ioRead, tr_ioWrite
|
||||
***********************************************************************
|
||||
* Reads or writes the block specified by the piece index, the offset in
|
||||
* that piece and the size of the block. Returns 0 if successful,
|
||||
* TR_ERROR_ASSERT if the parameters are incorrect, one of the
|
||||
* TR_ERROR_IO_* otherwise.
|
||||
**********************************************************************/
|
||||
/**
|
||||
* Reads the block specified by the piece index, offset, and length.
|
||||
* @return 0 on success, TR_ERROR_ASSERT if the arguments are incorrect,
|
||||
* or TR_ERROR_IO_* otherwise.
|
||||
*/
|
||||
int tr_ioRead ( struct tr_torrent*, int index, int begin, int len, uint8_t * );
|
||||
int tr_ioWrite ( struct tr_torrent *, int index, int begin, int len, const uint8_t * );
|
||||
|
||||
/**
|
||||
* Writes the block specified by the piece index, offset, and length.
|
||||
* @return 0 on success, TR_ERROR_ASSERT if the arguments are incorrect,
|
||||
* or TR_ERROR_IO_* otherwise.
|
||||
*/
|
||||
tr_errno tr_ioWrite ( struct tr_torrent *, int index, int begin, int len, const uint8_t * );
|
||||
|
||||
/* hashes the specified piece and updates the completion accordingly. */
|
||||
int tr_ioHash ( tr_torrent*, int piece );
|
||||
|
|
|
@ -712,7 +712,7 @@ ipc_addstat( benc_val_t * list, int tor,
|
|||
{
|
||||
benc_val_t * dict, * item;
|
||||
int ii, used;
|
||||
unsigned int error;
|
||||
tr_errno error;
|
||||
|
||||
/* always send torrent id */
|
||||
types |= IPC_ST_ID;
|
||||
|
@ -763,38 +763,42 @@ ipc_addstat( benc_val_t * list, int tor,
|
|||
{
|
||||
tr_bencInitStr( item, "", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_ASSERT, error ) )
|
||||
else if( error == TR_ERROR_ASSERT )
|
||||
{
|
||||
tr_bencInitStr( item, "assert", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_IO_PERMISSIONS, error ) )
|
||||
else if( error == TR_ERROR_IO_PERMISSIONS )
|
||||
{
|
||||
tr_bencInitStr( item, "io-permissions", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_IO_SPACE, error ) )
|
||||
else if( error == TR_ERROR_IO_SPACE )
|
||||
{
|
||||
tr_bencInitStr( item, "io-space", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_IO_FILE_TOO_BIG, error ) )
|
||||
else if( error == TR_ERROR_IO_FILE_TOO_BIG )
|
||||
{
|
||||
tr_bencInitStr( item, "io-file-too-big", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_IO_OPEN_FILES, error ) )
|
||||
else if( error == TR_ERROR_IO_OPEN_FILES )
|
||||
{
|
||||
tr_bencInitStr( item, "io-open-files", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_IO_MASK, error ) )
|
||||
else if( TR_ERROR_IS_IO( error ) )
|
||||
{
|
||||
tr_bencInitStr( item, "io-other", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_TC_ERROR, error ) )
|
||||
else if( error == TR_ERROR_TC_ERROR )
|
||||
{
|
||||
tr_bencInitStr( item, "tracker-error", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_ISSET( TR_ERROR_TC_WARNING, error ) )
|
||||
else if( error == TR_ERROR_TC_WARNING )
|
||||
{
|
||||
tr_bencInitStr( item, "tracker-warning", -1, 1 );
|
||||
}
|
||||
else if( TR_ERROR_IS_TC( error ) )
|
||||
{
|
||||
tr_bencInitStr( item, "tracker-other", -1, 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_bencInitStr( item, "other", -1, 1 );
|
||||
|
|
|
@ -919,12 +919,14 @@ msgsCallbackFunc( void * vpeer, void * vevent, void * vt )
|
|||
broadcastGotBlock( t, e->pieceIndex, e->offset, e->length );
|
||||
break;
|
||||
|
||||
case TR_PEERMSG_GOT_ASSERT_ERROR:
|
||||
addStrike( t, peer );
|
||||
peer->doPurge = 1;
|
||||
break;
|
||||
|
||||
case TR_PEERMSG_GOT_ERROR:
|
||||
case TR_PEERMSG_ERROR:
|
||||
if( TR_ERROR_IS_IO( e->err ) ) {
|
||||
t->tor->error = e->err;
|
||||
strlcpy( t->tor->errorString, tr_errorString( e->err ), sizeof(t->tor->errorString) );
|
||||
tr_torrentStop( t->tor );
|
||||
} else if( e->err == TR_ERROR_ASSERT ) {
|
||||
addStrike( t, peer );
|
||||
}
|
||||
peer->doPurge = 1;
|
||||
break;
|
||||
|
||||
|
|
|
@ -262,7 +262,7 @@ protocolSendChoke( tr_peermsgs * msgs, int choke )
|
|||
*** EVENTS
|
||||
**/
|
||||
|
||||
static const tr_peermsgs_event blankEvent = { 0, 0, 0, 0, 0.0f };
|
||||
static const tr_peermsgs_event blankEvent = { 0, 0, 0, 0, 0.0f, 0 };
|
||||
|
||||
static void
|
||||
publish( tr_peermsgs * msgs, tr_peermsgs_event * e )
|
||||
|
@ -271,18 +271,11 @@ publish( tr_peermsgs * msgs, tr_peermsgs_event * e )
|
|||
}
|
||||
|
||||
static void
|
||||
fireGotAssertError( tr_peermsgs * msgs )
|
||||
fireError( tr_peermsgs * msgs, tr_errno err )
|
||||
{
|
||||
tr_peermsgs_event e = blankEvent;
|
||||
e.eventType = TR_PEERMSG_GOT_ASSERT_ERROR;
|
||||
publish( msgs, &e );
|
||||
}
|
||||
|
||||
static void
|
||||
fireGotError( tr_peermsgs * msgs )
|
||||
{
|
||||
tr_peermsgs_event e = blankEvent;
|
||||
e.eventType = TR_PEERMSG_GOT_ERROR;
|
||||
e.eventType = TR_PEERMSG_ERROR;
|
||||
e.err = err;
|
||||
publish( msgs, &e );
|
||||
}
|
||||
|
||||
|
@ -592,29 +585,29 @@ expireOldRequests( tr_peermsgs * msgs )
|
|||
tr_list * prune = NULL;
|
||||
const time_t now = time( NULL );
|
||||
|
||||
// find queued requests that are too old
|
||||
// "time_requested" here is when the request was queued
|
||||
/* find queued requests that are too old
|
||||
"time_requested" here is when the request was queued */
|
||||
for( l=msgs->clientWillAskFor; l!=NULL; l=l->next ) {
|
||||
struct peer_request * req = l->data;
|
||||
if( req->time_requested + REQUEST_TTL_SECS < now )
|
||||
tr_list_prepend( &prune, req );
|
||||
}
|
||||
|
||||
// find sent requests that are too old
|
||||
// "time_requested" here is when the request was sent
|
||||
/* find sent requests that are too old
|
||||
"time_requested" here is when the request was sent */
|
||||
for( l=msgs->clientAskedFor; l!=NULL; l=l->next ) {
|
||||
struct peer_request * req = l->data;
|
||||
if( req->time_requested + REQUEST_TTL_SECS < now )
|
||||
tr_list_prepend( &prune, req );
|
||||
}
|
||||
|
||||
// expire the old requests
|
||||
/* expire the old requests */
|
||||
for( l=prune; l!=NULL; l=l->next ) {
|
||||
struct peer_request * req = l->data;
|
||||
tr_peerMsgsCancel( msgs, req->index, req->offset, req->length );
|
||||
}
|
||||
|
||||
// cleanup
|
||||
/* cleanup */
|
||||
tr_list_free( &prune, NULL );
|
||||
}
|
||||
|
||||
|
@ -1150,7 +1143,7 @@ readBtPiece( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
|
|||
if( !err )
|
||||
return READ_AGAIN;
|
||||
else {
|
||||
fireGotAssertError( msgs );
|
||||
fireError( msgs, err );
|
||||
return READ_DONE;
|
||||
}
|
||||
}
|
||||
|
@ -1164,7 +1157,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
|
|||
const uint8_t id = msgs->incoming.id;
|
||||
const size_t startBufLen = EVBUFFER_LENGTH( inbuf );
|
||||
|
||||
--msglen; // id length
|
||||
--msglen; /* id length */
|
||||
|
||||
if( inlen < msglen )
|
||||
return READ_MORE;
|
||||
|
@ -1174,7 +1167,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
|
|||
if( !messageLengthIsCorrect( msgs, id, msglen+1 ) )
|
||||
{
|
||||
dbgmsg( msgs, "bad packet - BT message #%d with a length of %d", (int)id, (int)msglen );
|
||||
fireGotError( msgs );
|
||||
fireError( msgs, TR_ERROR );
|
||||
return READ_DONE;
|
||||
}
|
||||
|
||||
|
@ -1386,12 +1379,12 @@ addPeerToBlamefield( tr_peermsgs * msgs, uint32_t index )
|
|||
tr_bitfieldAdd( msgs->info->blame, index );
|
||||
}
|
||||
|
||||
static int
|
||||
static tr_errno
|
||||
clientGotBlock( tr_peermsgs * msgs,
|
||||
const uint8_t * data,
|
||||
const struct peer_request * req )
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
tr_torrent * tor = msgs->torrent;
|
||||
const int block = _tr_block( tor, req->index, req->offset );
|
||||
struct peer_request *myreq;
|
||||
|
@ -1403,7 +1396,7 @@ clientGotBlock( tr_peermsgs * msgs,
|
|||
{
|
||||
dbgmsg( msgs, "wrong block size -- expected %u, got %d",
|
||||
tr_torBlockCountBytes( msgs->torrent, block ), req->length );
|
||||
return TR_ERROR_ASSERT;
|
||||
return TR_ERROR;
|
||||
}
|
||||
|
||||
/* save the block */
|
||||
|
@ -1444,9 +1437,8 @@ clientGotBlock( tr_peermsgs * msgs,
|
|||
**/
|
||||
|
||||
msgs->info->peerSentPieceDataAt = time( NULL );
|
||||
i = tr_ioWrite( tor, req->index, req->offset, req->length, data );
|
||||
if( i )
|
||||
return 0;
|
||||
if(( err = tr_ioWrite( tor, req->index, req->offset, req->length, data )))
|
||||
return err;
|
||||
|
||||
tr_cpBlockAdd( tor->completion, block );
|
||||
|
||||
|
@ -1656,7 +1648,7 @@ gotError( struct bufferevent * evbuf UNUSED, short what, void * vmsgs )
|
|||
if( what & ( EVBUFFER_EOF | EVBUFFER_ERROR ) ) {
|
||||
dbgmsg( vmsgs, "libevent got an error! what=%hd, errno=%d (%s)",
|
||||
what, errno, strerror(errno) );
|
||||
fireGotError( vmsgs );
|
||||
fireError( vmsgs, TR_ERROR );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,8 +65,7 @@ typedef enum
|
|||
TR_PEERMSG_CLIENT_BLOCK,
|
||||
TR_PEERMSG_PIECE_DATA,
|
||||
TR_PEERMSG_PEER_PROGRESS,
|
||||
TR_PEERMSG_GOT_ERROR,
|
||||
TR_PEERMSG_GOT_ASSERT_ERROR,
|
||||
TR_PEERMSG_ERROR,
|
||||
TR_PEERMSG_CANCEL,
|
||||
TR_PEERMSG_NEED_REQ
|
||||
}
|
||||
|
@ -79,6 +78,7 @@ typedef struct
|
|||
uint32_t offset; /* for TR_PEERMSG_GOT_BLOCK */
|
||||
uint32_t length; /* for TR_PEERMSG_GOT_BLOCK */
|
||||
float progress; /* for TR_PEERMSG_PEER_PROGRESS */
|
||||
tr_errno err; /* for TR_PEERMSG_GOT_ERROR */
|
||||
}
|
||||
tr_peermsgs_event;
|
||||
|
||||
|
|
|
@ -880,13 +880,35 @@ freeTorrent( tr_torrent * tor )
|
|||
tr_globalUnlock( h );
|
||||
}
|
||||
|
||||
enum
|
||||
/**
|
||||
*** Start/Stop Callback
|
||||
**/
|
||||
|
||||
static void
|
||||
fireActiveChange( tr_torrent * tor, int isRunning )
|
||||
{
|
||||
AFTER_RECHECK_NONE,
|
||||
AFTER_RECHECK_START,
|
||||
AFTER_RECHECK_STOP,
|
||||
AFTER_RECHECK_CLOSE
|
||||
};
|
||||
assert( tor != NULL );
|
||||
|
||||
if( tor->active_func != NULL )
|
||||
(tor->active_func)( tor, isRunning, tor->active_func_user_data );
|
||||
}
|
||||
|
||||
void
|
||||
tr_torrentSetActiveCallback( tr_torrent * tor,
|
||||
tr_torrent_active_func func,
|
||||
void * user_data )
|
||||
{
|
||||
assert( tor != NULL );
|
||||
tor->active_func = func;
|
||||
tor->active_func_user_data = user_data;
|
||||
}
|
||||
|
||||
void
|
||||
tr_torrentClearActiveCallback( tr_torrent * torrent )
|
||||
{
|
||||
tr_torrentSetActiveCallback( torrent, NULL, NULL );
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
checkAndStartImpl( void * vtor )
|
||||
|
@ -896,6 +918,7 @@ checkAndStartImpl( void * vtor )
|
|||
tr_globalLock( tor->handle );
|
||||
|
||||
tor->isRunning = 1;
|
||||
fireActiveChange( tor, tor->isRunning );
|
||||
*tor->errorString = '\0';
|
||||
tr_torrentResetTransferStats( tor );
|
||||
tor->cpStatus = tr_cpGetStatus( tor->completion );
|
||||
|
@ -959,6 +982,7 @@ stopTorrent( void * vtor )
|
|||
tr_ioRecheckRemove( tor );
|
||||
tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
|
||||
tr_trackerStop( tor->tracker );
|
||||
fireActiveChange( tor, 0 );
|
||||
|
||||
for( i=0; i<tor->info.fileCount; ++i )
|
||||
{
|
||||
|
|
|
@ -147,6 +147,9 @@ struct tr_torrent
|
|||
tr_torrent_status_func * status_func;
|
||||
void * status_func_user_data;
|
||||
|
||||
tr_torrent_active_func * active_func;
|
||||
void * active_func_user_data;
|
||||
|
||||
unsigned int statCur : 1;
|
||||
unsigned int isRunning : 1;
|
||||
|
||||
|
|
|
@ -67,28 +67,6 @@ enum
|
|||
TR_PEER_FROM__MAX
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* Error codes
|
||||
**********************************************************************/
|
||||
/* General errors */
|
||||
#define TR_OK 0x00000000
|
||||
#define TR_ERROR 0x81000000
|
||||
#define TR_ERROR_ASSERT 0x82000000
|
||||
/* I/O errors */
|
||||
#define TR_ERROR_IO_MASK 0x000000FF
|
||||
#define TR_ERROR_IO_PERMISSIONS 0x80000002
|
||||
#define TR_ERROR_IO_SPACE 0x80000004
|
||||
#define TR_ERROR_IO_FILE_TOO_BIG 0x80000008
|
||||
#define TR_ERROR_IO_OPEN_FILES 0x80000010
|
||||
#define TR_ERROR_IO_DUP_DOWNLOAD 0x80000020
|
||||
#define TR_ERROR_IO_OTHER 0x80000040
|
||||
/* Misc */
|
||||
#define TR_ERROR_TC_MASK 0x00000F00
|
||||
#define TR_ERROR_TC_ERROR 0x80000100
|
||||
#define TR_ERROR_TC_WARNING 0x80000200
|
||||
|
||||
#define TR_ERROR_ISSET( num, code ) ( (code) == ( (code) & (num) ) )
|
||||
|
||||
/***********************************************************************
|
||||
* tr_init
|
||||
***********************************************************************
|
||||
|
@ -496,7 +474,7 @@ void tr_torrentStop( tr_torrent * );
|
|||
|
||||
|
||||
/**
|
||||
*** Register to be notified whenever a torrent's state changes.
|
||||
***
|
||||
**/
|
||||
|
||||
typedef enum
|
||||
|
@ -511,12 +489,45 @@ typedef void (tr_torrent_status_func)(tr_torrent * torrent,
|
|||
cp_status_t status,
|
||||
void * user_data );
|
||||
|
||||
/**
|
||||
* Register to be notified whenever a torrent's state changes.
|
||||
*
|
||||
* func is invoked FROM LIBTRANSMISSION'S THREAD!
|
||||
* This means func must be fast (to avoid blocking peers),
|
||||
* shouldn't call libtransmission functions (to avoid deadlock),
|
||||
* and shouldn't modify client-level memory without using a mutex!
|
||||
*/
|
||||
void tr_torrentSetStatusCallback( tr_torrent * torrent,
|
||||
tr_torrent_status_func func,
|
||||
void * user_data );
|
||||
|
||||
void tr_torrentClearStatusCallback( tr_torrent * torrent );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
typedef void (tr_torrent_active_func)(tr_torrent * torrent,
|
||||
int isRunning,
|
||||
void * user_data );
|
||||
|
||||
/**
|
||||
* Register to be notified whenever a torrent starts or stops.
|
||||
*
|
||||
* func is invoked FROM LIBTRANSMISSION'S THREAD!
|
||||
* This means func must be fast (to avoid blocking peers),
|
||||
* shouldn't call libtransmission functions (to avoid deadlock),
|
||||
* and shouldn't modify client-level memory without using a mutex!
|
||||
*/
|
||||
void tr_torrentSetActiveCallback( tr_torrent * torrent,
|
||||
tr_torrent_active_func func,
|
||||
void * user_data );
|
||||
|
||||
void tr_torrentClearActiveCallback( tr_torrent * torrent );
|
||||
|
||||
|
||||
/**
|
||||
* MANUAL ANNOUNCE
|
||||
*
|
||||
|
@ -675,14 +686,41 @@ tr_torrent_status;
|
|||
|
||||
#define TR_STATUS_IS_ACTIVE(s) ((s) != TR_STATUS_STOPPED)
|
||||
|
||||
/***********************************************************************
|
||||
* tr_stat
|
||||
**********************************************************************/
|
||||
/**
|
||||
* Transmission error codes
|
||||
* errors are always negative, and 0 refers to no error.
|
||||
*/
|
||||
typedef enum tr_errno
|
||||
{
|
||||
TR_OK = 0,
|
||||
|
||||
/* general errors */
|
||||
TR_ERROR = -100,
|
||||
TR_ERROR_ASSERT,
|
||||
|
||||
/* io errors */
|
||||
TR_ERROR_IO_PARENT = -200,
|
||||
TR_ERROR_IO_PERMISSIONS,
|
||||
TR_ERROR_IO_SPACE,
|
||||
TR_ERROR_IO_FILE_TOO_BIG,
|
||||
TR_ERROR_IO_OPEN_FILES,
|
||||
TR_ERROR_IO_DUP_DOWNLOAD,
|
||||
TR_ERROR_IO_OTHER,
|
||||
|
||||
/* tracker errors */
|
||||
TR_ERROR_TC_ERROR = -300,
|
||||
TR_ERROR_TC_WARNING
|
||||
}
|
||||
tr_errno;
|
||||
|
||||
#define TR_ERROR_IS_IO(e) (TR_ERROR_IO_PARENT<=(e) && (e)<=TR_ERROR_IO_OTHER)
|
||||
#define TR_ERROR_IS_TC(e) (TR_ERROR_TC_ERROR<=(e) && (e)<=TR_ERROR_TC_WARNING)
|
||||
|
||||
struct tr_stat
|
||||
{
|
||||
tr_torrent_status status;
|
||||
|
||||
int error;
|
||||
tr_errno error;
|
||||
char errorString[128];
|
||||
|
||||
const tr_tracker_info * tracker;
|
||||
|
|
|
@ -489,10 +489,12 @@ tr_buildPath ( char *buf, size_t buflen, const char *first_element, ... )
|
|||
}
|
||||
|
||||
int
|
||||
tr_ioErrorFromErrno( void )
|
||||
tr_ioErrorFromErrno( int err )
|
||||
{
|
||||
switch( errno )
|
||||
switch( err )
|
||||
{
|
||||
case 0:
|
||||
return TR_OK;
|
||||
case EACCES:
|
||||
case EROFS:
|
||||
return TR_ERROR_IO_PERMISSIONS;
|
||||
|
@ -515,24 +517,30 @@ tr_errorString( int code )
|
|||
{
|
||||
case TR_OK:
|
||||
return "No error";
|
||||
|
||||
case TR_ERROR:
|
||||
return "Generic error";
|
||||
case TR_ERROR_ASSERT:
|
||||
return "Assert error";
|
||||
|
||||
case TR_ERROR_IO_PARENT:
|
||||
return "Download folder does not exist";
|
||||
case TR_ERROR_IO_PERMISSIONS:
|
||||
return "Insufficient permissions";
|
||||
case TR_ERROR_IO_SPACE:
|
||||
return "Insufficient free space";
|
||||
case TR_ERROR_IO_DUP_DOWNLOAD:
|
||||
return "Already active transfer with same name and download folder";
|
||||
case TR_ERROR_IO_FILE_TOO_BIG:
|
||||
return "File too large";
|
||||
case TR_ERROR_IO_OPEN_FILES:
|
||||
return "Too many open files";
|
||||
case TR_ERROR_IO_DUP_DOWNLOAD:
|
||||
return "Already active transfer with same name and download folder";
|
||||
case TR_ERROR_IO_OTHER:
|
||||
return "Generic I/O error";
|
||||
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
/****
|
||||
|
|
|
@ -76,7 +76,7 @@ void tr_buildPath( char* buf, size_t buflen,
|
|||
struct timeval timevalMsec( uint64_t milliseconds );
|
||||
|
||||
|
||||
int tr_ioErrorFromErrno( void );
|
||||
int tr_ioErrorFromErrno( int err );
|
||||
|
||||
const char * tr_errorString( int code );
|
||||
|
||||
|
|
Loading…
Reference in New Issue