source: trunk/libtransmission/fdlimit.c @ 11881

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

(trunk) #4019 "Minor patch, kill useless casts in transmission source." -- fixed. patch by jlouis

jlouis used Coccinelle to scan transmission's source for redundant code that casts a type to its own type.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.1 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 11881 2011-02-12 17:27:47Z 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    close( fd );
312}
313
314/*****
315******
316******
317******
318*****/
319
320struct tr_cached_file
321{
322    tr_bool          is_writable;
323    int              fd;
324    int              torrent_id;
325    tr_file_index_t  file_index;
326    time_t           used_at;
327};
328
329static inline tr_bool
330cached_file_is_open( const struct tr_cached_file * o )
331{
332    assert( o != NULL );
333
334    return o->fd >= 0;
335}
336
337static void
338cached_file_close( struct tr_cached_file * o )
339{
340    assert( cached_file_is_open( o ) );
341
342    tr_close_file( o->fd );
343    o->fd = -1;
344}
345
346/**
347 * returns 0 on success, or an errno value on failure.
348 * errno values include ENOENT if the parent folder doesn't exist,
349 * plus the errno values set by tr_mkdirp() and open().
350 */
351static int
352cached_file_open( struct tr_cached_file  * o,
353                  const char             * filename,
354                  tr_bool                  writable,
355                  tr_preallocation_mode    allocation,
356                  uint64_t                 file_size )
357{
358    int flags;
359    struct stat sb;
360    tr_bool alreadyExisted;
361
362    /* create subfolders, if any */
363    if( writable )
364    {
365        char * dir = tr_dirname( filename );
366        const int err = tr_mkdirp( dir, 0777 ) ? errno : 0;
367        if( err ) {
368            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) );
369            tr_free( dir );
370            return err;
371        }
372        tr_free( dir );
373    }
374
375    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
376
377    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_FULL ) )
378        if( preallocate_file_full( filename, file_size ) )
379            tr_dbg( "Preallocated file \"%s\"", filename );
380
381    /* open the file */
382    flags = writable ? ( O_RDWR | O_CREAT ) : O_RDONLY;
383    flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL;
384    o->fd = open( filename, flags, 0666 );
385
386    if( o->fd == -1 )
387    {
388        const int err = errno;
389        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
390        return err;
391    }
392
393    /* If the file already exists and it's too large, truncate it.
394     * This is a fringe case that happens if a torrent's been updated
395     * and one of the updated torrent's files is smaller.
396     * http://trac.transmissionbt.com/ticket/2228
397     * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
398     */
399    if( alreadyExisted && ( file_size < (uint64_t)sb.st_size ) )
400    {
401        if( ftruncate( o->fd, file_size ) == -1 )
402        {
403            const int err = errno;
404            tr_err( _( "Couldn't truncate \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
405            return err;
406        }
407    }
408
409    if( writable && !alreadyExisted && ( allocation == TR_PREALLOCATE_SPARSE ) )
410        preallocate_file_sparse( o->fd, file_size );
411
412    /* Many (most?) clients request blocks in ascending order,
413     * so increase the readahead buffer.
414     * Also, disable OS-level caching because "inactive memory" angers users. */
415    tr_set_file_for_single_pass( o->fd );
416
417    return 0;
418}
419
420/***
421****
422***/
423
424struct tr_fileset
425{
426    struct tr_cached_file * begin;
427    const struct tr_cached_file * end;
428};
429
430static void
431fileset_construct( struct tr_fileset * set, int n )
432{
433    struct tr_cached_file * o;
434    const struct tr_cached_file TR_CACHED_FILE_INIT = { 0, -1, 0, 0, 0 };
435
436    set->begin = tr_new( struct tr_cached_file, n );
437    set->end = set->begin + n;
438
439    for( o=set->begin; o!=set->end; ++o )
440        *o = TR_CACHED_FILE_INIT;
441}
442
443static void
444fileset_close_all( struct tr_fileset * set )
445{
446    struct tr_cached_file * o;
447
448    if( set != NULL )
449        for( o=set->begin; o!=set->end; ++o )
450            if( cached_file_is_open( o ) )
451                cached_file_close( o );
452}
453
454static void
455fileset_destruct( struct tr_fileset * set )
456{
457    fileset_close_all( set );
458    tr_free( set->begin );
459    set->end = set->begin = NULL;
460}
461
462static void
463fileset_close_torrent( struct tr_fileset * set, int torrent_id )
464{
465    struct tr_cached_file * o;
466
467    if( set != NULL )
468        for( o=set->begin; o!=set->end; ++o )
469            if( ( o->torrent_id == torrent_id ) && cached_file_is_open( o ) )
470                cached_file_close( o );
471}
472
473static struct tr_cached_file *
474fileset_lookup( struct tr_fileset * set, int torrent_id, tr_file_index_t i )
475{
476    struct tr_cached_file * o;
477
478    if( set != NULL )
479        for( o=set->begin; o!=set->end; ++o )
480            if( ( torrent_id == o->torrent_id ) && ( i == o->file_index ) && cached_file_is_open( o ) )
481                return o;
482
483    return NULL;
484}
485
486static struct tr_cached_file *
487fileset_get_empty_slot( struct tr_fileset * set )
488{
489    struct tr_cached_file * o;
490    struct tr_cached_file * cull;
491
492    /* try to find an unused slot */
493    for( o=set->begin; o!=set->end; ++o )
494        if( !cached_file_is_open( o ) )
495            return o;
496
497    /* all slots are full... recycle the least recently used */
498    for( cull=NULL, o=set->begin; o!=set->end; ++o )
499        if( !cull || o->used_at < cull->used_at )
500            cull = o;
501    cached_file_close( cull );
502    return cull;
503}
504
505static int
506fileset_get_size( const struct tr_fileset * set )
507{
508    return set ? set->end - set->begin : 0;
509}
510
511/***
512****
513***/
514
515struct tr_fdInfo
516{
517    int socket_count;
518    int socket_limit;
519    int public_socket_limit;
520    struct tr_fileset fileset;
521};
522
523static struct tr_fileset*
524get_fileset( tr_session * session )
525{
526    return session && session->fdInfo ? &session->fdInfo->fileset : NULL;
527}
528
529void
530tr_fdFileClose( tr_session * s, const tr_torrent * tor, tr_file_index_t i )
531{
532    struct tr_cached_file * o;
533
534    if(( o = fileset_lookup( get_fileset( s ), tr_torrentId( tor ), i )))
535    {
536        /* flush writable files so that their mtimes will be
537         * up-to-date when this function returns to the caller... */
538        if( o->is_writable )
539            tr_fsync( o->fd );
540
541        cached_file_close( o );
542    }
543}
544
545int
546tr_fdFileGetCached( tr_session * s, int torrent_id, tr_file_index_t i, tr_bool writable )
547{
548    struct tr_cached_file * o = fileset_lookup( get_fileset( s ), torrent_id, i );
549
550    if( !o || ( writable && !o->is_writable ) )
551        return -1;
552
553    o->used_at = tr_time( );
554    return o->fd;
555}
556
557void
558tr_fdTorrentClose( tr_session * session, int torrent_id )
559{
560    fileset_close_torrent( get_fileset( session ), torrent_id );
561}
562
563/* returns an fd on success, or a -1 on failure and sets errno */
564int
565tr_fdFileCheckout( tr_session             * session,
566                   int                      torrent_id,
567                   tr_file_index_t          i,
568                   const char             * filename,
569                   tr_bool                  writable,
570                   tr_preallocation_mode    allocation,
571                   uint64_t                 file_size )
572{
573    struct tr_fileset * set = get_fileset( session );
574    struct tr_cached_file * o = fileset_lookup( set, torrent_id, i );
575
576    if( o && writable && !o->is_writable )
577        cached_file_close( o ); /* close it so we can reopen in rw mode */
578    else if( !o )
579        o = fileset_get_empty_slot( set );
580
581    if( !cached_file_is_open( o ) )
582    {
583        const int err = cached_file_open( o, filename, writable, allocation, file_size );
584        if( err ) {
585            errno = err;
586            return -1;
587        }
588
589        dbgmsg( "opened '%s' writable %c", filename, writable?'y':'n' );
590        o->is_writable = writable;
591    }
592
593    dbgmsg( "checking out '%s'", filename );
594    o->torrent_id = torrent_id;
595    o->file_index = i;
596    o->used_at = tr_time( );
597    return o->fd;
598}
599
600/***
601****
602****  Sockets
603****
604***/
605
606int
607tr_fdSocketCreate( tr_session * session, int domain, int type )
608{
609    int s = -1;
610    struct tr_fdInfo * gFd;
611
612    assert( tr_isSession( session ) );
613    assert( session->fdInfo != NULL );
614
615    gFd = session->fdInfo;
616
617    if( gFd->socket_count < gFd->socket_limit )
618        if(( s = socket( domain, type, 0 )) < 0 )
619            if( sockerrno != EAFNOSUPPORT )
620                tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) );
621
622    if( s > -1 )
623        ++gFd->socket_count;
624
625    assert( gFd->socket_count >= 0 );
626
627    if( s >= 0 )
628    {
629        static tr_bool buf_logged = FALSE;
630        if( !buf_logged )
631        {
632            int i;
633            socklen_t size = sizeof( int );
634            buf_logged = TRUE;
635            getsockopt( s, SOL_SOCKET, SO_SNDBUF, &i, &size );
636            tr_dbg( "SO_SNDBUF size is %d", i );
637            getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size );
638            tr_dbg( "SO_RCVBUF size is %d", i );
639        }
640    }
641
642    return s;
643}
644
645int
646tr_fdSocketAccept( tr_session * s, int sockfd, tr_address * addr, tr_port * port )
647{
648    int fd;
649    unsigned int len;
650    struct tr_fdInfo * gFd;
651    struct sockaddr_storage sock;
652
653    assert( tr_isSession( s ) );
654    assert( s->fdInfo != NULL );
655    assert( addr );
656    assert( port );
657
658    gFd = s->fdInfo;
659
660    len = sizeof( struct sockaddr_storage );
661    fd = accept( sockfd, (struct sockaddr *) &sock, &len );
662
663    if( ( fd >= 0 ) && gFd->socket_count > gFd->socket_limit )
664    {
665        tr_netCloseSocket( fd );
666        fd = -1;
667    }
668
669    if( fd >= 0 )
670    {
671        /* "The ss_family field of the sockaddr_storage structure will always
672         * align with the family field of any protocol-specific structure." */
673        if( sock.ss_family == AF_INET )
674        {
675            struct sockaddr_in *si;
676            union { struct sockaddr_storage dummy; struct sockaddr_in si; } s;
677            s.dummy = sock;
678            si = &s.si;
679            addr->type = TR_AF_INET;
680            addr->addr.addr4.s_addr = si->sin_addr.s_addr;
681            *port = si->sin_port;
682        }
683        else
684        {
685            struct sockaddr_in6 *si;
686            union { struct sockaddr_storage dummy; struct sockaddr_in6 si; } s;
687            s.dummy = sock;
688            si = &s.si;
689            addr->type = TR_AF_INET6;
690            addr->addr.addr6 = si->sin6_addr;
691            *port = si->sin6_port;
692        }
693        ++gFd->socket_count;
694    }
695
696    return fd;
697}
698
699void
700tr_fdSocketClose( tr_session * session, int fd )
701{
702    assert( tr_isSession( session ) );
703
704    if( session->fdInfo != NULL )
705    {
706        struct tr_fdInfo * gFd = session->fdInfo;
707
708        if( fd >= 0 )
709        {
710            tr_netCloseSocket( fd );
711            --gFd->socket_count;
712        }
713
714        assert( gFd->socket_count >= 0 );
715    }
716}
717
718/***
719****
720****  Startup / Shutdown
721****
722***/
723
724static void
725ensureSessionFdInfoExists( tr_session * session )
726{
727    assert( tr_isSession( session ) );
728
729    if( session->fdInfo == NULL )
730        session->fdInfo = tr_new0( struct tr_fdInfo, 1 );
731}
732
733void
734tr_fdClose( tr_session * session )
735{
736    struct tr_fdInfo * gFd = session->fdInfo;
737
738    if( gFd != NULL )
739    {
740        fileset_destruct( &gFd->fileset );
741        tr_free( gFd );
742    }
743
744    session->fdInfo = NULL;
745}
746
747/***
748****
749***/
750
751int
752tr_fdGetFileLimit( tr_session * session )
753{
754    return fileset_get_size( get_fileset( session ) );
755}
756
757void
758tr_fdSetFileLimit( tr_session * session, int limit )
759{
760    ensureSessionFdInfoExists( session );
761
762    if( limit != tr_fdGetFileLimit( session ) )
763    {
764        struct tr_fileset * set = get_fileset( session );
765        fileset_destruct( set );
766        fileset_construct( set, limit );
767    }
768}
769
770void
771tr_fdSetPeerLimit( tr_session * session, int socket_limit )
772{
773    struct tr_fdInfo * gFd;
774
775    ensureSessionFdInfoExists( session );
776
777    gFd = session->fdInfo;
778
779#ifdef HAVE_GETRLIMIT
780    {
781        struct rlimit rlim;
782        const int NOFILE_BUFFER = 512;
783        const int open_max = sysconf( _SC_OPEN_MAX );
784        getrlimit( RLIMIT_NOFILE, &rlim );
785        rlim.rlim_cur = MAX( 1024, open_max );
786        rlim.rlim_cur = MIN( rlim.rlim_cur, rlim.rlim_max );
787        setrlimit( RLIMIT_NOFILE, &rlim );
788        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
789        gFd->socket_limit = MIN( socket_limit, (int)rlim.rlim_cur - NOFILE_BUFFER );
790    }
791#else
792    gFd->socket_limit = socket_limit;
793#endif
794    gFd->public_socket_limit = socket_limit;
795
796    tr_dbg( "socket limit is %d", gFd->socket_limit );
797}
798
799int
800tr_fdGetPeerLimit( const tr_session * session )
801{
802    return session && session->fdInfo ? session->fdInfo->public_socket_limit : -1;
803}
Note: See TracBrowser for help on using the repository browser.