Changeset 13681
- Timestamp:
- Dec 18, 2012, 3:03:23 AM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/libtransmission/fdlimit.c
r13631 r13681 76 76 preallocate_file_sparse (int fd, uint64_t length) 77 77 { 78 79 80 81 82 78 const char zero = '\0'; 79 bool success = 0; 80 81 if (!length) 82 success = true; 83 83 84 84 #ifdef HAVE_FALLOCATE64 85 86 87 #endif 88 89 90 91 92 93 94 85 if (!success) /* fallocate64 is always preferred, so try it first */ 86 success = !fallocate64 (fd, 0, 0, length); 87 #endif 88 89 if (!success) /* fallback: the old-style seek-and-write */ 90 success = (lseek (fd, length-1, SEEK_SET) != -1) 91 && (write (fd, &zero, 1) != -1) 92 && (ftruncate (fd, length) != -1); 93 94 return success; 95 95 } 96 96 … … 98 98 preallocate_file_full (const char * filename, uint64_t length) 99 99 { 100 100 bool success = 0; 101 101 102 102 #ifdef WIN32 103 103 104 105 106 { 107 108 109 110 104 HANDLE hFile = CreateFile (filename, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0); 105 if (hFile != INVALID_HANDLE_VALUE) 106 { 107 LARGE_INTEGER li; 108 li.QuadPart = length; 109 success = SetFilePointerEx (hFile, li, NULL, FILE_BEGIN) && SetEndOfFile (hFile); 110 CloseHandle (hFile); 111 111 } 112 112 113 113 #else 114 114 115 116 117 115 int flags = O_RDWR | O_CREAT | O_LARGEFILE; 116 int fd = open (filename, flags, 0666); 117 if (fd >= 0) 118 118 { 119 119 # ifdef HAVE_FALLOCATE64 120 if (!success) 121 { 122 success = !fallocate64 (fd, 0, 0, length); 123 } 120 if (!success) 121 success = !fallocate64 (fd, 0, 0, length); 124 122 # endif 125 123 # ifdef HAVE_XFS_XFS_H 126 127 { 128 129 130 131 132 124 if (!success && platform_test_xfs_fd (fd)) 125 { 126 xfs_flock64_t fl; 127 fl.l_whence = 0; 128 fl.l_start = 0; 129 fl.l_len = length; 130 success = !xfsctl (NULL, fd, XFS_IOC_RESVSP64, &fl); 133 131 } 134 132 # endif 135 133 # ifdef SYS_DARWIN 136 137 { 138 139 140 141 142 143 144 134 if (!success) 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); 145 143 } 146 144 # endif 147 145 # ifdef HAVE_POSIX_FALLOCATE 148 if (!success) 149 { 150 success = !posix_fallocate (fd, 0, length); 151 } 146 if (!success) 147 success = !posix_fallocate (fd, 0, length); 152 148 # endif 153 149 154 155 { 156 157 158 159 150 if (!success) /* if nothing else works, do it the old-fashioned way */ 151 { 152 uint8_t buf[ 4096 ]; 153 memset (buf, 0, sizeof (buf)); 154 success = true; 155 while (success && (length > 0)) 160 156 { 161 162 163 157 const int thisPass = MIN (length, sizeof (buf)); 158 success = write (fd, buf, thisPass) == thisPass; 159 length -= thisPass; 164 160 } 165 161 } 166 162 167 168 } 169 170 #endif 171 172 163 close (fd); 164 } 165 166 #endif 167 168 return success; 173 169 } 174 170 … … 179 175 { 180 176 #ifdef WIN32 181 177 return _commit (fd); 182 178 #else 183 179 return fsync (fd); 184 180 #endif 185 181 } … … 212 208 { 213 209 #ifdef HAVE_PREAD 214 210 return pread (fd, buf, count, offset); 215 211 #else 216 217 218 219 212 const off_t lrc = lseek (fd, offset, SEEK_SET); 213 if (lrc < 0) 214 return -1; 215 return read (fd, buf, count); 220 216 #endif 221 217 } … … 225 221 { 226 222 #ifdef HAVE_PWRITE 227 223 return pwrite (fd, buf, count, offset); 228 224 #else 229 230 231 232 225 const off_t lrc = lseek (fd, offset, SEEK_SET); 226 if (lrc < 0) 227 return -1; 228 return write (fd, buf, count); 233 229 #endif 234 230 } … … 238 234 { 239 235 #ifdef HAVE_POSIX_FADVISE 240 236 return posix_fadvise (fd, offset, count, POSIX_FADV_WILLNEED); 241 237 #elif defined (SYS_DARWIN) 242 243 244 245 238 struct radvisory radv; 239 radv.ra_offset = offset; 240 radv.ra_count = count; 241 return fcntl (fd, F_RDADVISE, &radv); 246 242 #else 247 243 return 0; 248 244 #endif 249 245 } … … 252 248 tr_set_file_for_single_pass (int fd) 253 249 { 254 255 { 256 257 258 250 if (fd >= 0) 251 { 252 /* Set hints about the lookahead buffer and caching. It's okay 253 for these to fail silently, so don't let them affect errno */ 254 const int err = errno; 259 255 #ifdef HAVE_POSIX_FADVISE 260 256 posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL); 261 257 #endif 262 258 #ifdef SYS_DARWIN 263 264 265 #endif 266 259 fcntl (fd, F_RDAHEAD, 1); 260 fcntl (fd, F_NOCACHE, 1); 261 #endif 262 errno = err; 267 263 } 268 264 } … … 271 267 open_local_file (const char * filename, int flags) 272 268 { 273 274 275 269 const int fd = open (filename, flags, 0666); 270 tr_set_file_for_single_pass (fd); 271 return fd; 276 272 } 277 273 int 278 274 tr_open_file_for_writing (const char * filename) 279 275 { 280 276 return open_local_file (filename, O_LARGEFILE|O_BINARY|O_CREAT|O_WRONLY); 281 277 } 282 278 int 283 279 tr_open_file_for_scanning (const char * filename) 284 280 { 285 281 return open_local_file (filename, O_LARGEFILE|O_BINARY|O_SEQUENTIAL|O_RDONLY); 286 282 } 287 283 … … 290 286 { 291 287 #if defined (HAVE_POSIX_FADVISE) 292 293 294 295 296 288 /* Set hint about not caching this file. 289 It's okay for this to fail silently, so don't let it affect errno */ 290 const int err = errno; 291 posix_fadvise (fd, 0, 0, POSIX_FADV_DONTNEED); 292 errno = err; 297 293 #endif 298 294 #ifdef SYS_DARWIN 299 300 301 302 #endif 303 295 /* it's unclear to me from the man pages if this actually flushes out the cache, 296 * but it couldn't hurt... */ 297 fcntl (fd, F_NOCACHE, 1); 298 #endif 299 close (fd); 304 300 } 305 301 … … 312 308 struct tr_cached_file 313 309 { 314 boolis_writable;315 intfd;316 inttorrent_id;317 tr_file_index_tfile_index;318 time_tused_at;310 bool is_writable; 311 int fd; 312 int torrent_id; 313 tr_file_index_t file_index; 314 time_t used_at; 319 315 }; 320 316 … … 322 318 cached_file_is_open (const struct tr_cached_file * o) 323 319 { 324 325 326 320 assert (o != NULL); 321 322 return o->fd >= 0; 327 323 } 328 324 … … 330 326 cached_file_close (struct tr_cached_file * o) 331 327 { 332 333 334 335 328 assert (cached_file_is_open (o)); 329 330 tr_close_file (o->fd); 331 o->fd = -1; 336 332 } 337 333 … … 348 344 uint64_t file_size) 349 345 { 350 int flags; 351 struct stat sb; 352 bool alreadyExisted; 353 354 /* create subfolders, if any */ 355 if (writable) 356 { 357 char * dir = tr_dirname (filename); 358 const int err = tr_mkdirp (dir, 0777) ? errno : 0; 359 if (err) { 360 tr_err (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err)); 361 tr_free (dir); 362 return err; 363 } 364 tr_free (dir); 365 } 366 367 alreadyExisted = !stat (filename, &sb) && S_ISREG (sb.st_mode); 368 369 if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_FULL)) 370 if (preallocate_file_full (filename, file_size)) 371 tr_dbg ("Preallocated file \"%s\"", filename); 372 373 /* open the file */ 374 flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY; 375 flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL; 376 o->fd = open (filename, flags, 0666); 377 378 if (o->fd == -1) 379 { 380 const int err = errno; 381 tr_err (_("Couldn't open \"%1$s\": %2$s"), filename, tr_strerror (err)); 382 return err; 383 } 384 385 /* If the file already exists and it's too large, truncate it. 386 * This is a fringe case that happens if a torrent's been updated 387 * and one of the updated torrent's files is smaller. 388 * http://trac.transmissionbt.com/ticket/2228 389 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 390 */ 391 if (alreadyExisted && (file_size < (uint64_t)sb.st_size)) 392 { 393 if (ftruncate (o->fd, file_size) == -1) 394 { 395 const int err = errno; 396 tr_err (_("Couldn't truncate \"%1$s\": %2$s"), filename, tr_strerror (err)); 397 return err; 398 } 399 } 400 401 if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_SPARSE)) 402 preallocate_file_sparse (o->fd, file_size); 403 404 /* Many (most?) clients request blocks in ascending order, 405 * so increase the readahead buffer. 406 * Also, disable OS-level caching because "inactive memory" angers users. */ 407 tr_set_file_for_single_pass (o->fd); 408 409 return 0; 346 int flags; 347 struct stat sb; 348 bool alreadyExisted; 349 350 /* create subfolders, if any */ 351 if (writable) 352 { 353 char * dir = tr_dirname (filename); 354 const int err = tr_mkdirp (dir, 0777) ? errno : 0; 355 if (err) 356 { 357 tr_err (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err)); 358 tr_free (dir); 359 return err; 360 } 361 tr_free (dir); 362 } 363 364 alreadyExisted = !stat (filename, &sb) && S_ISREG (sb.st_mode); 365 366 if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_FULL)) 367 if (preallocate_file_full (filename, file_size)) 368 tr_dbg ("Preallocated file \"%s\"", filename); 369 370 /* open the file */ 371 flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY; 372 flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL; 373 o->fd = open (filename, flags, 0666); 374 375 if (o->fd == -1) 376 { 377 const int err = errno; 378 tr_err (_("Couldn't open \"%1$s\": %2$s"), filename, tr_strerror (err)); 379 return err; 380 } 381 382 /* If the file already exists and it's too large, truncate it. 383 * This is a fringe case that happens if a torrent's been updated 384 * and one of the updated torrent's files is smaller. 385 * http://trac.transmissionbt.com/ticket/2228 386 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 387 */ 388 if (alreadyExisted && (file_size < (uint64_t)sb.st_size)) 389 { 390 if (ftruncate (o->fd, file_size) == -1) 391 { 392 const int err = errno; 393 tr_err (_("Couldn't truncate \"%1$s\": %2$s"), filename, tr_strerror (err)); 394 return err; 395 } 396 } 397 398 if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_SPARSE)) 399 preallocate_file_sparse (o->fd, file_size); 400 401 /* Many (most?) clients request blocks in ascending order, 402 * so increase the readahead buffer. 403 * Also, disable OS-level caching because "inactive memory" angers users. */ 404 tr_set_file_for_single_pass (o->fd); 405 406 return 0; 410 407 } 411 408 … … 416 413 struct tr_fileset 417 414 { 418 419 415 struct tr_cached_file * begin; 416 const struct tr_cached_file * end; 420 417 }; 421 418 … … 423 420 fileset_construct (struct tr_fileset * set, int n) 424 421 { 425 426 427 428 429 430 431 432 422 struct tr_cached_file * o; 423 const struct tr_cached_file TR_CACHED_FILE_INIT = { 0, -1, 0, 0, 0 }; 424 425 set->begin = tr_new (struct tr_cached_file, n); 426 set->end = set->begin + n; 427 428 for (o=set->begin; o!=set->end; ++o) 429 *o = TR_CACHED_FILE_INIT; 433 430 } 434 431 … … 436 433 fileset_close_all (struct tr_fileset * set) 437 434 { 438 439 440 441 442 443 435 struct tr_cached_file * o; 436 437 if (set != NULL) 438 for (o=set->begin; o!=set->end; ++o) 439 if (cached_file_is_open (o)) 440 cached_file_close (o); 444 441 } 445 442 … … 447 444 fileset_destruct (struct tr_fileset * set) 448 445 { 449 450 451 446 fileset_close_all (set); 447 tr_free (set->begin); 448 set->end = set->begin = NULL; 452 449 } 453 450 … … 455 452 fileset_close_torrent (struct tr_fileset * set, int torrent_id) 456 453 { 457 458 459 460 461 462 454 struct tr_cached_file * o; 455 456 if (set != NULL) 457 for (o=set->begin; o!=set->end; ++o) 458 if ((o->torrent_id == torrent_id) && cached_file_is_open (o)) 459 cached_file_close (o); 463 460 } 464 461 … … 466 463 fileset_lookup (struct tr_fileset * set, int torrent_id, tr_file_index_t i) 467 464 { 468 469 470 471 472 473 474 475 465 struct tr_cached_file * o; 466 467 if (set != NULL) 468 for (o=set->begin; o!=set->end; ++o) 469 if ((torrent_id == o->torrent_id) && (i == o->file_index) && cached_file_is_open (o)) 470 return o; 471 472 return NULL; 476 473 } 477 474 … … 479 476 fileset_get_empty_slot (struct tr_fileset * set) 480 477 { 481 482 483 484 { 485 486 487 488 489 490 491 492 493 494 495 496 497 498 } 499 500 478 struct tr_cached_file * cull = NULL; 479 480 if (set->begin != NULL) 481 { 482 struct tr_cached_file * o; 483 484 /* try to find an unused slot */ 485 for (o=set->begin; o!=set->end; ++o) 486 if (!cached_file_is_open (o)) 487 return o; 488 489 /* all slots are full... recycle the least recently used */ 490 for (cull=NULL, o=set->begin; o!=set->end; ++o) 491 if (!cull || o->used_at < cull->used_at) 492 cull = o; 493 494 cached_file_close (cull); 495 } 496 497 return cull; 501 498 } 502 499 … … 509 506 struct tr_fdInfo 510 507 { 511 512 508 int peerCount; 509 struct tr_fileset fileset; 513 510 }; 514 511 … … 516 513 ensureSessionFdInfoExists (tr_session * session) 517 514 { 518 519 520 521 { 522 523 524 525 526 527 528 529 530 531 532 533 { 534 535 536 515 assert (tr_isSession (session)); 516 517 if (session->fdInfo == NULL) 518 { 519 struct rlimit limit; 520 struct tr_fdInfo * i; 521 const int FILE_CACHE_SIZE = 32; 522 523 /* Create the local file cache */ 524 i = tr_new0 (struct tr_fdInfo, 1); 525 fileset_construct (&i->fileset, FILE_CACHE_SIZE); 526 session->fdInfo = i; 527 528 /* set the open-file limit to the largest safe size wrt FD_SETSIZE */ 529 if (!getrlimit (RLIMIT_NOFILE, &limit)) 530 { 531 const int old_limit = (int) limit.rlim_cur; 532 const int new_limit = MIN (limit.rlim_max, FD_SETSIZE); 533 if (new_limit != old_limit) 537 534 { 538 539 540 541 535 limit.rlim_cur = new_limit; 536 setrlimit (RLIMIT_NOFILE, &limit); 537 getrlimit (RLIMIT_NOFILE, &limit); 538 tr_inf ("Changed open file limit from %d to %d", old_limit, (int)limit.rlim_cur); 542 539 } 543 540 } … … 548 545 tr_fdClose (tr_session * session) 549 546 { 550 551 { 552 553 554 555 547 if (session && session->fdInfo) 548 { 549 struct tr_fdInfo * i = session->fdInfo; 550 fileset_destruct (&i->fileset); 551 tr_free (i); 552 session->fdInfo = NULL; 556 553 } 557 554 } … … 564 561 get_fileset (tr_session * session) 565 562 { 566 567 568 569 570 563 if (!session) 564 return NULL; 565 566 ensureSessionFdInfoExists (session); 567 return &session->fdInfo->fileset; 571 568 } 572 569 … … 574 571 tr_fdFileClose (tr_session * s, const tr_torrent * tor, tr_file_index_t i) 575 572 { 576 577 578 579 { 580 581 582 583 584 585 573 struct tr_cached_file * o; 574 575 if ((o = fileset_lookup (get_fileset (s), tr_torrentId (tor), i))) 576 { 577 /* flush writable files so that their mtimes will be 578 * up-to-date when this function returns to the caller... */ 579 if (o->is_writable) 580 tr_fsync (o->fd); 581 582 cached_file_close (o); 586 583 } 587 584 } … … 590 587 tr_fdFileGetCached (tr_session * s, int torrent_id, tr_file_index_t i, bool writable) 591 588 { 592 593 594 595 596 597 598 589 struct tr_cached_file * o = fileset_lookup (get_fileset (s), torrent_id, i); 590 591 if (!o || (writable && !o->is_writable)) 592 return -1; 593 594 o->used_at = tr_time (); 595 return o->fd; 599 596 } 600 597 … … 608 605 tr_fdFileGetCachedMTime (tr_session * s, int torrent_id, tr_file_index_t i, time_t * mtime) 609 606 { 610 611 612 613 614 615 616 617 607 bool success; 608 struct stat sb; 609 struct tr_cached_file * o = fileset_lookup (get_fileset (s), torrent_id, i); 610 611 if ((success = (o != NULL) && !fstat (o->fd, &sb))) 612 *mtime = TR_STAT_MTIME (sb); 613 614 return success; 618 615 } 619 616 … … 634 631 uint64_t file_size) 635 632 { 636 struct tr_fileset * set = get_fileset (session); 637 struct tr_cached_file * o = fileset_lookup (set, torrent_id, i); 638 639 if (o && writable && !o->is_writable) 640 cached_file_close (o); /* close it so we can reopen in rw mode */ 641 else if (!o) 642 o = fileset_get_empty_slot (set); 643 644 if (!cached_file_is_open (o)) 645 { 646 const int err = cached_file_open (o, filename, writable, allocation, file_size); 647 if (err) { 648 errno = err; 649 return -1; 650 } 651 652 dbgmsg ("opened '%s' writable %c", filename, writable?'y':'n'); 653 o->is_writable = writable; 654 } 655 656 dbgmsg ("checking out '%s'", filename); 657 o->torrent_id = torrent_id; 658 o->file_index = i; 659 o->used_at = tr_time (); 660 return o->fd; 633 struct tr_fileset * set = get_fileset (session); 634 struct tr_cached_file * o = fileset_lookup (set, torrent_id, i); 635 636 if (o && writable && !o->is_writable) 637 cached_file_close (o); /* close it so we can reopen in rw mode */ 638 else if (!o) 639 o = fileset_get_empty_slot (set); 640 641 if (!cached_file_is_open (o)) 642 { 643 const int err = cached_file_open (o, filename, writable, allocation, file_size); 644 if (err) 645 { 646 errno = err; 647 return -1; 648 } 649 650 dbgmsg ("opened '%s' writable %c", filename, writable?'y':'n'); 651 o->is_writable = writable; 652 } 653 654 dbgmsg ("checking out '%s'", filename); 655 o->torrent_id = torrent_id; 656 o->file_index = i; 657 o->used_at = tr_time (); 658 return o->fd; 661 659 } 662 660 … … 670 668 tr_fdSocketCreate (tr_session * session, int domain, int type) 671 669 { 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 { 691 692 693 { 694 695 696 697 698 699 700 701 } 702 } 703 704 670 int s = -1; 671 struct tr_fdInfo * gFd; 672 assert (tr_isSession (session)); 673 674 ensureSessionFdInfoExists (session); 675 gFd = session->fdInfo; 676 677 if (gFd->peerCount < session->peerLimit) 678 if ((s = socket (domain, type, 0)) < 0) 679 if (sockerrno != EAFNOSUPPORT) 680 tr_err (_("Couldn't create socket: %s"), tr_strerror (sockerrno)); 681 682 if (s > -1) 683 ++gFd->peerCount; 684 685 assert (gFd->peerCount >= 0); 686 687 if (s >= 0) 688 { 689 static bool buf_logged = false; 690 if (!buf_logged) 691 { 692 int i; 693 socklen_t size = sizeof (int); 694 buf_logged = true; 695 getsockopt (s, SOL_SOCKET, SO_SNDBUF, &i, &size); 696 tr_dbg ("SO_SNDBUF size is %d", i); 697 getsockopt (s, SOL_SOCKET, SO_RCVBUF, &i, &size); 698 tr_dbg ("SO_RCVBUF size is %d", i); 699 } 700 } 701 702 return s; 705 703 } 706 704 … … 708 706 tr_fdSocketAccept (tr_session * s, int sockfd, tr_address * addr, tr_port * port) 709 707 { 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 { 727 728 729 { 730 708 int fd; 709 unsigned int len; 710 struct tr_fdInfo * gFd; 711 struct sockaddr_storage sock; 712 713 assert (tr_isSession (s)); 714 assert (addr); 715 assert (port); 716 717 ensureSessionFdInfoExists (s); 718 gFd = s->fdInfo; 719 720 len = sizeof (struct sockaddr_storage); 721 fd = accept (sockfd, (struct sockaddr *) &sock, &len); 722 723 if (fd >= 0) 724 { 725 if ((gFd->peerCount < s->peerLimit) 726 && tr_address_from_sockaddr_storage (addr, port, &sock)) 727 { 728 ++gFd->peerCount; 731 729 } 732 730 else 733 731 { 734 735 736 } 737 } 738 739 732 tr_netCloseSocket (fd); 733 fd = -1; 734 } 735 } 736 737 return fd; 740 738 } 741 739 … … 743 741 tr_fdSocketClose (tr_session * session, int fd) 744 742 { 745 746 747 748 { 749 750 751 752 { 753 754 755 } 756 757 758 } 759 } 743 assert (tr_isSession (session)); 744 745 if (session->fdInfo != NULL) 746 { 747 struct tr_fdInfo * gFd = session->fdInfo; 748 749 if (fd >= 0) 750 { 751 tr_netCloseSocket (fd); 752 --gFd->peerCount; 753 } 754 755 assert (gFd->peerCount >= 0); 756 } 757 }
Note: See TracChangeset
for help on using the changeset viewer.