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:
Charles Kerr 2008-01-18 19:13:32 +00:00
parent dd75a769c9
commit de515ee339
13 changed files with 259 additions and 167 deletions

View File

@ -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' );

View File

@ -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.

View File

@ -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,

View File

@ -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 );

View File

@ -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 );

View File

@ -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;

View File

@ -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 );
}
}

View File

@ -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;

View File

@ -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 )
{

View File

@ -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;

View File

@ -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;

View File

@ -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";
}
/****

View File

@ -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 );