(libT) #849: preallocate files when possible to prevent disk fragmentation

This commit is contained in:
Charles Kerr 2008-11-05 05:56:06 +00:00
parent 194833dfec
commit 10566eece9
6 changed files with 99 additions and 17 deletions

View File

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

View File

@ -32,6 +32,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef SYS_DARWIN
#include <fcntl.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
@ -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 );

View File

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

View File

@ -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? */

View File

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

View File

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