From 10566eece9918132302f530f994bb181fbce47ab Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 5 Nov 2008 05:56:06 +0000 Subject: [PATCH] (libT) #849: preallocate files when possible to prevent disk fragmentation --- configure.ac | 2 +- libtransmission/fdlimit.c | 65 ++++++++++++++++++++++++++++------ libtransmission/fdlimit.h | 4 ++- libtransmission/inout.c | 11 +++--- libtransmission/torrent.c | 31 ++++++++++++++++ libtransmission/transmission.h | 3 ++ 6 files changed, 99 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index ff3277a26..b4628faa9 100644 --- a/configure.ac +++ b/configure.ac @@ -49,7 +49,7 @@ fi AC_HEADER_STDC AC_HEADER_TIME -AC_CHECK_FUNCS([lrintf strlcpy daemon dirname basename daemon strcasecmp localtime_r]) +AC_CHECK_FUNCS([lrintf strlcpy daemon dirname basename daemon strcasecmp localtime_r fallocate posix_fallocate]) AC_PROG_INSTALL AC_PROG_MAKE_SET ACX_PTHREAD diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c index 30011fc7a..3c8543744 100644 --- a/libtransmission/fdlimit.c +++ b/libtransmission/fdlimit.c @@ -32,6 +32,9 @@ #include #include #include +#ifdef SYS_DARWIN +#include +#endif #include #include @@ -96,6 +99,35 @@ static struct tr_fd_s * gFd = NULL; **** ***/ +static int +preallocateFile( int fd UNUSED, uint64_t length UNUSED ) +{ +#ifdef HAVE_FALLOCATE + + return fallocate( fd, 0, offset, length ); + +#elif defined(HAVE_POSIX_FALLOCATE) + + return posix_fallocate( fd, 0, length ); + +#elif defined(SYS_DARWIN) + + fstore_t fst; + fst.fst_flags = F_ALLOCATECONTIG; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = length; + fst.fst_bytesalloc = 0; + return fcntl( fd, F_PREALLOCATE, &fst ); + +#else + + #warning no known method to preallocate files on this platform + return -1; + +#endif +} + /** * returns 0 on success, or an errno value on failure. * errno values include ENOENT if the parent folder doesn't exist, @@ -105,12 +137,15 @@ static int TrOpenFile( int i, const char * folder, const char * torrentFile, - int write ) + int doWrite, + int doPreallocate, + uint64_t desiredFileSize ) { struct tr_openfile * file = &gFd->open[i]; int flags; char * filename; struct stat sb; + int alreadyExisted; /* confirm the parent folder exists */ if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) ) @@ -118,7 +153,7 @@ TrOpenFile( int i, /* create subfolders, if any */ filename = tr_buildPath( folder, torrentFile, NULL ); - if( write ) + if( doWrite ) { char * tmp = tr_dirname( filename ); const int err = tr_mkdirp( tmp, 0777 ) ? errno : 0; @@ -129,8 +164,10 @@ TrOpenFile( int i, } } + alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode ); + /* open the file */ - flags = write ? ( O_RDWR | O_CREAT ) : O_RDONLY; + flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY; #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif @@ -147,6 +184,10 @@ TrOpenFile( int i, return err; } + if( ( file->fd >= 0 ) && !alreadyExisted && doPreallocate ) + if( !preallocateFile( file->fd, desiredFileSize ) ) + tr_inf( _( "Preallocated file \"%s\"" ), filename ); + tr_free( filename ); return 0; } @@ -181,7 +222,9 @@ fileIsCheckedOut( const struct tr_openfile * o ) int tr_fdFileCheckout( const char * folder, const char * torrentFile, - int write ) + int doWrite, + int doPreallocate, + uint64_t desiredFileSize ) { int i, winner = -1; struct tr_openfile * o; @@ -189,11 +232,11 @@ tr_fdFileCheckout( const char * folder, assert( folder && *folder ); assert( torrentFile && *torrentFile ); - assert( write == 0 || write == 1 ); + assert( doWrite == 0 || doWrite == 1 ); filename = tr_buildPath( folder, torrentFile, NULL ); dbgmsg( "looking for file '%s', writable %c", filename, - write ? 'y' : 'n' ); + doWrite ? 'y' : 'n' ); tr_lockLock( gFd->lock ); @@ -218,7 +261,7 @@ tr_fdFileCheckout( const char * folder, continue; } - if( write && !o->isWritable ) + if( doWrite && !o->isWritable ) { dbgmsg( "found it! it's open and available, but isn't writable. closing..." ); @@ -280,7 +323,7 @@ tr_fdFileCheckout( const char * folder, o = &gFd->open[winner]; if( !fileIsOpen( o ) ) { - const int err = TrOpenFile( winner, folder, torrentFile, write ); + const int err = TrOpenFile( winner, folder, torrentFile, doWrite, doPreallocate, desiredFileSize ); if( err ) { tr_lockUnlock( gFd->lock ); tr_free( filename ); @@ -288,10 +331,10 @@ tr_fdFileCheckout( const char * folder, return -1; } - dbgmsg( "opened '%s' in slot %d, write %c", filename, winner, - write ? 'y' : 'n' ); + dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner, + doWrite ? 'y' : 'n' ); tr_strlcpy( o->filename, filename, sizeof( o->filename ) ); - o->isWritable = write; + o->isWritable = doWrite; } dbgmsg( "checking out '%s' in slot %d", filename, winner ); diff --git a/libtransmission/fdlimit.h b/libtransmission/fdlimit.h index 63c4c7c7e..8865427b5 100644 --- a/libtransmission/fdlimit.h +++ b/libtransmission/fdlimit.h @@ -54,7 +54,9 @@ void tr_fdClose( void ); */ int tr_fdFileCheckout( const char * folder, const char * torrentFile, - int doWrite ); + int doWrite, + int doPreallocate, + uint64_t desiredFileSize ); /** * Returns an fd from tr_fdFileCheckout() so that other clients may borrow it. diff --git a/libtransmission/inout.c b/libtransmission/inout.c index c2ea72b44..bb1673cd5 100644 --- a/libtransmission/inout.c +++ b/libtransmission/inout.c @@ -83,9 +83,7 @@ readOrWriteBytes( const tr_torrent * tor, if( ( ioMode == TR_IO_READ ) && !fileExists ) /* does file exist? */ err = errno; - else if( ( fd = tr_fdFileCheckout ( tor->downloadDir, - file->name, - ioMode == TR_IO_WRITE ) ) < 0 ) + else if( ( fd = tr_fdFileCheckout ( tor->downloadDir, file->name, ioMode == TR_IO_WRITE, !file->dnd, file->length ) ) < 0 ) err = errno; else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ( (off_t)-1 ) ) err = errno; @@ -153,7 +151,12 @@ ensureMinimumFileSize( const tr_torrent * tor, assert( 0 <= fileIndex && fileIndex < tor->info.fileCount ); assert( minBytes <= file->length ); - fd = tr_fdFileCheckout( tor->downloadDir, file->name, TRUE ); + fd = tr_fdFileCheckout( tor->downloadDir, + file->name, + TRUE, + tor->session->doPreallocateFiles && !file->dnd, + file->length ); + if( fd < 0 ) /* bad fd */ err = errno; else if( fstat ( fd, &sb ) ) /* how big is the file? */ diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index d371a6a03..e367bcc23 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -1768,3 +1768,34 @@ tr_torrentSetDoneDate( tr_torrent * tor, tor->doneDate = t; } +/** +*** +**/ + +uint64_t +tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor ) +{ + const tr_file * it; + const tr_file * end; + struct stat sb; + uint64_t bytesLeft = 0; + + for( it=tor->info.files, end=it+tor->info.fileCount; it!=end; ++it ) + { + if( !it->dnd ) + { + char * path = tr_buildPath( tor->downloadDir, it->name, NULL ); + + bytesLeft += it->length; + + if( !stat( path, &sb ) + && S_ISREG( sb.st_mode ) + && ( sb.st_size <= it->length ) ) + bytesLeft -= sb.st_size; + + tr_free( path ); + } + } + + return bytesLeft; +} diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 8e5301f51..c88b2e86d 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -834,6 +834,9 @@ void tr_torrentStop( tr_torrent * torrent ); tr_torrent* tr_torrentNext( tr_session * session, tr_torrent * current ); + +uint64_t tr_torrentGetBytesLeftToAllocate( const tr_torrent * torrent ); + /** * @brief Returns this torrent's unique ID. * -- 2.40.0