33
33
#include <stdlib.h>
34
34
#include <string.h>
36
39
#include <sys/types.h>
37
40
#include <sys/stat.h>
38
41
#ifdef HAVE_GETRLIMIT
39
#include <sys/time.h> /* getrlimit */
40
#include <sys/resource.h> /* getrlimit */
42
#include <sys/time.h> /* getrlimit */
43
#include <sys/resource.h> /* getrlimit */
42
45
#include <unistd.h>
43
#include <libgen.h> /* dirname */
44
46
#include <fcntl.h> /* O_LARGEFILE */
53
55
#include "platform.h" /* tr_lock */
57
#define TR_UINT_TO_PTR(i) (void*)((uint64_t)i)
59
#define TR_UINT_TO_PTR(i) ((void*)((uint32_t)i))
62
#define dbgmsg(fmt...) tr_deepLog( __FILE__, __LINE__, NULL, ##fmt )
58
#define dbgmsg( ... ) \
60
if( tr_deepLoggingIsActive( ) ) \
61
tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
78
unsigned int isCheckedOut : 1;
79
unsigned int isWritable : 1;
80
unsigned int closeWhenDone : 1;
81
char filename[MAX_PATH_LENGTH];
78
unsigned int isCheckedOut : 1;
79
unsigned int isWritable : 1;
80
unsigned int closeWhenDone : 1;
81
char filename[MAX_PATH_LENGTH];
92
struct tr_openfile open[TR_MAX_OPEN_FILES];
91
struct tr_openfile open[TR_MAX_OPEN_FILES];
95
94
static struct tr_fd_s * gFd = NULL;
106
const char * torrentFile,
103
preallocateFile( int fd UNUSED, uint64_t length UNUSED )
105
#ifdef HAVE_FALLOCATE
107
return fallocate( fd, 0, offset, length );
109
#elif defined(HAVE_POSIX_FALLOCATE)
111
return posix_fallocate( fd, 0, length );
113
#elif defined(SYS_DARWIN)
116
fst.fst_flags = F_ALLOCATECONTIG;
117
fst.fst_posmode = F_PEOFPOSMODE;
119
fst.fst_length = length;
120
fst.fst_bytesalloc = 0;
121
return fcntl( fd, F_PREALLOCATE, &fst );
125
#warning no known method to preallocate files on this platform
132
* returns 0 on success, or an errno value on failure.
133
* errno values include ENOENT if the parent folder doesn't exist,
134
* plus the errno values set by tr_mkdirp() and open().
139
const char * torrentFile,
142
uint64_t desiredFileSize )
109
144
struct tr_openfile * file = &gFd->open[i];
111
char filename[MAX_PATH_LENGTH];
114
150
/* confirm the parent folder exists */
115
151
if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
116
return TR_ERROR_IO_PARENT;
118
154
/* create subfolders, if any */
119
tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL );
121
char * tmp = tr_strdup( filename );
122
const int err = tr_mkdirp( dirname(tmp), 0777 ) ? errno : 0;
155
filename = tr_buildPath( folder, torrentFile, NULL );
158
char * tmp = tr_dirname( filename );
159
const int err = tr_mkdirp( tmp, 0777 ) ? errno : 0;
125
return tr_ioErrorFromErrno( err );
167
alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
128
169
/* open the file */
129
flags = write ? (O_RDWR | O_CREAT) : O_RDONLY;
170
flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY;
130
171
#ifdef O_LARGEFILE
131
172
flags |= O_LARGEFILE;
134
175
flags |= O_BINARY;
136
177
file->fd = open( filename, flags, 0666 );
137
if( file->fd == -1 ) {
138
180
const int err = errno;
139
tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror(err) );
140
return tr_ioErrorFromErrno( err );
181
tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename,
182
tr_strerror( err ) );
187
if( ( file->fd >= 0 ) && !alreadyExisted && doPreallocate )
188
if( !preallocateFile( file->fd, desiredFileSize ) )
189
tr_inf( _( "Preallocated file \"%s\"" ), filename );
167
216
fileIsCheckedOut( const struct tr_openfile * o )
169
return fileIsOpen(o) && o->isCheckedOut;
218
return fileIsOpen( o ) && o->isCheckedOut;
221
/* returns an fd on success, or a -1 on failure and sets errno */
173
223
tr_fdFileCheckout( const char * folder,
174
const char * torrentFile,
224
const char * torrentFile,
227
uint64_t desiredFileSize )
178
230
struct tr_openfile * o;
179
char filename[MAX_PATH_LENGTH];
181
233
assert( folder && *folder );
182
234
assert( torrentFile && *torrentFile );
183
assert( write==0 || write==1 );
235
assert( doWrite == 0 || doWrite == 1 );
185
tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL );
186
dbgmsg( "looking for file '%s', writable %c", filename, write?'y':'n' );
237
filename = tr_buildPath( folder, torrentFile, NULL );
238
dbgmsg( "looking for file '%s', writable %c", filename,
239
doWrite ? 'y' : 'n' );
188
241
tr_lockLock( gFd->lock );
190
243
/* Is it already open? */
191
for( i=0; i<TR_MAX_OPEN_FILES; ++i )
244
for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
193
246
o = &gFd->open[i];
198
251
if( strcmp( filename, o->filename ) )
201
if( fileIsCheckedOut( o ) ) {
254
if( fileIsCheckedOut( o ) )
202
256
dbgmsg( "found it! it's open, but checked out. waiting..." );
203
257
tr_lockUnlock( gFd->lock );
210
if( write && !o->isWritable ) {
211
dbgmsg( "found it! it's open and available, but isn't writable. closing..." );
264
if( doWrite && !o->isWritable )
267
"found it! it's open and available, but isn't writable. closing..." );
212
268
TrCloseFile( i );
221
dbgmsg( "it's not already open. looking for an open slot or an old file." );
278
"it's not already open. looking for an open slot or an old file." );
222
279
while( winner < 0 )
224
281
uint64_t date = tr_date( ) + 1;
226
283
/* look for the file that's been open longest */
227
for( i=0; i<TR_MAX_OPEN_FILES; ++i )
284
for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
229
286
o = &gFd->open[i];
231
if( !fileIsOpen( o ) ) {
288
if( !fileIsOpen( o ) )
233
291
dbgmsg( "found an empty slot in %d", winner );
237
if( date > o->date ) {
244
if( fileIsOpen( &gFd->open[winner] ) ) {
245
dbgmsg( "closing file '%s', slot #%d", gFd->open[winner].filename, winner );
304
if( fileIsOpen( &gFd->open[winner] ) )
306
dbgmsg( "closing file '%s', slot #%d",
307
gFd->open[winner].filename,
246
309
TrCloseFile( winner );
249
dbgmsg( "everything's full! waiting for someone else to finish something" );
315
"everything's full! waiting for someone else to finish something" );
250
316
tr_lockUnlock( gFd->lock );
252
318
tr_lockLock( gFd->lock );
257
323
o = &gFd->open[winner];
258
324
if( !fileIsOpen( o ) )
260
const tr_errno err = TrOpenFile( winner, folder, torrentFile, write );
326
const int err = TrOpenFile( winner, folder, torrentFile, doWrite, doPreallocate, desiredFileSize );
262
328
tr_lockUnlock( gFd->lock );
266
dbgmsg( "opened '%s' in slot %d, write %c", filename, winner, write?'y':'n' );
334
dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner,
335
doWrite ? 'y' : 'n' );
267
336
tr_strlcpy( o->filename, filename, sizeof( o->filename ) );
268
o->isWritable = write;
337
o->isWritable = doWrite;
271
340
dbgmsg( "checking out '%s' in slot %d", filename, winner );
272
341
o->isCheckedOut = 1;
273
342
o->closeWhenDone = 0;
274
343
o->date = tr_date( );
275
345
tr_lockUnlock( gFd->lock );
303
374
tr_fdFileClose( const char * filename )
306
378
tr_lockLock( gFd->lock );
308
for( i=0; i<TR_MAX_OPEN_FILES; ++i )
380
for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
310
382
struct tr_openfile * o = &gFd->open[i];
311
if( !fileIsOpen(o) || strcmp(filename,o->filename) )
383
if( !fileIsOpen( o ) || strcmp( filename, o->filename ) )
314
386
dbgmsg( "tr_fdFileClose closing '%s'", filename );
316
if( !o->isCheckedOut ) {
388
if( !o->isCheckedOut )
317
390
dbgmsg( "not checked out, so closing it now... '%s'", filename );
318
391
TrCloseFile( i );
320
dbgmsg( "flagging file '%s', slot #%d to be closed when checked in", gFd->open[i].filename, i );
396
"flagging file '%s', slot #%d to be closed when checked in",
397
gFd->open[i].filename, i );
321
398
o->closeWhenDone = 1;
325
402
tr_lockUnlock( gFd->lock );
334
static tr_list * reservedSockets = NULL;
337
setSocketPriority( int fd, int isReserved )
340
tr_list_append( &reservedSockets, TR_UINT_TO_PTR(fd) );
344
socketWasReserved( int fd )
346
return tr_list_remove_data( &reservedSockets, TR_UINT_TO_PTR(fd) ) != NULL;
350
412
getSocketMax( struct tr_fd_s * gFd )
352
return gFd->normalMax;
414
return gFd->socketMax;
356
tr_fdSocketCreate( int type, int isReserved )
418
tr_fdSocketCreate( int type )
359
422
tr_lockLock( gFd->lock );
361
if( isReserved || ( gFd->normal < getSocketMax( gFd ) ) )
424
if( gFd->socketCount < getSocketMax( gFd ) )
362
425
if( ( s = socket( AF_INET, type, 0 ) ) < 0 )
363
tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) );
426
tr_err( _( "Couldn't create socket: %s" ),
427
tr_strerror( sockerrno ) );
367
setSocketPriority( s, isReserved );
375
assert( gFd->reserved >= 0 );
376
assert( gFd->normal >= 0 );
432
assert( gFd->socketCount >= 0 );
378
434
tr_lockUnlock( gFd->lock );
383
tr_fdSocketAccept( int b, struct in_addr * addr, tr_port_t * port )
439
tr_fdSocketAccept( int b,
440
struct in_addr * addr,
387
445
struct sockaddr_in sock;
392
450
tr_lockLock( gFd->lock );
393
if( gFd->normal < getSocketMax( gFd ) )
451
if( gFd->socketCount < getSocketMax( gFd ) )
395
453
len = sizeof( sock );
396
454
s = accept( b, (struct sockaddr *) &sock, &len );
400
setSocketPriority( s, FALSE );
401
458
*addr = sock.sin_addr;
402
459
*port = sock.sin_port;
405
462
tr_lockUnlock( gFd->lock );
423
480
tr_lockLock( gFd->lock );
426
484
socketClose( s );
427
if( socketWasReserved( s ) )
433
assert( gFd->reserved >= 0 );
434
assert( gFd->normal >= 0 );
488
assert( gFd->socketCount >= 0 );
436
490
tr_lockUnlock( gFd->lock );
456
510
struct rlimit rlim;
457
511
getrlimit( RLIMIT_NOFILE, &rlim );
458
512
rlim.rlim_cur = MIN( rlim.rlim_max,
459
(rlim_t)(globalPeerLimit + NOFILE_BUFFER) );
513
(rlim_t)( globalPeerLimit + NOFILE_BUFFER ) );
460
514
setrlimit( RLIMIT_NOFILE, &rlim );
461
gFd->normalMax = rlim.rlim_cur - NOFILE_BUFFER;
515
gFd->socketMax = rlim.rlim_cur - NOFILE_BUFFER;
462
516
tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
465
gFd->normalMax = globalPeerLimit;
519
gFd->socketMax = globalPeerLimit;
467
521
tr_dbg( "%d usable file descriptors", globalPeerLimit );
469
for( i=0; i<TR_MAX_OPEN_FILES; ++i )
523
for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
470
524
gFd->open[i].fd = -1;
478
for( i=0; i<TR_MAX_OPEN_FILES; ++i )
532
for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
479
533
if( fileIsOpen( &gFd->open[i] ) )
480
534
TrCloseFile( i );
482
536
tr_lockFree( gFd->lock );
484
tr_list_free( &reservedSockets, NULL );
489
543
tr_fdSetPeerLimit( uint16_t n )
491
assert( gFd!=NULL && "tr_fdInit() must be called first!" );
545
assert( gFd != NULL && "tr_fdInit() must be called first!" );
496
550
tr_fdGetPeerLimit( void )
498
return gFd ? gFd->normalMax : -1;
552
return gFd ? gFd->socketMax : -1;