(libT) #849: preallocate files when possible to prevent disk fragmentation
This commit is contained in:
parent
194833dfec
commit
10566eece9
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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? */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue