2 * This file Copyright (C) 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 14147 2013-07-27 16:18:12Z jordan $
13 #ifdef HAVE_POSIX_FADVISE
17 #define _XOPEN_SOURCE 600
28 #ifdef HAVE_FALLOCATE64
29 /* FIXME can't find the right #include voodoo to pick up the declaration.. */
30 extern int fallocate64 (int fd
, int mode
, uint64_t offset
, uint64_t len
);
37 #include <sys/types.h>
39 #include <sys/time.h> /* getrlimit */
40 #include <sys/resource.h> /* getrlimit */
41 #include <fcntl.h> /* O_LARGEFILE posix_fadvise */
42 #include <unistd.h> /* lseek (), write (), ftruncate (), pread (), pwrite (), etc */
44 #include "transmission.h"
48 #include "torrent.h" /* tr_isTorrent () */
53 if (tr_logGetDeepEnabled ()) \
54 tr_logAddDeep (__FILE__, __LINE__, NULL, __VA_ARGS__); \
73 #define O_SEQUENTIAL 0
78 preallocate_file_sparse (int fd
, uint64_t length
)
80 const char zero
= '\0';
86 #ifdef HAVE_FALLOCATE64
87 if (!success
) /* fallocate64 is always preferred, so try it first */
88 success
= !fallocate64 (fd
, 0, 0, length
);
91 if (!success
) /* fallback: the old-style seek-and-write */
92 success
= (lseek (fd
, length
-1, SEEK_SET
) != -1)
93 && (write (fd
, &zero
, 1) != -1)
94 && (ftruncate (fd
, length
) != -1);
100 preallocate_file_full (const char * filename
, uint64_t length
)
106 HANDLE hFile
= CreateFile (filename
, GENERIC_WRITE
, 0, 0, CREATE_NEW
, FILE_FLAG_RANDOM_ACCESS
, 0);
107 if (hFile
!= INVALID_HANDLE_VALUE
)
110 li
.QuadPart
= length
;
111 success
= SetFilePointerEx (hFile
, li
, NULL
, FILE_BEGIN
) && SetEndOfFile (hFile
);
117 int flags
= O_RDWR
| O_CREAT
| O_LARGEFILE
;
118 int fd
= open (filename
, flags
, 0666);
121 # ifdef HAVE_FALLOCATE64
123 success
= !fallocate64 (fd
, 0, 0, length
);
125 # ifdef HAVE_XFS_XFS_H
126 if (!success
&& platform_test_xfs_fd (fd
))
132 success
= !xfsctl (NULL
, fd
, XFS_IOC_RESVSP64
, &fl
);
139 fst
.fst_flags
= F_ALLOCATECONTIG
;
140 fst
.fst_posmode
= F_PEOFPOSMODE
;
142 fst
.fst_length
= length
;
143 fst
.fst_bytesalloc
= 0;
144 success
= !fcntl (fd
, F_PREALLOCATE
, &fst
);
147 # ifdef HAVE_POSIX_FALLOCATE
149 success
= !posix_fallocate (fd
, 0, length
);
152 if (!success
) /* if nothing else works, do it the old-fashioned way */
155 memset (buf
, 0, sizeof (buf
));
157 while (success
&& (length
> 0))
159 const int thisPass
= MIN (length
, sizeof (buf
));
160 success
= write (fd
, buf
, thisPass
) == thisPass
;
174 /* portability wrapper for fsync (). */
186 /* Like pread and pwrite, except that the position is undefined afterwards.
187 And of course they are not thread-safe. */
189 /* don't use pread/pwrite on old versions of uClibc because they're buggy.
190 * https://trac.transmissionbt.com/ticket/3826 */
192 #define TR_UCLIBC_CHECK_VERSION(major,minor,micro) \
193 (__UCLIBC_MAJOR__ > (major) || \
194 (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ > (minor)) || \
195 (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ == (minor) && \
196 __UCLIBC_SUBLEVEL__ >= (micro)))
197 #if !TR_UCLIBC_CHECK_VERSION (0,9,28)
209 tr_pread (int fd
, void *buf
, size_t count
, off_t offset
)
212 return pread (fd
, buf
, count
, offset
);
214 const off_t lrc
= lseek (fd
, offset
, SEEK_SET
);
217 return read (fd
, buf
, count
);
222 tr_pwrite (int fd
, const void *buf
, size_t count
, off_t offset
)
225 return pwrite (fd
, buf
, count
, offset
);
227 const off_t lrc
= lseek (fd
, offset
, SEEK_SET
);
230 return write (fd
, buf
, count
);
235 tr_prefetch (int fd UNUSED
, off_t offset UNUSED
, size_t count UNUSED
)
237 #ifdef HAVE_POSIX_FADVISE
238 return posix_fadvise (fd
, offset
, count
, POSIX_FADV_WILLNEED
);
239 #elif defined (SYS_DARWIN)
240 struct radvisory radv
;
241 radv
.ra_offset
= offset
;
242 radv
.ra_count
= count
;
243 return fcntl (fd
, F_RDADVISE
, &radv
);
250 tr_set_file_for_single_pass (int fd
)
254 /* Set hints about the lookahead buffer and caching. It's okay
255 for these to fail silently, so don't let them affect errno */
256 const int err
= errno
;
257 #ifdef HAVE_POSIX_FADVISE
258 posix_fadvise (fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
261 fcntl (fd
, F_RDAHEAD
, 1);
262 fcntl (fd
, F_NOCACHE
, 1);
269 open_local_file (const char * filename
, int flags
)
271 const int fd
= open (filename
, flags
, 0666);
272 tr_set_file_for_single_pass (fd
);
276 tr_open_file_for_writing (const char * filename
)
278 return open_local_file (filename
, O_LARGEFILE
|O_BINARY
|O_CREAT
|O_WRONLY
);
281 tr_open_file_for_scanning (const char * filename
)
283 return open_local_file (filename
, O_LARGEFILE
|O_BINARY
|O_SEQUENTIAL
|O_RDONLY
);
287 tr_close_file (int fd
)
298 struct tr_cached_file
303 tr_file_index_t file_index
;
308 cached_file_is_open (const struct tr_cached_file
* o
)
316 cached_file_close (struct tr_cached_file
* o
)
318 assert (cached_file_is_open (o
));
320 tr_close_file (o
->fd
);
325 * returns 0 on success, or an errno value on failure.
326 * errno values include ENOENT if the parent folder doesn't exist,
327 * plus the errno values set by tr_mkdirp () and open ().
330 cached_file_open (struct tr_cached_file
* o
,
331 const char * filename
,
333 tr_preallocation_mode allocation
,
338 bool already_existed
;
341 /* create subfolders, if any */
344 char * dir
= tr_dirname (filename
);
345 const int err
= tr_mkdirp (dir
, 0777) ? errno
: 0;
348 tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir
, tr_strerror (err
));
355 already_existed
= !stat (filename
, &sb
) && S_ISREG (sb
.st_mode
);
357 if (writable
&& !already_existed
&& (allocation
== TR_PREALLOCATE_FULL
))
358 if (preallocate_file_full (filename
, file_size
))
359 tr_logAddDebug ("Preallocated file \"%s\"", filename
);
361 /* we can't resize the file w/o write permissions */
362 resize_needed
= already_existed
&& (file_size
< (uint64_t)sb
.st_size
);
363 writable
|= resize_needed
;
366 flags
= writable
? (O_RDWR
| O_CREAT
) : O_RDONLY
;
367 flags
|= O_LARGEFILE
| O_BINARY
| O_SEQUENTIAL
;
368 o
->fd
= open (filename
, flags
, 0666);
372 const int err
= errno
;
373 tr_logAddError (_("Couldn't open \"%1$s\": %2$s"), filename
, tr_strerror (err
));
377 /* If the file already exists and it's too large, truncate it.
378 * This is a fringe case that happens if a torrent's been updated
379 * and one of the updated torrent's files is smaller.
380 * http://trac.transmissionbt.com/ticket/2228
381 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
383 if (resize_needed
&& (ftruncate (o
->fd
, file_size
) == -1))
385 const int err
= errno
;
386 tr_logAddError (_("Couldn't truncate \"%1$s\": %2$s"), filename
, tr_strerror (err
));
390 if (writable
&& !already_existed
&& (allocation
== TR_PREALLOCATE_SPARSE
))
391 preallocate_file_sparse (o
->fd
, file_size
);
393 /* Many (most?) clients request blocks in ascending order,
394 * so increase the readahead buffer.
395 * Also, disable OS-level caching because "inactive memory" angers users. */
396 tr_set_file_for_single_pass (o
->fd
);
407 struct tr_cached_file
* begin
;
408 const struct tr_cached_file
* end
;
412 fileset_construct (struct tr_fileset
* set
, int n
)
414 struct tr_cached_file
* o
;
415 const struct tr_cached_file TR_CACHED_FILE_INIT
= { 0, -1, 0, 0, 0 };
417 set
->begin
= tr_new (struct tr_cached_file
, n
);
418 set
->end
= set
->begin
+ n
;
420 for (o
=set
->begin
; o
!=set
->end
; ++o
)
421 *o
= TR_CACHED_FILE_INIT
;
425 fileset_close_all (struct tr_fileset
* set
)
427 struct tr_cached_file
* o
;
430 for (o
=set
->begin
; o
!=set
->end
; ++o
)
431 if (cached_file_is_open (o
))
432 cached_file_close (o
);
436 fileset_destruct (struct tr_fileset
* set
)
438 fileset_close_all (set
);
439 tr_free (set
->begin
);
440 set
->end
= set
->begin
= NULL
;
444 fileset_close_torrent (struct tr_fileset
* set
, int torrent_id
)
446 struct tr_cached_file
* o
;
449 for (o
=set
->begin
; o
!=set
->end
; ++o
)
450 if ((o
->torrent_id
== torrent_id
) && cached_file_is_open (o
))
451 cached_file_close (o
);
454 static struct tr_cached_file
*
455 fileset_lookup (struct tr_fileset
* set
, int torrent_id
, tr_file_index_t i
)
457 struct tr_cached_file
* o
;
460 for (o
=set
->begin
; o
!=set
->end
; ++o
)
461 if ((torrent_id
== o
->torrent_id
) && (i
== o
->file_index
) && cached_file_is_open (o
))
467 static struct tr_cached_file
*
468 fileset_get_empty_slot (struct tr_fileset
* set
)
470 struct tr_cached_file
* cull
= NULL
;
472 if (set
->begin
!= NULL
)
474 struct tr_cached_file
* o
;
476 /* try to find an unused slot */
477 for (o
=set
->begin
; o
!=set
->end
; ++o
)
478 if (!cached_file_is_open (o
))
481 /* all slots are full... recycle the least recently used */
482 for (cull
=NULL
, o
=set
->begin
; o
!=set
->end
; ++o
)
483 if (!cull
|| o
->used_at
< cull
->used_at
)
486 cached_file_close (cull
);
494 **** Startup / Shutdown
501 struct tr_fileset fileset
;
505 ensureSessionFdInfoExists (tr_session
* session
)
507 assert (tr_isSession (session
));
509 if (session
->fdInfo
== NULL
)
512 struct tr_fdInfo
* i
;
513 const int FILE_CACHE_SIZE
= 32;
515 /* Create the local file cache */
516 i
= tr_new0 (struct tr_fdInfo
, 1);
517 fileset_construct (&i
->fileset
, FILE_CACHE_SIZE
);
520 /* set the open-file limit to the largest safe size wrt FD_SETSIZE */
521 if (!getrlimit (RLIMIT_NOFILE
, &limit
))
523 const int old_limit
= (int) limit
.rlim_cur
;
524 const int new_limit
= MIN (limit
.rlim_max
, FD_SETSIZE
);
525 if (new_limit
!= old_limit
)
527 limit
.rlim_cur
= new_limit
;
528 setrlimit (RLIMIT_NOFILE
, &limit
);
529 getrlimit (RLIMIT_NOFILE
, &limit
);
530 tr_logAddInfo ("Changed open file limit from %d to %d", old_limit
, (int)limit
.rlim_cur
);
537 tr_fdClose (tr_session
* session
)
539 if (session
&& session
->fdInfo
)
541 struct tr_fdInfo
* i
= session
->fdInfo
;
542 fileset_destruct (&i
->fileset
);
544 session
->fdInfo
= NULL
;
552 static struct tr_fileset
*
553 get_fileset (tr_session
* session
)
558 ensureSessionFdInfoExists (session
);
559 return &session
->fdInfo
->fileset
;
563 tr_fdFileClose (tr_session
* s
, const tr_torrent
* tor
, tr_file_index_t i
)
565 struct tr_cached_file
* o
;
567 if ((o
= fileset_lookup (get_fileset (s
), tr_torrentId (tor
), i
)))
569 /* flush writable files so that their mtimes will be
570 * up-to-date when this function returns to the caller... */
574 cached_file_close (o
);
579 tr_fdFileGetCached (tr_session
* s
, int torrent_id
, tr_file_index_t i
, bool writable
)
581 struct tr_cached_file
* o
= fileset_lookup (get_fileset (s
), torrent_id
, i
);
583 if (!o
|| (writable
&& !o
->is_writable
))
586 o
->used_at
= tr_time ();
591 #define TR_STAT_MTIME(sb)((sb).st_mtimespec.tv_sec)
593 #define TR_STAT_MTIME(sb)((sb).st_mtime)
597 tr_fdFileGetCachedMTime (tr_session
* s
, int torrent_id
, tr_file_index_t i
, time_t * mtime
)
601 struct tr_cached_file
* o
= fileset_lookup (get_fileset (s
), torrent_id
, i
);
603 if ((success
= (o
!= NULL
) && !fstat (o
->fd
, &sb
)))
604 *mtime
= TR_STAT_MTIME (sb
);
610 tr_fdTorrentClose (tr_session
* session
, int torrent_id
)
612 assert (tr_sessionIsLocked (session
));
614 fileset_close_torrent (get_fileset (session
), torrent_id
);
617 /* returns an fd on success, or a -1 on failure and sets errno */
619 tr_fdFileCheckout (tr_session
* session
,
622 const char * filename
,
624 tr_preallocation_mode allocation
,
627 struct tr_fileset
* set
= get_fileset (session
);
628 struct tr_cached_file
* o
= fileset_lookup (set
, torrent_id
, i
);
630 if (o
&& writable
&& !o
->is_writable
)
631 cached_file_close (o
); /* close it so we can reopen in rw mode */
633 o
= fileset_get_empty_slot (set
);
635 if (!cached_file_is_open (o
))
637 const int err
= cached_file_open (o
, filename
, writable
, allocation
, file_size
);
644 dbgmsg ("opened '%s' writable %c", filename
, writable
?'y':'n');
645 o
->is_writable
= writable
;
648 dbgmsg ("checking out '%s'", filename
);
649 o
->torrent_id
= torrent_id
;
651 o
->used_at
= tr_time ();
662 tr_fdSocketCreate (tr_session
* session
, int domain
, int type
)
665 struct tr_fdInfo
* gFd
;
666 assert (tr_isSession (session
));
668 ensureSessionFdInfoExists (session
);
669 gFd
= session
->fdInfo
;
671 if (gFd
->peerCount
< session
->peerLimit
)
672 if ((s
= socket (domain
, type
, 0)) < 0)
673 if (sockerrno
!= EAFNOSUPPORT
)
674 tr_logAddError (_("Couldn't create socket: %s"), tr_strerror (sockerrno
));
679 assert (gFd
->peerCount
>= 0);
683 static bool buf_logged
= false;
687 socklen_t size
= sizeof (int);
689 getsockopt (s
, SOL_SOCKET
, SO_SNDBUF
, &i
, &size
);
690 tr_logAddDebug ("SO_SNDBUF size is %d", i
);
691 getsockopt (s
, SOL_SOCKET
, SO_RCVBUF
, &i
, &size
);
692 tr_logAddDebug ("SO_RCVBUF size is %d", i
);
700 tr_fdSocketAccept (tr_session
* s
, int sockfd
, tr_address
* addr
, tr_port
* port
)
704 struct tr_fdInfo
* gFd
;
705 struct sockaddr_storage sock
;
707 assert (tr_isSession (s
));
711 ensureSessionFdInfoExists (s
);
714 len
= sizeof (struct sockaddr_storage
);
715 fd
= accept (sockfd
, (struct sockaddr
*) &sock
, &len
);
719 if ((gFd
->peerCount
< s
->peerLimit
)
720 && tr_address_from_sockaddr_storage (addr
, port
, &sock
))
726 tr_netCloseSocket (fd
);
735 tr_fdSocketClose (tr_session
* session
, int fd
)
737 assert (tr_isSession (session
));
739 if (session
->fdInfo
!= NULL
)
741 struct tr_fdInfo
* gFd
= session
->fdInfo
;
745 tr_netCloseSocket (fd
);
749 assert (gFd
->peerCount
>= 0);