/* returns an fd on success, or a -1 on failure and sets errno */ int tr_fdFileCheckout( const char * folder, const char * torrentFile, int doWrite, int doPreallocate, uint64_t desiredFileSize ) { int i, winner = -1; struct tr_openfile * o; char * filename; assert( folder && *folder ); assert( torrentFile && *torrentFile ); assert( doWrite == 0 || doWrite == 1 ); filename = tr_buildPath( folder, torrentFile, NULL ); dbgmsg( "looking for file '%s', writable %c", filename, doWrite ? 'y' : 'n' ); tr_lockLock( gFd->lock ); /* Is it already open? */ for( i = 0; i < TR_MAX_OPEN_FILES; ++i ) { o = &gFd->open[i]; if( !fileIsOpen( o ) ) continue; if( strcmp( filename, o->filename ) ) continue; if( fileIsCheckedOut( o ) ) { dbgmsg( "found it! it's open, but checked out. waiting..." ); tr_lockUnlock( gFd->lock ); tr_wait( 200 ); tr_lockLock( gFd->lock ); i = -1; /* reloop */ continue; } if( doWrite && !o->isWritable ) { dbgmsg( "found it! it's open and available, but isn't writable. closing..." ); TrCloseFile( i ); break; } dbgmsg( "found it! it's ready for use!" ); winner = i; break; } dbgmsg( "it's not already open. looking for an open slot or an old file." ); while( winner < 0 ) { uint64_t date = tr_date( ) + 1; /* look for the file that's been open longest */ for( i = 0; i < TR_MAX_OPEN_FILES; ++i ) { o = &gFd->open[i]; if( !fileIsOpen( o ) ) { winner = i; dbgmsg( "found an empty slot in %d", winner ); break; } if( date > o->date ) { date = o->date; winner = i; } } if( winner >= 0 ) { if( fileIsOpen( &gFd->open[winner] ) ) { dbgmsg( "closing file '%s', slot #%d", gFd->open[winner].filename, winner ); TrCloseFile( winner ); } } else { dbgmsg( "everything's full! waiting for someone else to finish something" ); tr_lockUnlock( gFd->lock ); tr_wait( 200 ); tr_lockLock( gFd->lock ); } } assert( winner >= 0 ); o = &gFd->open[winner]; if( !fileIsOpen( o ) ) { const int err = TrOpenFile( winner, folder, torrentFile, doWrite, doPreallocate, desiredFileSize ); if( err ) { tr_lockUnlock( gFd->lock ); tr_free( filename ); errno = err; return -1; } dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner, doWrite ? 'y' : 'n' ); tr_strlcpy( o->filename, filename, sizeof( o->filename ) ); o->isWritable = doWrite; } dbgmsg( "checking out '%s' in slot %d", filename, winner ); o->isCheckedOut = 1; o->closeWhenDone = 0; o->date = tr_date( ); tr_free( filename ); tr_lockUnlock( gFd->lock ); return o->fd; }
/* returns an fd on success, or a -1 on failure and sets errno */ int tr_fdFileCheckout( tr_session * session, int torrentId, tr_file_index_t fileNum, const char * filename, tr_bool doWrite, tr_preallocation_mode preallocationMode, uint64_t desiredFileSize ) { int i, winner = -1; struct tr_fdInfo * gFd; struct tr_openfile * o; assert( tr_isSession( session ) ); assert( session->fdInfo != NULL ); assert( torrentId > 0 ); assert( filename && *filename ); assert( tr_isBool( doWrite ) ); gFd = session->fdInfo; dbgmsg( "looking for file '%s', writable %c", filename, doWrite ? 'y' : 'n' ); /* is it already open? */ for( i=0; i<gFd->openFileLimit; ++i ) { o = &gFd->openFiles[i]; if( torrentId != o->torrentId ) continue; if( fileNum != o->fileNum ) continue; if( !fileIsOpen( o ) ) continue; if( doWrite && !o->isWritable ) { dbgmsg( "found it! it's open and available, but isn't writable. closing..." ); TrCloseFile( o ); break; } dbgmsg( "found it! it's ready for use!" ); winner = i; break; } dbgmsg( "it's not already open. looking for an open slot or an old file." ); while( winner < 0 ) { time_t date = tr_time( ) + 1; /* look for the file that's been open longest */ for( i=0; i<gFd->openFileLimit; ++i ) { o = &gFd->openFiles[i]; if( !fileIsOpen( o ) ) { winner = i; dbgmsg( "found an empty slot in %d", winner ); break; } if( date > o->date ) { date = o->date; winner = i; } } assert( winner >= 0 ); if( fileIsOpen( &gFd->openFiles[winner] ) ) { dbgmsg( "closing file \"%s\"", gFd->openFiles[winner].filename ); TrCloseFile( &gFd->openFiles[winner] ); } } assert( winner >= 0 ); o = &gFd->openFiles[winner]; if( !fileIsOpen( o ) ) { const int err = TrOpenFile( session, winner, filename, doWrite, preallocationMode, desiredFileSize ); if( err ) { errno = err; return -1; } dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner, doWrite ? 'y' : 'n' ); tr_strlcpy( o->filename, filename, sizeof( o->filename ) ); o->isWritable = doWrite; } dbgmsg( "checking out '%s' in slot %d", filename, winner ); o->torrentId = torrentId; o->fileNum = fileNum; o->date = tr_time( ); return o->fd; }