source: trunk/libtransmission/fdlimit.c @ 11813

Last change on this file since 11813 was 11813, checked in by jordan, 11 years ago

(trunk libT) #3973 "JIT verification verifies freshly-downloaded torrents" -- test fix.

Files downloaded in Transmission 2.20 betas [1..3] forced each piece to be checked twice -- once on download, and once when uploading the piece for the first time. Older versions of Transmission didn't perform the latter check unless the file had changed after it was downloaded. This commit restores that behavior.

  • Property svn:keywords set to Date Rev Author Id
File size: 18.6 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: fdlimit.c 11813 2011-02-02 20:30:04Z jordan $
11 */
12
13#ifndef WIN32
14 #define HAVE_GETRLIMIT
15#endif
16
17#ifdef HAVE_POSIX_FADVISE
18 #ifdef _XOPEN_SOURCE
19  #undef _XOPEN_SOURCE
20 #endif
21 #define _XOPEN_SOURCE 600
22#endif
23
24#include <assert.h>
25#include <errno.h>
26#include <inttypes.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#ifdef SYS_DARWIN
31 #include <fcntl.h>
32#endif
33
34#ifdef HAVE_FALLOCATE64
35  /* FIXME can't find the right #include voodoo to pick up the declaration.. */
36  extern int fallocate64( int fd, int mode, uint64_t offset, uint64_t len );
37#endif
38
39#ifdef HAVE_XFS_XFS_H
40 #include <xfs/xfs.h>
41#endif
42
43#include <sys/types.h>
44#include <sys/stat.h>
45#ifdef HAVE_GETRLIMIT
46 #include <sys/time.h> /* getrlimit */
47 #include <sys/resource.h> /* getrlimit */
48#endif
49#include <fcntl.h> /* O_LARGEFILE posix_fadvise */
50#include <unistd.h>
51
52#include "transmission.h"
53#include "fdlimit.h"
54#include "net.h"
55#include "session.h"
56#include "torrent.h" /* tr_isTorrent() */
57
58#define dbgmsg( ... ) \
59    do { \
60        if( tr_deepLoggingIsActive( ) ) \
61            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
62    } while( 0 )
63
64/***
65****
66****  Local Files
67****
68***/
69
70#ifndef O_LARGEFILE
71 #define O_LARGEFILE 0
72#endif
73
74#ifndef O_BINARY
75 #define O_BINARY 0
76#endif
77
78#ifndef O_SEQUENTIAL
79 #define O_SEQUENTIAL 0
80#endif
81
82
83static tr_bool
84preallocate_file_sparse( int fd, uint64_t length )
85{
86    const char zero = '\0';
87    tr_bool success = 0;
88
89    if( !length )
90        success = TRUE;
91
92#ifdef HAVE_FALLOCATE64
93    if( !success ) /* fallocate64 is always preferred, so try it first */
94        success = !fallocate64( fd, 0, 0, length );
95#endif
96
97    if( !success ) /* fallback: the old-style seek-and-write */
98        success = ( lseek( fd, length-1, SEEK_SET ) != -1 )
99               && ( write( fd, &zero, 1 ) != -1 )
100               && ( ftruncate( fd, length ) != -1 );
101
102    return success;
103}
104
105static tr_bool
106preallocate_file_full( const char * filename, uint64_t length )
107{
108    tr_bool success = 0;
109
110#ifdef WIN32
111
112    HANDLE hFile = CreateFile( filename, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0 );
113    if( hFile != INVALID_HANDLE_VALUE )
114    {
115        LARGE_INTEGER li;
116        li.QuadPart = length;
117        success = SetFilePointerEx( hFile, li, NULL, FILE_BEGIN ) && SetEndOfFile( hFile );
118        CloseHandle( hFile );
119    }
120
121#else
122
123    int flags = O_RDWR | O_CREAT | O_LARGEFILE;
124    int fd = open( filename, flags, 0666 );
125    if( fd >= 0 )
126    {
127# ifdef HAVE_FALLOCATE64
128       if( !success )
129       {
130           success = !fallocate64( fd, 0, 0, length );
131       }
132# endif
133# ifdef HAVE_XFS_XFS_H
134        if( !success && platform_test_xfs_fd( fd ) )
135        {
136            xfs_flock64_t fl;
137            fl.l_whence = 0;
138            fl.l_start = 0;
139            fl.l_len = length;
140            success = !xfsctl( NULL, fd, XFS_IOC_RESVSP64, &fl );
141        }
142# endif
143# ifdef SYS_DARWIN
144        if( !success )
145        {
146            fstore_t fst;
147            fst.fst_flags = F_ALLOCATECONTIG;
148            fst.fst_posmode = F_PEOFPOSMODE;
149            fst.fst_offset = 0;
150            fst.fst_length = length;
151            fst.fst_bytesalloc = 0;
152            success = !fcntl( fd, F_PREALLOCATE, &fst );
153        }
154# endif
155# ifdef HAVE_POSIX_FALLOCATE
156        if( !success )
157        {
158            success = !posix_fallocate( fd, 0, length );
159        }
160# endif
161
162        if( !success ) /* if nothing else works, do it the old-fashioned way */
163        {
164            uint8_t buf[ 4096 ];
165            memset( buf, 0, sizeof( buf ) );
166            success = TRUE;
167            while ( success && ( length > 0 ) )
168            {
169                const int thisPass = MIN( length, sizeof( buf ) );
170                success = write( fd, buf, thisPass ) == thisPass;
171                length -= thisPass;
172            }
173        }
174
175        close( fd );
176    }
177
178#endif
179
180    return success;
181}
182
183/* Like pread and pwrite, except that the position is undefined afterwards.
184   And of course they are not thread-safe. */
185
186/* don't use pread/pwrite on old versions of uClibc because they're buggy.
187 * https://trac.transmissionbt.com/ticket/3826 */
188#ifdef __UCLIBC__
189#define TR_UCLIBC_CHECK_VERSION(major,minor,micro) \
190    (__UCLIBC_MAJOR__ > (major) || \
191     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ > (minor)) || \
192     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ == (minor) && \
193      __UCLIBC_SUBLEVEL__ >= (micro)))
194#if !TR_UCLIBC_CHECK_VERSION(0,9,28)
195 #undef HAVE_PREAD
196 #undef HAVE_PWRITE
197#endif
198#endif
199
200#ifdef SYS_DARWIN
201 #define HAVE_PREAD
202 #define HAVE_PWRITE
203#endif
204
205ssize_t
206tr_pread( int fd, void *buf, size_t count, off_t offset )
207{
208#ifdef HAVE_PREAD
209    return pread( fd, buf, count, offset );
210#else
211    const off_t lrc = lseek( fd, offset, SEEK_SET );
212    if( lrc < 0 )
213        return -1;
214    return read( fd, buf, count );
215#endif
216}
217
218ssize_t
219tr_pwrite( int fd, const void *buf, size_t count, off_t offset )
220{
221#ifdef HAVE_PWRITE
222    return pwrite( fd, buf, count, offset );
223#else
224    const off_t lrc = lseek( fd, offset, SEEK_SET );
225    if( lrc < 0 )
226        return -1;
227    return write( fd, buf, count );
228#endif
229}
230
231int
232tr_prefetch( int fd UNUSED, off_t offset UNUSED, size_t count UNUSED )
233{
234#ifdef HAVE_POSIX_FADVISE
235    return posix_fadvise( fd, offset, count, POSIX_FADV_WILLNEED );
236#elif defined(SYS_DARWIN)
237    struct radvisory radv;
238    radv.ra_offset = offset;
239    radv.ra_count = count;
240    return fcntl( fd, F_RDADVISE, &radv );
241#else
242    return 0;
243#endif
244}
245
246void
247tr_set_file_for_single_pass( int fd )
248{
249    if( fd >= 0 )
250    {
251        /* Set hints about the lookahead buffer and caching. It's okay
252           for these to fail silently, so don't let them affect errno */
253        const int err = errno;
254#ifdef HAVE_POSIX_FADVISE
255        posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL );
256#endif
257#ifdef SYS_DARWIN
258        fcntl( fd, F_RDAHEAD, 1 );
259        fcntl( fd, F_NOCACHE, 1 );
260#endif
261        errno = err;
262    }
263}
264
265static int
266open_local_file( const char * filename, int flags )
267{
268    const int fd = open( filename, flags, 0666 );
269    tr_set_file_for_single_pass( fd );
270    return fd;
271}
272int
273tr_open_file_for_writing( const char * filename )
274{
275    return open_local_file( filename, O_LARGEFILE|O_BINARY|O_CREAT|O_WRONLY );
276}
277int
278tr_open_file_for_scanning( const char * filename )
279{
280    return open_local_file( filename, O_LARGEFILE|O_BINARY|O_SEQUENTIAL|O_RDONLY );
281}
282
283void
284tr_close_file( int fd )
285{
286#if defined(HAVE_POSIX_FADVISE)
287    /* Set hint about not caching this file.
288       It's okay for this to fail silently, so don't let it affect errno */
289    const int err = errno;
290    posix_fadvise( fd, 0, 0, POSIX_FADV_DONTNEED );
291    errno = err;
292#endif
293#ifdef SYS_DARWIN
294    /* it's unclear to me from the man pages if this actually flushes out the cache,
295     * but it couldn't hurt... */
296    fcntl( fd, F_NOCACHE, 1 );
297#endif
298    fsync( fd );
299    close( fd );
300}
301
302/*****
303******
304******
305******
306*****/
307
308struct tr_cached_file
309{
310    tr_bool          is_writable;
311    int              fd;
312    int              torrent_id;
313    tr_file_index_t  file_index;
314    time_t           used_at;
315};
316
317static inline tr_bool
318cached_file_is_open( const struct tr_cached_file * o )
319{
320    assert( o != NULL );
321
322    return o->fd >= 0;
323}
324
325static void
326cached_file_close( struct tr_cached_file * o )
327{
328    assert( cached_file_is_open( o ) );
329
330    tr_close_file( o->fd );
331    o->fd = -1;
332}
333
334/**
335 * returns 0 on success, or an errno value on failure.
336 * errno values include ENOENT if the parent folder doesn't exist,
337 * plus the errno values set by tr_mkdirp() and open().
338 */
339static int
340cached_file_open( struct tr_cached_file  * o,
341                  const char             * filename,
342                  tr_bool                  writable,
343                  tr_preallocation_mode    allocation,
344                  uint64_t                 file_size )
345{
346    int flags;
347    struct stat sb;
348    tr_bool alreadyExisted;
349
350    /* create subfolders, if any */
351    if( writable )
352    {
353        char * dir = tr_dirname( filename );
354        const int err = tr_mkdirp( dir, 0777 ) ? errno : 0;
355        if( err ) {
356            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) );
357            tr_free( dir );
358            return err;
359        }
360        tr_free( dir );
361    }
362
363    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
364
365    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_FULL ) )
366        if( preallocate_file_full( filename, file_size ) )
367            tr_dbg( "Preallocated file \"%s\"", filename );
368
369    /* open the file */
370    flags = writable ? ( O_RDWR | O_CREAT ) : O_RDONLY;
371    flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL;
372    o->fd = open( filename, flags, 0666 );
373
374    if( o->fd == -1 )
375    {
376        const int err = errno;
377        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
378        return err;
379    }
380
381    /* If the file already exists and it's too large, truncate it.
382     * This is a fringe case that happens if a torrent's been updated
383     * and one of the updated torrent's files is smaller.
384     * http://trac.transmissionbt.com/ticket/2228
385     * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
386     */
387    if( alreadyExisted && ( file_size < (uint64_t)sb.st_size ) )
388        ftruncate( o->fd, file_size );
389
390    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_SPARSE ) )
391        preallocate_file_sparse( o->fd, file_size );
392
393    /* Many (most?) clients request blocks in ascending order,
394     * so increase the readahead buffer.
395     * Also, disable OS-level caching because "inactive memory" angers users. */
396    tr_set_file_for_single_pass( o->fd );
397
398    return 0;
399}
400
401/***
402****
403***/
404
405struct tr_fileset
406{
407    struct tr_cached_file * begin;
408    const struct tr_cached_file * end;
409};
410
411static void
412fileset_construct( struct tr_fileset * set, int n )
413{
414    struct tr_cached_file * o;
415    const struct tr_cached_file TR_CACHED_FILE_INIT = { 0, -1, 0, 0, 0 };
416
417    set->begin = tr_new( struct tr_cached_file, n );
418    set->end = set->begin + n;
419
420    for( o=set->begin; o!=set->end; ++o )
421        *o = TR_CACHED_FILE_INIT;
422}
423
424static void
425fileset_close_all( struct tr_fileset * set )
426{
427    struct tr_cached_file * o;
428
429    if( set != NULL )
430        for( o=set->begin; o!=set->end; ++o )
431            if( cached_file_is_open( o ) )
432                cached_file_close( o );
433}
434
435static void
436fileset_destruct( struct tr_fileset * set )
437{
438    fileset_close_all( set );
439    tr_free( set->begin );
440    set->end = set->begin = NULL;
441}
442
443static void
444fileset_close_torrent( struct tr_fileset * set, int torrent_id )
445{
446    struct tr_cached_file * o;
447
448    if( set != NULL )
449        for( o=set->begin; o!=set->end; ++o )
450            if( ( o->torrent_id == torrent_id ) && cached_file_is_open( o ) )
451                cached_file_close( o );
452}
453
454static struct tr_cached_file *
455fileset_lookup( struct tr_fileset * set, int torrent_id, tr_file_index_t i )
456{
457    struct tr_cached_file * o;
458
459    if( set != NULL )
460        for( o=set->begin; o!=set->end; ++o )
461            if( ( torrent_id == o->torrent_id ) && ( i == o->file_index ) && cached_file_is_open( o ) )
462                return o;
463
464    return NULL;
465}
466
467static struct tr_cached_file *
468fileset_get_empty_slot( struct tr_fileset * set )
469{
470    struct tr_cached_file * o;
471    struct tr_cached_file * cull;
472
473    /* try to find an unused slot */
474    for( o=set->begin; o!=set->end; ++o )
475        if( !cached_file_is_open( o ) )
476            return o;
477
478    /* all slots are full... recycle the least recently used */
479    for( cull=NULL, o=set->begin; o!=set->end; ++o )
480        if( !cull || o->used_at < cull->used_at )
481            cull = o;
482    cached_file_close( cull );
483    return cull;
484}
485
486static int
487fileset_get_size( const struct tr_fileset * set )
488{
489    return set ? set->end - set->begin : 0;
490}
491
492/***
493****
494***/
495
496struct tr_fdInfo
497{
498    int socket_count;
499    int socket_limit;
500    int public_socket_limit;
501    struct tr_fileset fileset;
502};
503
504static struct tr_fileset*
505get_fileset( tr_session * session )
506{
507    return session && session->fdInfo ? &session->fdInfo->fileset : NULL;
508}
509
510void
511tr_fdFileClose( tr_session * s, const tr_torrent * tor, tr_file_index_t i )
512{
513    struct tr_cached_file * o;
514
515    if(( o = fileset_lookup( get_fileset( s ), tr_torrentId( tor ), i )))
516        cached_file_close( o );
517}
518
519int
520tr_fdFileGetCached( tr_session * s, int torrent_id, tr_file_index_t i, tr_bool writable )
521{
522    struct tr_cached_file * o = fileset_lookup( get_fileset( s ), torrent_id, i );
523
524    if( !o || ( writable && !o->is_writable ) )
525        return -1;
526
527    o->used_at = tr_time( );
528    return o->fd;
529}
530
531void
532tr_fdTorrentClose( tr_session * session, int torrent_id )
533{
534    fileset_close_torrent( get_fileset( session ), torrent_id );
535}
536
537/* returns an fd on success, or a -1 on failure and sets errno */
538int
539tr_fdFileCheckout( tr_session             * session,
540                   int                      torrent_id,
541                   tr_file_index_t          i,
542                   const char             * filename,
543                   tr_bool                  writable,
544                   tr_preallocation_mode    allocation,
545                   uint64_t                 file_size )
546{
547    struct tr_fileset * set = get_fileset( session );
548    struct tr_cached_file * o = fileset_lookup( set, torrent_id, i );
549
550    if( o && writable && !o->is_writable )
551        cached_file_close( o ); /* close it so we can reopen in rw mode */
552    else if( !o )
553        o = fileset_get_empty_slot( set );
554
555    if( !cached_file_is_open( o ) )
556    {
557        const int err = cached_file_open( o, filename, writable, allocation, file_size );
558        if( err ) {
559            errno = err;
560            return -1;
561        }
562
563        dbgmsg( "opened '%s' writable %c", filename, writable?'y':'n' );
564        o->is_writable = writable;
565    }
566
567    dbgmsg( "checking out '%s'", filename );
568    o->torrent_id = torrent_id;
569    o->file_index = i;
570    o->used_at = tr_time( );
571    return o->fd;
572}
573
574/***
575****
576****  Sockets
577****
578***/
579
580int
581tr_fdSocketCreate( tr_session * session, int domain, int type )
582{
583    int s = -1;
584    struct tr_fdInfo * gFd;
585
586    assert( tr_isSession( session ) );
587    assert( session->fdInfo != NULL );
588
589    gFd = session->fdInfo;
590
591    if( gFd->socket_count < gFd->socket_limit )
592        if(( s = socket( domain, type, 0 )) < 0 )
593            if( sockerrno != EAFNOSUPPORT )
594                tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) );
595
596    if( s > -1 )
597        ++gFd->socket_count;
598
599    assert( gFd->socket_count >= 0 );
600
601    if( s >= 0 )
602    {
603        static tr_bool buf_logged = FALSE;
604        if( !buf_logged )
605        {
606            int i;
607            socklen_t size = sizeof( int );
608            buf_logged = TRUE;
609            getsockopt( s, SOL_SOCKET, SO_SNDBUF, &i, &size );
610            tr_dbg( "SO_SNDBUF size is %d", i );
611            getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size );
612            tr_dbg( "SO_RCVBUF size is %d", i );
613        }
614    }
615
616    return s;
617}
618
619int
620tr_fdSocketAccept( tr_session * s, int sockfd, tr_address * addr, tr_port * port )
621{
622    int fd;
623    unsigned int len;
624    struct tr_fdInfo * gFd;
625    struct sockaddr_storage sock;
626
627    assert( tr_isSession( s ) );
628    assert( s->fdInfo != NULL );
629    assert( addr );
630    assert( port );
631
632    gFd = s->fdInfo;
633
634    len = sizeof( struct sockaddr_storage );
635    fd = accept( sockfd, (struct sockaddr *) &sock, &len );
636
637    if( ( fd >= 0 ) && gFd->socket_count > gFd->socket_limit )
638    {
639        tr_netCloseSocket( fd );
640        fd = -1;
641    }
642
643    if( fd >= 0 )
644    {
645        /* "The ss_family field of the sockaddr_storage structure will always
646         * align with the family field of any protocol-specific structure." */
647        if( sock.ss_family == AF_INET )
648        {
649            struct sockaddr_in *si;
650            union { struct sockaddr_storage dummy; struct sockaddr_in si; } s;
651            s.dummy = sock;
652            si = &s.si;
653            addr->type = TR_AF_INET;
654            addr->addr.addr4.s_addr = si->sin_addr.s_addr;
655            *port = si->sin_port;
656        }
657        else
658        {
659            struct sockaddr_in6 *si;
660            union { struct sockaddr_storage dummy; struct sockaddr_in6 si; } s;
661            s.dummy = sock;
662            si = &s.si;
663            addr->type = TR_AF_INET6;
664            addr->addr.addr6 = si->sin6_addr;
665            *port = si->sin6_port;
666        }
667        ++gFd->socket_count;
668    }
669
670    return fd;
671}
672
673void
674tr_fdSocketClose( tr_session * session, int fd )
675{
676    assert( tr_isSession( session ) );
677
678    if( session->fdInfo != NULL )
679    {
680        struct tr_fdInfo * gFd = session->fdInfo;
681
682        if( fd >= 0 )
683        {
684            tr_netCloseSocket( fd );
685            --gFd->socket_count;
686        }
687
688        assert( gFd->socket_count >= 0 );
689    }
690}
691
692/***
693****
694****  Startup / Shutdown
695****
696***/
697
698static void
699ensureSessionFdInfoExists( tr_session * session )
700{
701    assert( tr_isSession( session ) );
702
703    if( session->fdInfo == NULL )
704        session->fdInfo = tr_new0( struct tr_fdInfo, 1 );
705}
706
707void
708tr_fdClose( tr_session * session )
709{
710    struct tr_fdInfo * gFd = session->fdInfo;
711
712    if( gFd != NULL )
713    {
714        fileset_destruct( &gFd->fileset );
715        tr_free( gFd );
716    }
717
718    session->fdInfo = NULL;
719}
720
721/***
722****
723***/
724
725int
726tr_fdGetFileLimit( tr_session * session )
727{
728    return fileset_get_size( get_fileset( session ) );
729}
730
731void
732tr_fdSetFileLimit( tr_session * session, int limit )
733{
734    ensureSessionFdInfoExists( session );
735
736    if( limit != tr_fdGetFileLimit( session ) )
737    {
738        struct tr_fileset * set = get_fileset( session );
739        fileset_destruct( set );
740        fileset_construct( set, limit );
741    }
742}
743
744void
745tr_fdSetPeerLimit( tr_session * session, int socket_limit )
746{
747    struct tr_fdInfo * gFd;
748
749    ensureSessionFdInfoExists( session );
750
751    gFd = session->fdInfo;
752
753#ifdef HAVE_GETRLIMIT
754    {
755        struct rlimit rlim;
756        const int NOFILE_BUFFER = 512;
757        const int open_max = sysconf( _SC_OPEN_MAX );
758        getrlimit( RLIMIT_NOFILE, &rlim );
759        rlim.rlim_cur = MAX( 1024, open_max );
760        rlim.rlim_cur = MIN( rlim.rlim_cur, rlim.rlim_max );
761        setrlimit( RLIMIT_NOFILE, &rlim );
762        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
763        gFd->socket_limit = MIN( socket_limit, (int)rlim.rlim_cur - NOFILE_BUFFER );
764    }
765#else
766    gFd->socket_limit = socket_limit;
767#endif
768    gFd->public_socket_limit = socket_limit;
769
770    tr_dbg( "socket limit is %d", (int)gFd->socket_limit );
771}
772
773int
774tr_fdGetPeerLimit( const tr_session * session )
775{
776    return session && session->fdInfo ? session->fdInfo->public_socket_limit : -1;
777}
Note: See TracBrowser for help on using the repository browser.