source: trunk/libtransmission/fdlimit.c @ 11816

Last change on this file since 11816 was 11816, checked in by jordan, 12 years ago

(trunk libT) #3973 "JIT verification verifies fresh downloaded torrents" -- win32 portability fix for r11813.

fsync() doesn't exist on Windows. bencode had a private function, tr_fsync(), that is a portability wrapper around fsync() on *nix and _commit() on win32. Make this function package-visible, rather than private, so fdlimit.c can use it too.

  • Property svn:keywords set to Date Rev Author Id
File size: 18.7 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 11816 2011-02-02 23:19:54Z 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
184/* portability wrapper for fsync(). */
185int
186tr_fsync( int fd )
187{
188#ifdef WIN32
189    return _commit( fd );
190#else
191    return fsync( fd );
192#endif
193}
194
195
196/* Like pread and pwrite, except that the position is undefined afterwards.
197   And of course they are not thread-safe. */
198
199/* don't use pread/pwrite on old versions of uClibc because they're buggy.
200 * https://trac.transmissionbt.com/ticket/3826 */
201#ifdef __UCLIBC__
202#define TR_UCLIBC_CHECK_VERSION(major,minor,micro) \
203    (__UCLIBC_MAJOR__ > (major) || \
204     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ > (minor)) || \
205     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ == (minor) && \
206      __UCLIBC_SUBLEVEL__ >= (micro)))
207#if !TR_UCLIBC_CHECK_VERSION(0,9,28)
208 #undef HAVE_PREAD
209 #undef HAVE_PWRITE
210#endif
211#endif
212
213#ifdef SYS_DARWIN
214 #define HAVE_PREAD
215 #define HAVE_PWRITE
216#endif
217
218ssize_t
219tr_pread( int fd, void *buf, size_t count, off_t offset )
220{
221#ifdef HAVE_PREAD
222    return pread( 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 read( fd, buf, count );
228#endif
229}
230
231ssize_t
232tr_pwrite( int fd, const void *buf, size_t count, off_t offset )
233{
234#ifdef HAVE_PWRITE
235    return pwrite( fd, buf, count, offset );
236#else
237    const off_t lrc = lseek( fd, offset, SEEK_SET );
238    if( lrc < 0 )
239        return -1;
240    return write( fd, buf, count );
241#endif
242}
243
244int
245tr_prefetch( int fd UNUSED, off_t offset UNUSED, size_t count UNUSED )
246{
247#ifdef HAVE_POSIX_FADVISE
248    return posix_fadvise( fd, offset, count, POSIX_FADV_WILLNEED );
249#elif defined(SYS_DARWIN)
250    struct radvisory radv;
251    radv.ra_offset = offset;
252    radv.ra_count = count;
253    return fcntl( fd, F_RDADVISE, &radv );
254#else
255    return 0;
256#endif
257}
258
259void
260tr_set_file_for_single_pass( int fd )
261{
262    if( fd >= 0 )
263    {
264        /* Set hints about the lookahead buffer and caching. It's okay
265           for these to fail silently, so don't let them affect errno */
266        const int err = errno;
267#ifdef HAVE_POSIX_FADVISE
268        posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL );
269#endif
270#ifdef SYS_DARWIN
271        fcntl( fd, F_RDAHEAD, 1 );
272        fcntl( fd, F_NOCACHE, 1 );
273#endif
274        errno = err;
275    }
276}
277
278static int
279open_local_file( const char * filename, int flags )
280{
281    const int fd = open( filename, flags, 0666 );
282    tr_set_file_for_single_pass( fd );
283    return fd;
284}
285int
286tr_open_file_for_writing( const char * filename )
287{
288    return open_local_file( filename, O_LARGEFILE|O_BINARY|O_CREAT|O_WRONLY );
289}
290int
291tr_open_file_for_scanning( const char * filename )
292{
293    return open_local_file( filename, O_LARGEFILE|O_BINARY|O_SEQUENTIAL|O_RDONLY );
294}
295
296void
297tr_close_file( int fd )
298{
299#if defined(HAVE_POSIX_FADVISE)
300    /* Set hint about not caching this file.
301       It's okay for this to fail silently, so don't let it affect errno */
302    const int err = errno;
303    posix_fadvise( fd, 0, 0, POSIX_FADV_DONTNEED );
304    errno = err;
305#endif
306#ifdef SYS_DARWIN
307    /* it's unclear to me from the man pages if this actually flushes out the cache,
308     * but it couldn't hurt... */
309    fcntl( fd, F_NOCACHE, 1 );
310#endif
311    tr_fsync( fd );
312    close( fd );
313}
314
315/*****
316******
317******
318******
319*****/
320
321struct tr_cached_file
322{
323    tr_bool          is_writable;
324    int              fd;
325    int              torrent_id;
326    tr_file_index_t  file_index;
327    time_t           used_at;
328};
329
330static inline tr_bool
331cached_file_is_open( const struct tr_cached_file * o )
332{
333    assert( o != NULL );
334
335    return o->fd >= 0;
336}
337
338static void
339cached_file_close( struct tr_cached_file * o )
340{
341    assert( cached_file_is_open( o ) );
342
343    tr_close_file( o->fd );
344    o->fd = -1;
345}
346
347/**
348 * returns 0 on success, or an errno value on failure.
349 * errno values include ENOENT if the parent folder doesn't exist,
350 * plus the errno values set by tr_mkdirp() and open().
351 */
352static int
353cached_file_open( struct tr_cached_file  * o,
354                  const char             * filename,
355                  tr_bool                  writable,
356                  tr_preallocation_mode    allocation,
357                  uint64_t                 file_size )
358{
359    int flags;
360    struct stat sb;
361    tr_bool alreadyExisted;
362
363    /* create subfolders, if any */
364    if( writable )
365    {
366        char * dir = tr_dirname( filename );
367        const int err = tr_mkdirp( dir, 0777 ) ? errno : 0;
368        if( err ) {
369            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) );
370            tr_free( dir );
371            return err;
372        }
373        tr_free( dir );
374    }
375
376    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
377
378    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_FULL ) )
379        if( preallocate_file_full( filename, file_size ) )
380            tr_dbg( "Preallocated file \"%s\"", filename );
381
382    /* open the file */
383    flags = writable ? ( O_RDWR | O_CREAT ) : O_RDONLY;
384    flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL;
385    o->fd = open( filename, flags, 0666 );
386
387    if( o->fd == -1 )
388    {
389        const int err = errno;
390        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
391        return err;
392    }
393
394    /* If the file already exists and it's too large, truncate it.
395     * This is a fringe case that happens if a torrent's been updated
396     * and one of the updated torrent's files is smaller.
397     * http://trac.transmissionbt.com/ticket/2228
398     * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
399     */
400    if( alreadyExisted && ( file_size < (uint64_t)sb.st_size ) )
401        ftruncate( o->fd, file_size );
402
403    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_SPARSE ) )
404        preallocate_file_sparse( o->fd, file_size );
405
406    /* Many (most?) clients request blocks in ascending order,
407     * so increase the readahead buffer.
408     * Also, disable OS-level caching because "inactive memory" angers users. */
409    tr_set_file_for_single_pass( o->fd );
410
411    return 0;
412}
413
414/***
415****
416***/
417
418struct tr_fileset
419{
420    struct tr_cached_file * begin;
421    const struct tr_cached_file * end;
422};
423
424static void
425fileset_construct( struct tr_fileset * set, int n )
426{
427    struct tr_cached_file * o;
428    const struct tr_cached_file TR_CACHED_FILE_INIT = { 0, -1, 0, 0, 0 };
429
430    set->begin = tr_new( struct tr_cached_file, n );
431    set->end = set->begin + n;
432
433    for( o=set->begin; o!=set->end; ++o )
434        *o = TR_CACHED_FILE_INIT;
435}
436
437static void
438fileset_close_all( struct tr_fileset * set )
439{
440    struct tr_cached_file * o;
441
442    if( set != NULL )
443        for( o=set->begin; o!=set->end; ++o )
444            if( cached_file_is_open( o ) )
445                cached_file_close( o );
446}
447
448static void
449fileset_destruct( struct tr_fileset * set )
450{
451    fileset_close_all( set );
452    tr_free( set->begin );
453    set->end = set->begin = NULL;
454}
455
456static void
457fileset_close_torrent( struct tr_fileset * set, int torrent_id )
458{
459    struct tr_cached_file * o;
460
461    if( set != NULL )
462        for( o=set->begin; o!=set->end; ++o )
463            if( ( o->torrent_id == torrent_id ) && cached_file_is_open( o ) )
464                cached_file_close( o );
465}
466
467static struct tr_cached_file *
468fileset_lookup( struct tr_fileset * set, int torrent_id, tr_file_index_t i )
469{
470    struct tr_cached_file * o;
471
472    if( set != NULL )
473        for( o=set->begin; o!=set->end; ++o )
474            if( ( torrent_id == o->torrent_id ) && ( i == o->file_index ) && cached_file_is_open( o ) )
475                return o;
476
477    return NULL;
478}
479
480static struct tr_cached_file *
481fileset_get_empty_slot( struct tr_fileset * set )
482{
483    struct tr_cached_file * o;
484    struct tr_cached_file * cull;
485
486    /* try to find an unused slot */
487    for( o=set->begin; o!=set->end; ++o )
488        if( !cached_file_is_open( o ) )
489            return o;
490
491    /* all slots are full... recycle the least recently used */
492    for( cull=NULL, o=set->begin; o!=set->end; ++o )
493        if( !cull || o->used_at < cull->used_at )
494            cull = o;
495    cached_file_close( cull );
496    return cull;
497}
498
499static int
500fileset_get_size( const struct tr_fileset * set )
501{
502    return set ? set->end - set->begin : 0;
503}
504
505/***
506****
507***/
508
509struct tr_fdInfo
510{
511    int socket_count;
512    int socket_limit;
513    int public_socket_limit;
514    struct tr_fileset fileset;
515};
516
517static struct tr_fileset*
518get_fileset( tr_session * session )
519{
520    return session && session->fdInfo ? &session->fdInfo->fileset : NULL;
521}
522
523void
524tr_fdFileClose( tr_session * s, const tr_torrent * tor, tr_file_index_t i )
525{
526    struct tr_cached_file * o;
527
528    if(( o = fileset_lookup( get_fileset( s ), tr_torrentId( tor ), i )))
529        cached_file_close( o );
530}
531
532int
533tr_fdFileGetCached( tr_session * s, int torrent_id, tr_file_index_t i, tr_bool writable )
534{
535    struct tr_cached_file * o = fileset_lookup( get_fileset( s ), torrent_id, i );
536
537    if( !o || ( writable && !o->is_writable ) )
538        return -1;
539
540    o->used_at = tr_time( );
541    return o->fd;
542}
543
544void
545tr_fdTorrentClose( tr_session * session, int torrent_id )
546{
547    fileset_close_torrent( get_fileset( session ), torrent_id );
548}
549
550/* returns an fd on success, or a -1 on failure and sets errno */
551int
552tr_fdFileCheckout( tr_session             * session,
553                   int                      torrent_id,
554                   tr_file_index_t          i,
555                   const char             * filename,
556                   tr_bool                  writable,
557                   tr_preallocation_mode    allocation,
558                   uint64_t                 file_size )
559{
560    struct tr_fileset * set = get_fileset( session );
561    struct tr_cached_file * o = fileset_lookup( set, torrent_id, i );
562
563    if( o && writable && !o->is_writable )
564        cached_file_close( o ); /* close it so we can reopen in rw mode */
565    else if( !o )
566        o = fileset_get_empty_slot( set );
567
568    if( !cached_file_is_open( o ) )
569    {
570        const int err = cached_file_open( o, filename, writable, allocation, file_size );
571        if( err ) {
572            errno = err;
573            return -1;
574        }
575
576        dbgmsg( "opened '%s' writable %c", filename, writable?'y':'n' );
577        o->is_writable = writable;
578    }
579
580    dbgmsg( "checking out '%s'", filename );
581    o->torrent_id = torrent_id;
582    o->file_index = i;
583    o->used_at = tr_time( );
584    return o->fd;
585}
586
587/***
588****
589****  Sockets
590****
591***/
592
593int
594tr_fdSocketCreate( tr_session * session, int domain, int type )
595{
596    int s = -1;
597    struct tr_fdInfo * gFd;
598
599    assert( tr_isSession( session ) );
600    assert( session->fdInfo != NULL );
601
602    gFd = session->fdInfo;
603
604    if( gFd->socket_count < gFd->socket_limit )
605        if(( s = socket( domain, type, 0 )) < 0 )
606            if( sockerrno != EAFNOSUPPORT )
607                tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) );
608
609    if( s > -1 )
610        ++gFd->socket_count;
611
612    assert( gFd->socket_count >= 0 );
613
614    if( s >= 0 )
615    {
616        static tr_bool buf_logged = FALSE;
617        if( !buf_logged )
618        {
619            int i;
620            socklen_t size = sizeof( int );
621            buf_logged = TRUE;
622            getsockopt( s, SOL_SOCKET, SO_SNDBUF, &i, &size );
623            tr_dbg( "SO_SNDBUF size is %d", i );
624            getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size );
625            tr_dbg( "SO_RCVBUF size is %d", i );
626        }
627    }
628
629    return s;
630}
631
632int
633tr_fdSocketAccept( tr_session * s, int sockfd, tr_address * addr, tr_port * port )
634{
635    int fd;
636    unsigned int len;
637    struct tr_fdInfo * gFd;
638    struct sockaddr_storage sock;
639
640    assert( tr_isSession( s ) );
641    assert( s->fdInfo != NULL );
642    assert( addr );
643    assert( port );
644
645    gFd = s->fdInfo;
646
647    len = sizeof( struct sockaddr_storage );
648    fd = accept( sockfd, (struct sockaddr *) &sock, &len );
649
650    if( ( fd >= 0 ) && gFd->socket_count > gFd->socket_limit )
651    {
652        tr_netCloseSocket( fd );
653        fd = -1;
654    }
655
656    if( fd >= 0 )
657    {
658        /* "The ss_family field of the sockaddr_storage structure will always
659         * align with the family field of any protocol-specific structure." */
660        if( sock.ss_family == AF_INET )
661        {
662            struct sockaddr_in *si;
663            union { struct sockaddr_storage dummy; struct sockaddr_in si; } s;
664            s.dummy = sock;
665            si = &s.si;
666            addr->type = TR_AF_INET;
667            addr->addr.addr4.s_addr = si->sin_addr.s_addr;
668            *port = si->sin_port;
669        }
670        else
671        {
672            struct sockaddr_in6 *si;
673            union { struct sockaddr_storage dummy; struct sockaddr_in6 si; } s;
674            s.dummy = sock;
675            si = &s.si;
676            addr->type = TR_AF_INET6;
677            addr->addr.addr6 = si->sin6_addr;
678            *port = si->sin6_port;
679        }
680        ++gFd->socket_count;
681    }
682
683    return fd;
684}
685
686void
687tr_fdSocketClose( tr_session * session, int fd )
688{
689    assert( tr_isSession( session ) );
690
691    if( session->fdInfo != NULL )
692    {
693        struct tr_fdInfo * gFd = session->fdInfo;
694
695        if( fd >= 0 )
696        {
697            tr_netCloseSocket( fd );
698            --gFd->socket_count;
699        }
700
701        assert( gFd->socket_count >= 0 );
702    }
703}
704
705/***
706****
707****  Startup / Shutdown
708****
709***/
710
711static void
712ensureSessionFdInfoExists( tr_session * session )
713{
714    assert( tr_isSession( session ) );
715
716    if( session->fdInfo == NULL )
717        session->fdInfo = tr_new0( struct tr_fdInfo, 1 );
718}
719
720void
721tr_fdClose( tr_session * session )
722{
723    struct tr_fdInfo * gFd = session->fdInfo;
724
725    if( gFd != NULL )
726    {
727        fileset_destruct( &gFd->fileset );
728        tr_free( gFd );
729    }
730
731    session->fdInfo = NULL;
732}
733
734/***
735****
736***/
737
738int
739tr_fdGetFileLimit( tr_session * session )
740{
741    return fileset_get_size( get_fileset( session ) );
742}
743
744void
745tr_fdSetFileLimit( tr_session * session, int limit )
746{
747    ensureSessionFdInfoExists( session );
748
749    if( limit != tr_fdGetFileLimit( session ) )
750    {
751        struct tr_fileset * set = get_fileset( session );
752        fileset_destruct( set );
753        fileset_construct( set, limit );
754    }
755}
756
757void
758tr_fdSetPeerLimit( tr_session * session, int socket_limit )
759{
760    struct tr_fdInfo * gFd;
761
762    ensureSessionFdInfoExists( session );
763
764    gFd = session->fdInfo;
765
766#ifdef HAVE_GETRLIMIT
767    {
768        struct rlimit rlim;
769        const int NOFILE_BUFFER = 512;
770        const int open_max = sysconf( _SC_OPEN_MAX );
771        getrlimit( RLIMIT_NOFILE, &rlim );
772        rlim.rlim_cur = MAX( 1024, open_max );
773        rlim.rlim_cur = MIN( rlim.rlim_cur, rlim.rlim_max );
774        setrlimit( RLIMIT_NOFILE, &rlim );
775        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
776        gFd->socket_limit = MIN( socket_limit, (int)rlim.rlim_cur - NOFILE_BUFFER );
777    }
778#else
779    gFd->socket_limit = socket_limit;
780#endif
781    gFd->public_socket_limit = socket_limit;
782
783    tr_dbg( "socket limit is %d", (int)gFd->socket_limit );
784}
785
786int
787tr_fdGetPeerLimit( const tr_session * session )
788{
789    return session && session->fdInfo ? session->fdInfo->public_socket_limit : -1;
790}
Note: See TracBrowser for help on using the repository browser.