2 * This file Copyright (C) 2010 Mnemosyne LLC
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.
10 * $Id: fdlimit.c 11398 2010-11-11 15:31:11Z charles $
14 #define HAVE_GETRLIMIT
17 #ifdef HAVE_POSIX_FADVISE
21 #define _XOPEN_SOURCE 600
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
);
43 #include <sys/types.h>
46 #include <sys/time.h> /* getrlimit */
47 #include <sys/resource.h> /* getrlimit */
49 #include <fcntl.h> /* O_LARGEFILE posix_fadvise */
52 #include "transmission.h"
55 #include "platform.h" /* TR_PATH_MAX, TR_PATH_DELIMITER */
57 #include "torrent.h" /* tr_isTorrent() */
60 #define dbgmsg( ... ) \
62 if( tr_deepLoggingIsActive( ) ) \
63 tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
74 tr_file_index_t fileNum
;
75 char filename
[TR_PATH_MAX
];
84 int publicSocketLimit
;
86 struct tr_openfile
* openFiles
;
100 preallocateFileSparse( int fd
, uint64_t length
)
102 const char zero
= '\0';
108 #ifdef HAVE_FALLOCATE64
109 if( !success
) /* fallocate64 is always preferred, so try it first */
110 success
= !fallocate64( fd
, 0, 0, length
);
113 if( !success
) /* fallback: the old-style seek-and-write */
114 success
= ( lseek( fd
, length
-1, SEEK_SET
) != -1 )
115 && ( write( fd
, &zero
, 1 ) != -1 )
116 && ( ftruncate( fd
, length
) != -1 );
122 preallocateFileFull( const char * filename
, uint64_t length
)
128 HANDLE hFile
= CreateFile( filename
, GENERIC_WRITE
, 0, 0, CREATE_NEW
, FILE_FLAG_RANDOM_ACCESS
, 0 );
129 if( hFile
!= INVALID_HANDLE_VALUE
)
132 li
.QuadPart
= length
;
133 success
= SetFilePointerEx( hFile
, li
, NULL
, FILE_BEGIN
) && SetEndOfFile( hFile
);
134 CloseHandle( hFile
);
139 int flags
= O_RDWR
| O_CREAT
| O_LARGEFILE
;
140 int fd
= open( filename
, flags
, 0666 );
143 # ifdef HAVE_FALLOCATE64
146 success
= !fallocate64( fd
, 0, 0, length
);
149 # ifdef HAVE_XFS_XFS_H
150 if( !success
&& platform_test_xfs_fd( fd
) )
156 success
= !xfsctl( NULL
, fd
, XFS_IOC_RESVSP64
, &fl
);
163 fst
.fst_flags
= F_ALLOCATECONTIG
;
164 fst
.fst_posmode
= F_PEOFPOSMODE
;
166 fst
.fst_length
= length
;
167 fst
.fst_bytesalloc
= 0;
168 success
= !fcntl( fd
, F_PREALLOCATE
, &fst
);
171 # ifdef HAVE_POSIX_FALLOCATE
174 success
= !posix_fallocate( fd
, 0, length
);
178 if( !success
) /* if nothing else works, do it the old-fashioned way */
181 memset( buf
, 0, sizeof( buf
) );
183 while ( success
&& ( length
> 0 ) )
185 const int thisPass
= MIN( length
, sizeof( buf
) );
186 success
= write( fd
, buf
, thisPass
) == thisPass
;
199 /* Like pread and pwrite, except that the position is undefined afterwards.
200 And of course they are not thread-safe. */
208 tr_pread( int fd
, void *buf
, size_t count
, off_t offset
)
211 return pread( fd
, buf
, count
, offset
);
213 const off_t lrc
= lseek( fd
, offset
, SEEK_SET
);
216 return read( fd
, buf
, count
);
221 tr_pwrite( int fd
, const void *buf
, size_t count
, off_t offset
)
224 return pwrite( fd
, buf
, count
, offset
);
226 const off_t lrc
= lseek( fd
, offset
, SEEK_SET
);
229 return write( fd
, buf
, count
);
234 tr_prefetch( int fd UNUSED
, off_t offset UNUSED
, size_t count UNUSED
)
236 #ifdef HAVE_POSIX_FADVISE
237 return posix_fadvise( fd
, offset
, count
, POSIX_FADV_WILLNEED
);
238 #elif defined(SYS_DARWIN)
239 struct radvisory radv
;
240 radv
.ra_offset
= offset
;
241 radv
.ra_count
= count
;
242 return fcntl( fd
, F_RDADVISE
, &radv
);
249 tr_open_file_for_writing( const char * filename
)
251 int flags
= O_WRONLY
| O_CREAT
;
256 flags
|= O_LARGEFILE
;
258 return open( filename
, flags
, 0666 );
262 tr_open_file_for_scanning( const char * filename
)
267 /* build the flags */
270 flags
|= O_SEQUENTIAL
;
276 flags
|= O_LARGEFILE
;
280 fd
= open( filename
, flags
, 0666 );
283 /* Set hints about the lookahead buffer and caching. It's okay
284 for these to fail silently, so don't let them affect errno */
285 const int err
= errno
;
286 #ifdef HAVE_POSIX_FADVISE
287 posix_fadvise( fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
290 fcntl( fd
, F_NOCACHE
, 1 );
291 fcntl( fd
, F_RDAHEAD
, 1 );
300 tr_close_file( int fd
)
302 #if defined(HAVE_POSIX_FADVISE)
303 /* Set hint about not caching this file.
304 It's okay for this to fail silently, so don't let it affect errno */
305 const int err
= errno
;
306 posix_fadvise( fd
, 0, 0, POSIX_FADV_DONTNEED
);
310 /* it's unclear to me from the man pages if this actually flushes out the cache,
311 * but it couldn't hurt... */
312 fcntl( fd
, F_NOCACHE
, 1 );
318 * returns 0 on success, or an errno value on failure.
319 * errno values include ENOENT if the parent folder doesn't exist,
320 * plus the errno values set by tr_mkdirp() and open().
323 TrOpenFile( tr_session
* session
,
325 const char * filename
,
327 tr_preallocation_mode preallocationMode
,
328 uint64_t desiredFileSize
)
332 tr_bool alreadyExisted
;
333 struct tr_openfile
* file
;
335 assert( tr_isSession( session
) );
336 assert( session
->fdInfo
!= NULL
);
338 file
= &session
->fdInfo
->openFiles
[i
];
340 /* create subfolders, if any */
343 char * dir
= tr_dirname( filename
);
344 const int err
= tr_mkdirp( dir
, 0777 ) ? errno
: 0;
346 tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir
, tr_strerror( err
) );
353 alreadyExisted
= !stat( filename
, &sb
) && S_ISREG( sb
.st_mode
);
355 if( doWrite
&& !alreadyExisted
&& ( preallocationMode
== TR_PREALLOCATE_FULL
) )
356 if( preallocateFileFull( filename
, desiredFileSize
) )
357 tr_dbg( _( "Preallocated file \"%s\"" ), filename
);
360 flags
= doWrite
? ( O_RDWR
| O_CREAT
) : O_RDONLY
;
362 flags
|= O_SEQUENTIAL
;
365 flags
|= O_LARGEFILE
;
370 file
->fd
= open( filename
, flags
, 0666 );
373 const int err
= errno
;
374 tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename
, tr_strerror( err
) );
378 /* If the file already exists and it's too large, truncate it.
379 * This is a fringe case that happens if a torrent's been updated
380 * and one of the updated torrent's files is smaller.
381 * http://trac.transmissionbt.com/ticket/2228
382 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
384 if( alreadyExisted
&& ( desiredFileSize
< (uint64_t)sb
.st_size
) )
385 ftruncate( file
->fd
, desiredFileSize
);
387 if( doWrite
&& !alreadyExisted
&& ( preallocationMode
== TR_PREALLOCATE_SPARSE
) )
388 preallocateFileSparse( file
->fd
, desiredFileSize
);
390 #ifdef HAVE_POSIX_FADVISE
391 /* this doubles the OS level readahead buffer, which in practice
392 * turns out to be a good thing, because many (most?) clients request
393 * chunks of blocks in order.
394 * It's okay for this to fail silently, so don't let it affect errno */
396 const int err
= errno
;
397 posix_fadvise( file
->fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
402 #if defined( SYS_DARWIN )
404 * 1. Enable readahead for reasons described above w/POSIX_FADV_SEQUENTIAL.
406 * 2. Disable OS-level caching due to user reports of adverse effects of
407 * excessive inactive memory. However this is experimental because
408 * previous attempts at this have *also* had adverse effects (see r8198)
410 * It's okay for this to fail silently, so don't let it affect errno
413 const int err
= errno
;
414 fcntl( file
->fd
, F_NOCACHE
, 1 );
415 fcntl( file
->fd
, F_RDAHEAD
, 1 );
423 static inline tr_bool
424 fileIsOpen( const struct tr_openfile
* o
)
430 TrCloseFile( struct tr_openfile
* o
)
433 assert( fileIsOpen( o
) );
435 tr_close_file( o
->fd
);
440 tr_fdFileGetCached( tr_session
* session
,
442 tr_file_index_t fileNum
,
445 struct tr_openfile
* match
= NULL
;
446 struct tr_fdInfo
* gFd
;
448 assert( tr_isSession( session
) );
449 assert( session
->fdInfo
!= NULL
);
450 assert( torrentId
> 0 );
451 assert( tr_isBool( doWrite
) );
453 gFd
= session
->fdInfo
;
455 /* is it already open? */
458 struct tr_openfile
* o
;
459 for( i
=0; i
<gFd
->openFileLimit
; ++i
)
461 o
= &gFd
->openFiles
[i
];
463 if( torrentId
!= o
->torrentId
)
465 if( fileNum
!= o
->fileNum
)
467 if( !fileIsOpen( o
) )
475 if( ( match
!= NULL
) && ( !doWrite
|| match
->isWritable
) )
477 match
->date
= tr_time( );
484 /* returns an fd on success, or a -1 on failure and sets errno */
486 tr_fdFileCheckout( tr_session
* session
,
488 tr_file_index_t fileNum
,
489 const char * filename
,
491 tr_preallocation_mode preallocationMode
,
492 uint64_t desiredFileSize
)
495 struct tr_fdInfo
* gFd
;
496 struct tr_openfile
* o
;
498 assert( tr_isSession( session
) );
499 assert( session
->fdInfo
!= NULL
);
500 assert( torrentId
> 0 );
501 assert( filename
&& *filename
);
502 assert( tr_isBool( doWrite
) );
504 gFd
= session
->fdInfo
;
506 dbgmsg( "looking for file '%s', writable %c", filename
, doWrite
? 'y' : 'n' );
508 /* is it already open? */
509 for( i
=0; i
<gFd
->openFileLimit
; ++i
)
511 o
= &gFd
->openFiles
[i
];
513 if( torrentId
!= o
->torrentId
)
515 if( fileNum
!= o
->fileNum
)
517 if( !fileIsOpen( o
) )
520 if( doWrite
&& !o
->isWritable
)
522 dbgmsg( "found it! it's open and available, but isn't writable. closing..." );
527 dbgmsg( "found it! it's ready for use!" );
532 dbgmsg( "it's not already open. looking for an open slot or an old file." );
535 time_t date
= tr_time( ) + 1;
537 /* look for the file that's been open longest */
538 for( i
=0; i
<gFd
->openFileLimit
; ++i
)
540 o
= &gFd
->openFiles
[i
];
542 if( !fileIsOpen( o
) )
545 dbgmsg( "found an empty slot in %d", winner
);
556 assert( winner
>= 0 );
558 if( fileIsOpen( &gFd
->openFiles
[winner
] ) )
560 dbgmsg( "closing file \"%s\"", gFd
->openFiles
[winner
].filename
);
561 TrCloseFile( &gFd
->openFiles
[winner
] );
565 assert( winner
>= 0 );
566 o
= &gFd
->openFiles
[winner
];
567 if( !fileIsOpen( o
) )
569 const int err
= TrOpenFile( session
, winner
, filename
, doWrite
,
570 preallocationMode
, desiredFileSize
);
576 dbgmsg( "opened '%s' in slot %d, doWrite %c", filename
, winner
,
577 doWrite
? 'y' : 'n' );
578 tr_strlcpy( o
->filename
, filename
, sizeof( o
->filename
) );
579 o
->isWritable
= doWrite
;
582 dbgmsg( "checking out '%s' in slot %d", filename
, winner
);
583 o
->torrentId
= torrentId
;
584 o
->fileNum
= fileNum
;
585 o
->date
= tr_time( );
590 tr_fdFileClose( tr_session
* session
,
591 const tr_torrent
* tor
,
592 tr_file_index_t fileNum
)
594 struct tr_openfile
* o
;
595 struct tr_fdInfo
* gFd
;
596 const struct tr_openfile
* end
;
597 const int torrentId
= tr_torrentId( tor
);
599 assert( tr_isSession( session
) );
600 assert( session
->fdInfo
!= NULL
);
601 assert( tr_isTorrent( tor
) );
602 assert( fileNum
< tor
->info
.fileCount
);
604 gFd
= session
->fdInfo
;
606 for( o
=gFd
->openFiles
, end
=o
+gFd
->openFileLimit
; o
!=end
; ++o
)
608 if( torrentId
!= o
->torrentId
)
610 if( fileNum
!= o
->fileNum
)
612 if( !fileIsOpen( o
) )
615 dbgmsg( "tr_fdFileClose closing \"%s\"", o
->filename
);
621 tr_fdTorrentClose( tr_session
* session
, int torrentId
)
623 assert( tr_isSession( session
) );
625 if( session
->fdInfo
!= NULL
)
627 struct tr_openfile
* o
;
628 const struct tr_openfile
* end
;
629 struct tr_fdInfo
* gFd
= session
->fdInfo
;
631 for( o
=gFd
->openFiles
, end
=o
+gFd
->openFileLimit
; o
!=end
; ++o
)
632 if( fileIsOpen( o
) && ( o
->torrentId
== torrentId
) )
644 tr_fdSocketCreate( tr_session
* session
, int domain
, int type
)
647 struct tr_fdInfo
* gFd
;
649 assert( tr_isSession( session
) );
650 assert( session
->fdInfo
!= NULL
);
652 gFd
= session
->fdInfo
;
654 if( gFd
->socketCount
< gFd
->socketLimit
)
655 if( ( s
= socket( domain
, type
, 0 ) ) < 0 )
657 if( sockerrno
!= EAFNOSUPPORT
)
658 tr_err( _( "Couldn't create socket: %s" ),
659 tr_strerror( sockerrno
) );
665 assert( gFd
->socketCount
>= 0 );
669 static tr_bool buf_logged
= FALSE
;
673 socklen_t size
= sizeof( int );
675 getsockopt( s
, SOL_SOCKET
, SO_SNDBUF
, &i
, &size
);
676 tr_dbg( "SO_SNDBUF size is %d", i
);
677 getsockopt( s
, SOL_SOCKET
, SO_RCVBUF
, &i
, &size
);
678 tr_dbg( "SO_RCVBUF size is %d", i
);
686 tr_fdSocketAccept( tr_session
* session
,
693 struct tr_fdInfo
* gFd
;
694 struct sockaddr_storage sock
;
696 assert( tr_isSession( session
) );
697 assert( session
->fdInfo
!= NULL
);
701 gFd
= session
->fdInfo
;
703 len
= sizeof( struct sockaddr_storage
);
704 s
= accept( b
, (struct sockaddr
*) &sock
, &len
);
706 if( ( s
>= 0 ) && gFd
->socketCount
> gFd
->socketLimit
)
708 tr_netCloseSocket( s
);
714 /* "The ss_family field of the sockaddr_storage structure will always
715 * align with the family field of any protocol-specific structure." */
716 if( sock
.ss_family
== AF_INET
)
718 struct sockaddr_in
*si
;
719 union { struct sockaddr_storage dummy
; struct sockaddr_in si
; } s
;
722 addr
->type
= TR_AF_INET
;
723 addr
->addr
.addr4
.s_addr
= si
->sin_addr
.s_addr
;
724 *port
= si
->sin_port
;
728 struct sockaddr_in6
*si
;
729 union { struct sockaddr_storage dummy
; struct sockaddr_in6 si
; } s
;
732 addr
->type
= TR_AF_INET6
;
733 addr
->addr
.addr6
= si
->sin6_addr
;
734 *port
= si
->sin6_port
;
743 tr_fdSocketClose( tr_session
* session
, int fd
)
745 assert( tr_isSession( session
) );
747 if( session
->fdInfo
!= NULL
)
749 struct tr_fdInfo
* gFd
= session
->fdInfo
;
753 tr_netCloseSocket( fd
);
757 assert( gFd
->socketCount
>= 0 );
763 **** Startup / Shutdown
768 ensureSessionFdInfoExists( tr_session
* session
)
770 assert( tr_isSession( session
) );
772 if( session
->fdInfo
== NULL
)
773 session
->fdInfo
= tr_new0( struct tr_fdInfo
, 1 );
777 tr_fdClose( tr_session
* session
)
779 struct tr_fdInfo
* gFd
;
780 struct tr_openfile
* o
;
781 const struct tr_openfile
* end
;
783 assert( tr_isSession( session
) );
784 assert( session
->fdInfo
!= NULL
);
786 gFd
= session
->fdInfo
;
788 for( o
=gFd
->openFiles
, end
=o
+gFd
->openFileLimit
; o
!=end
; ++o
)
789 if( fileIsOpen( o
) )
792 tr_free( gFd
->openFiles
);
794 session
->fdInfo
= NULL
;
802 tr_fdSetFileLimit( tr_session
* session
, int limit
)
804 struct tr_fdInfo
* gFd
;
806 ensureSessionFdInfoExists( session
);
808 gFd
= session
->fdInfo
;
810 if( gFd
->openFileLimit
!= limit
)
813 struct tr_openfile
* o
;
814 const struct tr_openfile
* end
;
816 /* close any files we've got open */
817 for( o
=gFd
->openFiles
, end
=o
+gFd
->openFileLimit
; o
!=end
; ++o
)
818 if( fileIsOpen( o
) )
821 /* rebuild the openFiles array */
822 tr_free( gFd
->openFiles
);
823 gFd
->openFiles
= tr_new0( struct tr_openfile
, limit
);
824 gFd
->openFileLimit
= limit
;
825 for( i
=0; i
<gFd
->openFileLimit
; ++i
)
826 gFd
->openFiles
[i
].fd
= -1;
831 tr_fdGetFileLimit( const tr_session
* session
)
833 return session
&& session
->fdInfo
? session
->fdInfo
->openFileLimit
: -1;
837 tr_fdSetPeerLimit( tr_session
* session
, int socketLimit
)
839 struct tr_fdInfo
* gFd
;
841 ensureSessionFdInfoExists( session
);
843 gFd
= session
->fdInfo
;
845 #ifdef HAVE_GETRLIMIT
848 const int NOFILE_BUFFER
= 512;
849 const int open_max
= sysconf( _SC_OPEN_MAX
);
850 getrlimit( RLIMIT_NOFILE
, &rlim
);
851 rlim
.rlim_cur
= MAX( 1024, open_max
);
852 rlim
.rlim_cur
= MIN( rlim
.rlim_cur
, rlim
.rlim_max
);
853 setrlimit( RLIMIT_NOFILE
, &rlim
);
854 tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim
.rlim_cur
);
855 gFd
->socketLimit
= MIN( socketLimit
, (int)rlim
.rlim_cur
- NOFILE_BUFFER
);
858 gFd
->socketLimit
= socketLimit
;
860 gFd
->publicSocketLimit
= socketLimit
;
862 tr_dbg( "socket limit is %d", (int)gFd
->socketLimit
);
866 tr_fdGetPeerLimit( const tr_session
* session
)
868 return session
&& session
->fdInfo
? session
->fdInfo
->publicSocketLimit
: -1;