Revert "transmission: update from 2.13 to 2.22"
[tomato.git] / release / src / router / transmission / libtransmission / fdlimit.c
blob9cc8209c0b9acbd25b6e3d9d9524a96e6a2f97f7
1 /*
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 $
13 #ifndef WIN32
14 #define HAVE_GETRLIMIT
15 #endif
17 #ifdef HAVE_POSIX_FADVISE
18 #ifdef _XOPEN_SOURCE
19 #undef _XOPEN_SOURCE
20 #endif
21 #define _XOPEN_SOURCE 600
22 #endif
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
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
39 #ifdef HAVE_XFS_XFS_H
40 #include <xfs/xfs.h>
41 #endif
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>
52 #include "transmission.h"
53 #include "fdlimit.h"
54 #include "net.h"
55 #include "platform.h" /* TR_PATH_MAX, TR_PATH_DELIMITER */
56 #include "session.h"
57 #include "torrent.h" /* tr_isTorrent() */
58 #include "utils.h"
60 #define dbgmsg( ... ) \
61 do { \
62 if( tr_deepLoggingIsActive( ) ) \
63 tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
64 } while( 0 )
66 /**
67 ***
68 **/
70 struct tr_openfile
72 tr_bool isWritable;
73 int torrentId;
74 tr_file_index_t fileNum;
75 char filename[TR_PATH_MAX];
76 int fd;
77 time_t date;
80 struct tr_fdInfo
82 int socketCount;
83 int socketLimit;
84 int publicSocketLimit;
85 int openFileLimit;
86 struct tr_openfile * openFiles;
89 /***
90 ****
91 **** Local Files
92 ****
93 ***/
95 #ifndef O_LARGEFILE
96 #define O_LARGEFILE 0
97 #endif
99 static tr_bool
100 preallocateFileSparse( int fd, uint64_t length )
102 const char zero = '\0';
103 tr_bool success = 0;
105 if( !length )
106 success = TRUE;
108 #ifdef HAVE_FALLOCATE64
109 if( !success ) /* fallocate64 is always preferred, so try it first */
110 success = !fallocate64( fd, 0, 0, length );
111 #endif
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 );
118 return success;
121 static tr_bool
122 preallocateFileFull( const char * filename, uint64_t length )
124 tr_bool success = 0;
126 #ifdef WIN32
128 HANDLE hFile = CreateFile( filename, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0 );
129 if( hFile != INVALID_HANDLE_VALUE )
131 LARGE_INTEGER li;
132 li.QuadPart = length;
133 success = SetFilePointerEx( hFile, li, NULL, FILE_BEGIN ) && SetEndOfFile( hFile );
134 CloseHandle( hFile );
137 #else
139 int flags = O_RDWR | O_CREAT | O_LARGEFILE;
140 int fd = open( filename, flags, 0666 );
141 if( fd >= 0 )
143 # ifdef HAVE_FALLOCATE64
144 if( !success )
146 success = !fallocate64( fd, 0, 0, length );
148 # endif
149 # ifdef HAVE_XFS_XFS_H
150 if( !success && platform_test_xfs_fd( fd ) )
152 xfs_flock64_t fl;
153 fl.l_whence = 0;
154 fl.l_start = 0;
155 fl.l_len = length;
156 success = !xfsctl( NULL, fd, XFS_IOC_RESVSP64, &fl );
158 # endif
159 # ifdef SYS_DARWIN
160 if( !success )
162 fstore_t fst;
163 fst.fst_flags = F_ALLOCATECONTIG;
164 fst.fst_posmode = F_PEOFPOSMODE;
165 fst.fst_offset = 0;
166 fst.fst_length = length;
167 fst.fst_bytesalloc = 0;
168 success = !fcntl( fd, F_PREALLOCATE, &fst );
170 # endif
171 # ifdef HAVE_POSIX_FALLOCATE
172 if( !success )
174 success = !posix_fallocate( fd, 0, length );
176 # endif
178 if( !success ) /* if nothing else works, do it the old-fashioned way */
180 uint8_t buf[ 4096 ];
181 memset( buf, 0, sizeof( buf ) );
182 success = TRUE;
183 while ( success && ( length > 0 ) )
185 const int thisPass = MIN( length, sizeof( buf ) );
186 success = write( fd, buf, thisPass ) == thisPass;
187 length -= thisPass;
191 close( fd );
194 #endif
196 return success;
199 /* Like pread and pwrite, except that the position is undefined afterwards.
200 And of course they are not thread-safe. */
202 #ifdef SYS_DARWIN
203 #define HAVE_PREAD
204 #define HAVE_PWRITE
205 #endif
207 ssize_t
208 tr_pread( int fd, void *buf, size_t count, off_t offset )
210 #ifdef HAVE_PREAD
211 return pread( fd, buf, count, offset );
212 #else
213 const off_t lrc = lseek( fd, offset, SEEK_SET );
214 if( lrc < 0 )
215 return -1;
216 return read( fd, buf, count );
217 #endif
220 ssize_t
221 tr_pwrite( int fd, const void *buf, size_t count, off_t offset )
223 #ifdef HAVE_PWRITE
224 return pwrite( fd, buf, count, offset );
225 #else
226 const off_t lrc = lseek( fd, offset, SEEK_SET );
227 if( lrc < 0 )
228 return -1;
229 return write( fd, buf, count );
230 #endif
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 );
243 #else
244 return 0;
245 #endif
249 tr_open_file_for_writing( const char * filename )
251 int flags = O_WRONLY | O_CREAT;
252 #ifdef O_BINARY
253 flags |= O_BINARY;
254 #endif
255 #ifdef O_LARGEFILE
256 flags |= O_LARGEFILE;
257 #endif
258 return open( filename, flags, 0666 );
262 tr_open_file_for_scanning( const char * filename )
264 int fd;
265 int flags;
267 /* build the flags */
268 flags = O_RDONLY;
269 #ifdef O_SEQUENTIAL
270 flags |= O_SEQUENTIAL;
271 #endif
272 #ifdef O_BINARY
273 flags |= O_BINARY;
274 #endif
275 #ifdef O_LARGEFILE
276 flags |= O_LARGEFILE;
277 #endif
279 /* open the file */
280 fd = open( filename, flags, 0666 );
281 if( fd >= 0 )
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 );
288 #endif
289 #ifdef SYS_DARWIN
290 fcntl( fd, F_NOCACHE, 1 );
291 fcntl( fd, F_RDAHEAD, 1 );
292 #endif
293 errno = err;
296 return fd;
299 void
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 );
307 errno = err;
308 #endif
309 #ifdef SYS_DARWIN
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 );
313 #endif
314 close( fd );
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().
322 static int
323 TrOpenFile( tr_session * session,
324 int i,
325 const char * filename,
326 tr_bool doWrite,
327 tr_preallocation_mode preallocationMode,
328 uint64_t desiredFileSize )
330 int flags;
331 struct stat sb;
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 */
341 if( doWrite )
343 char * dir = tr_dirname( filename );
344 const int err = tr_mkdirp( dir, 0777 ) ? errno : 0;
345 if( err ) {
346 tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) );
347 tr_free( dir );
348 return err;
350 tr_free( dir );
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 );
359 /* open the file */
360 flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY;
361 #ifdef O_SEQUENTIAL
362 flags |= O_SEQUENTIAL;
363 #endif
364 #ifdef O_LARGEFILE
365 flags |= O_LARGEFILE;
366 #endif
367 #ifdef WIN32
368 flags |= O_BINARY;
369 #endif
370 file->fd = open( filename, flags, 0666 );
371 if( file->fd == -1 )
373 const int err = errno;
374 tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
375 return 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 );
398 errno = err;
400 #endif
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 );
416 errno = err;
418 #endif
420 return 0;
423 static inline tr_bool
424 fileIsOpen( const struct tr_openfile * o )
426 return o->fd >= 0;
429 static void
430 TrCloseFile( struct tr_openfile * o )
432 assert( o != NULL );
433 assert( fileIsOpen( o ) );
435 tr_close_file( o->fd );
436 o->fd = -1;
440 tr_fdFileGetCached( tr_session * session,
441 int torrentId,
442 tr_file_index_t fileNum,
443 tr_bool doWrite )
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? */
457 int i;
458 struct tr_openfile * o;
459 for( i=0; i<gFd->openFileLimit; ++i )
461 o = &gFd->openFiles[i];
463 if( torrentId != o->torrentId )
464 continue;
465 if( fileNum != o->fileNum )
466 continue;
467 if( !fileIsOpen( o ) )
468 continue;
470 match = o;
471 break;
475 if( ( match != NULL ) && ( !doWrite || match->isWritable ) )
477 match->date = tr_time( );
478 return match->fd;
481 return -1;
484 /* returns an fd on success, or a -1 on failure and sets errno */
486 tr_fdFileCheckout( tr_session * session,
487 int torrentId,
488 tr_file_index_t fileNum,
489 const char * filename,
490 tr_bool doWrite,
491 tr_preallocation_mode preallocationMode,
492 uint64_t desiredFileSize )
494 int i, winner = -1;
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 )
514 continue;
515 if( fileNum != o->fileNum )
516 continue;
517 if( !fileIsOpen( o ) )
518 continue;
520 if( doWrite && !o->isWritable )
522 dbgmsg( "found it! it's open and available, but isn't writable. closing..." );
523 TrCloseFile( o );
524 break;
527 dbgmsg( "found it! it's ready for use!" );
528 winner = i;
529 break;
532 dbgmsg( "it's not already open. looking for an open slot or an old file." );
533 while( winner < 0 )
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 ) )
544 winner = i;
545 dbgmsg( "found an empty slot in %d", winner );
546 break;
549 if( date > o->date )
551 date = o->date;
552 winner = i;
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 );
571 if( err ) {
572 errno = err;
573 return -1;
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( );
586 return o->fd;
589 void
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 )
609 continue;
610 if( fileNum != o->fileNum )
611 continue;
612 if( !fileIsOpen( o ) )
613 continue;
615 dbgmsg( "tr_fdFileClose closing \"%s\"", o->filename );
616 TrCloseFile( o );
620 void
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 ) )
633 TrCloseFile( o );
637 /***
638 ****
639 **** Sockets
640 ****
641 ***/
644 tr_fdSocketCreate( tr_session * session, int domain, int type )
646 int s = -1;
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 ) );
662 if( s > -1 )
663 ++gFd->socketCount;
665 assert( gFd->socketCount >= 0 );
667 if( s >= 0 )
669 static tr_bool buf_logged = FALSE;
670 if( !buf_logged )
672 int i;
673 socklen_t size = sizeof( int );
674 buf_logged = TRUE;
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 );
682 return s;
686 tr_fdSocketAccept( tr_session * session,
687 int b,
688 tr_address * addr,
689 tr_port * port )
691 int s;
692 unsigned int len;
693 struct tr_fdInfo * gFd;
694 struct sockaddr_storage sock;
696 assert( tr_isSession( session ) );
697 assert( session->fdInfo != NULL );
698 assert( addr );
699 assert( port );
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 );
709 s = -1;
712 if( s >= 0 )
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;
720 s.dummy = sock;
721 si = &s.si;
722 addr->type = TR_AF_INET;
723 addr->addr.addr4.s_addr = si->sin_addr.s_addr;
724 *port = si->sin_port;
726 else
728 struct sockaddr_in6 *si;
729 union { struct sockaddr_storage dummy; struct sockaddr_in6 si; } s;
730 s.dummy = sock;
731 si = &s.si;
732 addr->type = TR_AF_INET6;
733 addr->addr.addr6 = si->sin6_addr;
734 *port = si->sin6_port;
736 ++gFd->socketCount;
739 return s;
742 void
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;
751 if( fd >= 0 )
753 tr_netCloseSocket( fd );
754 --gFd->socketCount;
757 assert( gFd->socketCount >= 0 );
761 /***
762 ****
763 **** Startup / Shutdown
764 ****
765 ***/
767 static void
768 ensureSessionFdInfoExists( tr_session * session )
770 assert( tr_isSession( session ) );
772 if( session->fdInfo == NULL )
773 session->fdInfo = tr_new0( struct tr_fdInfo, 1 );
776 void
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 ) )
790 TrCloseFile( o );
792 tr_free( gFd->openFiles );
793 tr_free( gFd );
794 session->fdInfo = NULL;
797 /***
798 ****
799 ***/
801 void
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 )
812 int i;
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 ) )
819 TrCloseFile( 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;
836 void
837 tr_fdSetPeerLimit( tr_session * session, int socketLimit )
839 struct tr_fdInfo * gFd;
841 ensureSessionFdInfoExists( session );
843 gFd = session->fdInfo;
845 #ifdef HAVE_GETRLIMIT
847 struct rlimit rlim;
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 );
857 #else
858 gFd->socketLimit = socketLimit;
859 #endif
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;