]> granicus.if.org Git - transmission/commitdiff
(libT) #849: preallocate files when possible to prevent disk fragmentation
authorCharles Kerr <charles@transmissionbt.com>
Wed, 5 Nov 2008 05:56:06 +0000 (05:56 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Wed, 5 Nov 2008 05:56:06 +0000 (05:56 +0000)
configure.ac
libtransmission/fdlimit.c
libtransmission/fdlimit.h
libtransmission/inout.c
libtransmission/torrent.c
libtransmission/transmission.h

index ff3277a26a877b4a37603444de11b623be2a5010..b4628faa9f27e0b455c041c79243180246776266 100644 (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
index 30011fc7a389a0700afa281446212574cb256317..3c854374452368a6368d3beab341151472d7e816 100644 (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 );
index 63c4c7c7e951a7ac5a68671a45508cf20ebca287..8865427b55a648da47ba6a86ab6fb2cb493b0413 100644 (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.
index c2ea72b44b416208ff25d1c4caf1a9f8f75b75f1..bb1673cd517318345ff1c3b34cd6d2f6583e391f 100644 (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? */
index d371a6a03d958b315426bbcb0ed1b743c5893094..e367bcc2374eee2ecfad5a212a7da81349ab3986 100644 (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;
+}
index 8e5301f514bc903d6fe840917ee949341bb29d57..c88b2e86dd2cd30182f6f6c7b5d9b8107eb140f4 100644 (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.
  *