source: trunk/libtransmission/fdlimit.c @ 7127

Last change on this file since 7127 was 7127, checked in by charles, 14 years ago

(libt) #1482: libtransmission/fdlimit.c preallocateFile uses undeclared desiredFileSize on WIN32

  • Property svn:keywords set to Date Rev Author Id
File size: 12.9 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 7127 2008-11-19 16:06:31Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <errno.h>
27#include <inttypes.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#ifdef SYS_DARWIN
32#include <fcntl.h>
33#endif
34
35#ifdef HAVE_FALLOCATE
36 #include <linux/falloc.h>
37#endif
38
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <unistd.h>
42#include <fcntl.h> /* O_LARGEFILE */
43
44#include <event.h>
45#include <evutil.h>
46
47#include "transmission.h"
48#include "fdlimit.h"
49#include "list.h"
50#include "net.h"
51#include "platform.h" /* tr_lock */
52#include "utils.h"
53
54#define dbgmsg( ... ) \
55    do { \
56        if( tr_deepLoggingIsActive( ) ) \
57            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
58    } while( 0 )
59
60/**
61***
62**/
63
64enum
65{
66    TR_MAX_OPEN_FILES = 16, /* real files, not sockets */
67
68    NOFILE_BUFFER = 512, /* the process' number of open files is
69                            globalMaxPeers + NOFILE_BUFFER */
70};
71
72struct tr_openfile
73{
74    unsigned int    isCheckedOut  : 1;
75    unsigned int    isWritable    : 1;
76    unsigned int    closeWhenDone : 1;
77    char            filename[MAX_PATH_LENGTH];
78    int             fd;
79    uint64_t        date;
80};
81
82struct tr_fd_s
83{
84    int                   socketCount;
85    int                   socketMax;
86    tr_lock *             lock;
87    struct tr_openfile    open[TR_MAX_OPEN_FILES];
88};
89
90static struct tr_fd_s * gFd = NULL;
91
92/***
93****
94****  Local Files
95****
96***/
97
98#ifndef O_LARGEFILE
99#define O_LARGEFILE 0
100#endif
101
102static int
103preallocateFile( const char * filename, uint64_t length )
104{
105    int success = 0;
106
107#ifdef WIN32
108
109    HANDLE hFile = CreateFile( filename, GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0 );
110    if( hFile != INVALID_HANDLE_VALUE )
111    {
112        LARGE_INTEGER li;
113        li.QuadPart = length;
114        success = SetFilePointerEx( hFile, length, NULL, FILE_BEGIN )
115               && SetEndOfFile( hFile );
116        CloseHandle( hFile );
117    }
118
119#else
120
121    int flags = O_RDWR | O_CREAT | O_LARGEFILE;
122    int fd = open( filename, flags, 0666 );
123    if( fd >= 0 )
124    {
125       
126# ifdef HAVE_FALLOCATE
127
128        success = !fallocate( fd, FALLOC_FL_KEEP_SIZE, 0, length );
129
130# elif defined(HAVE_POSIX_FALLOCATE)
131
132        success = !posix_fallocate( fd, 0, length );
133
134# elif defined(SYS_DARWIN)
135
136        fstore_t fst;
137        fst.fst_flags = F_ALLOCATECONTIG;
138        fst.fst_posmode = F_PEOFPOSMODE;
139        fst.fst_offset = 0;
140        fst.fst_length = length;
141        fst.fst_bytesalloc = 0;
142        success = !fcntl( fd, F_PREALLOCATE, &fst );
143
144# else
145
146        #warning no known method to preallocate files on this platform
147        success = 0;
148
149# endif
150
151        close( fd );
152    }
153
154#endif
155
156    return success;
157}
158
159/**
160 * returns 0 on success, or an errno value on failure.
161 * errno values include ENOENT if the parent folder doesn't exist,
162 * plus the errno values set by tr_mkdirp() and open().
163 */
164static int
165TrOpenFile( int          i,
166            const char * folder,
167            const char * torrentFile,
168            int          doWrite,
169            int          doPreallocate,
170            uint64_t     desiredFileSize )
171{
172    struct tr_openfile * file = &gFd->open[i];
173    int                  flags;
174    char               * filename;
175    struct stat          sb;
176    int                  alreadyExisted;
177
178    /* confirm the parent folder exists */
179    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
180        return ENOENT;
181
182    /* create subfolders, if any */
183    filename = tr_buildPath( folder, torrentFile, NULL );
184    if( doWrite )
185    {
186        char * tmp = tr_dirname( filename );
187        const int err = tr_mkdirp( tmp, 0777 ) ? errno : 0;
188        tr_free( tmp );
189        if( err ) {
190            tr_free( filename );
191            return err;
192        }
193    }
194
195    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
196
197    if( doWrite && !alreadyExisted && doPreallocate )
198        if( preallocateFile( filename, desiredFileSize ) )
199            tr_inf( _( "Preallocated file \"%s\"" ), filename );
200   
201    /* open the file */
202    flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY;
203#ifdef O_LARGEFILE
204    flags |= O_LARGEFILE;
205#endif
206#ifdef WIN32
207    flags |= O_BINARY;
208#endif
209    file->fd = open( filename, flags, 0666 );
210    if( file->fd == -1 )
211    {
212        const int err = errno;
213        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename,
214               tr_strerror( err ) );
215        tr_free( filename );
216        return err;
217    }
218
219    tr_free( filename );
220    return 0;
221}
222
223static int
224fileIsOpen( const struct tr_openfile * o )
225{
226    return o->fd >= 0;
227}
228
229static void
230TrCloseFile( int i )
231{
232    struct tr_openfile * o = &gFd->open[i];
233
234    assert( i >= 0 );
235    assert( i < TR_MAX_OPEN_FILES );
236    assert( fileIsOpen( o ) );
237
238    close( o->fd );
239    o->fd = -1;
240    o->isCheckedOut = 0;
241}
242
243static int
244fileIsCheckedOut( const struct tr_openfile * o )
245{
246    return fileIsOpen( o ) && o->isCheckedOut;
247}
248
249/* returns an fd on success, or a -1 on failure and sets errno */
250int
251tr_fdFileCheckout( const char * folder,
252                   const char * torrentFile,
253                   int          doWrite,
254                   int          doPreallocate,
255                   uint64_t     desiredFileSize )
256{
257    int                  i, winner = -1;
258    struct tr_openfile * o;
259    char               * filename;
260
261    assert( folder && *folder );
262    assert( torrentFile && *torrentFile );
263    assert( doWrite == 0 || doWrite == 1 );
264
265    filename = tr_buildPath( folder, torrentFile, NULL );
266    dbgmsg( "looking for file '%s', writable %c", filename,
267            doWrite ? 'y' : 'n' );
268
269    tr_lockLock( gFd->lock );
270
271    /* Is it already open? */
272    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
273    {
274        o = &gFd->open[i];
275
276        if( !fileIsOpen( o ) )
277            continue;
278
279        if( strcmp( filename, o->filename ) )
280            continue;
281
282        if( fileIsCheckedOut( o ) )
283        {
284            dbgmsg( "found it!  it's open, but checked out.  waiting..." );
285            tr_lockUnlock( gFd->lock );
286            tr_wait( 200 );
287            tr_lockLock( gFd->lock );
288            i = -1; /* reloop */
289            continue;
290        }
291
292        if( doWrite && !o->isWritable )
293        {
294            dbgmsg(
295                "found it!  it's open and available, but isn't writable. closing..." );
296            TrCloseFile( i );
297            break;
298        }
299
300        dbgmsg( "found it!  it's ready for use!" );
301        winner = i;
302        break;
303    }
304
305    dbgmsg(
306        "it's not already open.  looking for an open slot or an old file." );
307    while( winner < 0 )
308    {
309        uint64_t date = tr_date( ) + 1;
310
311        /* look for the file that's been open longest */
312        for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
313        {
314            o = &gFd->open[i];
315
316            if( !fileIsOpen( o ) )
317            {
318                winner = i;
319                dbgmsg( "found an empty slot in %d", winner );
320                break;
321            }
322
323            if( date > o->date )
324            {
325                date = o->date;
326                winner = i;
327            }
328        }
329
330        if( winner >= 0 )
331        {
332            if( fileIsOpen( &gFd->open[winner] ) )
333            {
334                dbgmsg( "closing file '%s', slot #%d",
335                        gFd->open[winner].filename,
336                        winner );
337                TrCloseFile( winner );
338            }
339        }
340        else
341        {
342            dbgmsg(
343                "everything's full!  waiting for someone else to finish something" );
344            tr_lockUnlock( gFd->lock );
345            tr_wait( 200 );
346            tr_lockLock( gFd->lock );
347        }
348    }
349
350    assert( winner >= 0 );
351    o = &gFd->open[winner];
352    if( !fileIsOpen( o ) )
353    {
354        const int err = TrOpenFile( winner, folder, torrentFile, doWrite, doPreallocate, desiredFileSize );
355        if( err ) {
356            tr_lockUnlock( gFd->lock );
357            tr_free( filename );
358            errno = err;
359            return -1;
360        }
361
362        dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner,
363                doWrite ? 'y' : 'n' );
364        tr_strlcpy( o->filename, filename, sizeof( o->filename ) );
365        o->isWritable = doWrite;
366    }
367
368    dbgmsg( "checking out '%s' in slot %d", filename, winner );
369    o->isCheckedOut = 1;
370    o->closeWhenDone = 0;
371    o->date = tr_date( );
372    tr_free( filename );
373    tr_lockUnlock( gFd->lock );
374    return o->fd;
375}
376
377void
378tr_fdFileReturn( int fd )
379{
380    int i;
381
382    tr_lockLock( gFd->lock );
383
384    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
385    {
386        struct tr_openfile * o = &gFd->open[i];
387        if( o->fd != fd )
388            continue;
389
390        dbgmsg( "releasing file '%s' in slot #%d", o->filename, i );
391        o->isCheckedOut = 0;
392        if( o->closeWhenDone )
393            TrCloseFile( i );
394
395        break;
396    }
397
398    tr_lockUnlock( gFd->lock );
399}
400
401void
402tr_fdFileClose( const char * filename )
403{
404    int i;
405
406    tr_lockLock( gFd->lock );
407
408    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
409    {
410        struct tr_openfile * o = &gFd->open[i];
411        if( !fileIsOpen( o ) || strcmp( filename, o->filename ) )
412            continue;
413
414        dbgmsg( "tr_fdFileClose closing '%s'", filename );
415
416        if( !o->isCheckedOut )
417        {
418            dbgmsg( "not checked out, so closing it now... '%s'", filename );
419            TrCloseFile( i );
420        }
421        else
422        {
423            dbgmsg(
424                "flagging file '%s', slot #%d to be closed when checked in",
425                gFd->open[i].filename, i );
426            o->closeWhenDone = 1;
427        }
428    }
429
430    tr_lockUnlock( gFd->lock );
431}
432
433/***
434****
435****  Sockets
436****
437***/
438
439static int
440getSocketMax( struct tr_fd_s * gFd )
441{
442    return gFd->socketMax;
443}
444
445int
446tr_fdSocketCreate( int type )
447{
448    int s = -1;
449
450    tr_lockLock( gFd->lock );
451
452    if( gFd->socketCount < getSocketMax( gFd ) )
453        if( ( s = socket( AF_INET, type, 0 ) ) < 0 )
454            tr_err( _( "Couldn't create socket: %s" ),
455                   tr_strerror( sockerrno ) );
456
457    if( s > -1 )
458        ++gFd->socketCount;
459
460    assert( gFd->socketCount >= 0 );
461
462    tr_lockUnlock( gFd->lock );
463    return s;
464}
465
466int
467tr_fdSocketAccept( int              b,
468                   struct in_addr * addr,
469                   tr_port_t *      port )
470{
471    int                s = -1;
472    unsigned int       len;
473    struct sockaddr_in sock;
474
475    assert( addr );
476    assert( port );
477
478    tr_lockLock( gFd->lock );
479    if( gFd->socketCount < getSocketMax( gFd ) )
480    {
481        len = sizeof( sock );
482        s = accept( b, (struct sockaddr *) &sock, &len );
483    }
484    if( s > -1 )
485    {
486        *addr = sock.sin_addr;
487        *port = sock.sin_port;
488        ++gFd->socketCount;
489    }
490    tr_lockUnlock( gFd->lock );
491
492    return s;
493}
494
495static void
496socketClose( int fd )
497{
498#ifdef BEOS_NETSERVER
499    closesocket( fd );
500#else
501    EVUTIL_CLOSESOCKET( fd );
502#endif
503}
504
505void
506tr_fdSocketClose( int s )
507{
508    tr_lockLock( gFd->lock );
509
510    if( s >= 0 )
511    {
512        socketClose( s );
513        --gFd->socketCount;
514    }
515
516    assert( gFd->socketCount >= 0 );
517
518    tr_lockUnlock( gFd->lock );
519}
520
521/***
522****
523****  Startup / Shutdown
524****
525***/
526
527void
528tr_fdInit( int globalPeerLimit )
529{
530    int i;
531
532    assert( gFd == NULL );
533    gFd = tr_new0( struct tr_fd_s, 1 );
534    gFd->lock = tr_lockNew( );
535    gFd->socketMax = globalPeerLimit;
536    tr_dbg( "%d usable file descriptors", globalPeerLimit );
537
538    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
539        gFd->open[i].fd = -1;
540}
541
542void
543tr_fdClose( void )
544{
545    int i = 0;
546
547    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
548        if( fileIsOpen( &gFd->open[i] ) )
549            TrCloseFile( i );
550
551    tr_lockFree( gFd->lock );
552
553    tr_free( gFd );
554    gFd = NULL;
555}
556
557void
558tr_fdSetPeerLimit( uint16_t n )
559{
560    assert( gFd != NULL && "tr_fdInit() must be called first!" );
561    gFd->socketMax = n;
562}
563
564uint16_t
565tr_fdGetPeerLimit( void )
566{
567    return gFd ? gFd->socketMax : -1;
568}
569
Note: See TracBrowser for help on using the repository browser.