Changeset 3969 for trunk/libtransmission/fastresume.c
- Timestamp:
- Nov 26, 2007, 3:54:20 AM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/libtransmission/fastresume.c
r3815 r3969 53 53 #include <unistd.h> 54 54 55 #include <event.h> 56 55 57 #include "transmission.h" 56 58 #include "completion.h" … … 330 332 } 331 333 332 static int 333 loadDestination( tr_torrent * tor, FILE * fp, const char * destination, int argIsFallback ) 334 { 335 int pathlen = 0; 336 char path[MAX_PATH_LENGTH]; 337 338 for( ;; ) { 339 const int ch = fgetc( fp ); 340 if( ch==EOF ) /* end of file */ 341 return TR_ERROR_IO_OTHER; 342 if( ch=='\0' ) /* end of string */ 343 break; 344 path[pathlen++] = (char) ch; 345 } 346 347 path[pathlen] = '\0'; 348 349 if( argIsFallback ) 350 tor->destination = tr_strdup( pathlen ? path : destination ); 351 else 352 tor->destination = tr_strdup( destination && *destination ? destination : path ); 353 354 return TR_OK; 355 } 356 357 static int 358 loadSpeeds( tr_torrent * tor, FILE * file ) 359 { 360 const size_t len = FR_SPEED_LEN; 361 char * buf = tr_new0( char, len ); 362 char * walk = buf; 363 uint16_t i16; 364 uint8_t i8; 365 366 if( len != fread( buf, 1, len, file ) ) { 367 tr_inf( "Couldn't read from resume file" ); 368 free( buf ); 369 return TR_ERROR_IO_OTHER; 370 } 371 372 memcpy( &i16, walk, 2 ); walk += 2; 373 tr_torrentSetSpeedLimit( tor, TR_DOWN, i16 ); 374 memcpy( &i8, walk, 1 ); walk += 1; 375 tr_torrentSetSpeedMode( tor, TR_DOWN, (tr_speedlimit)i8 ); 376 memcpy( &i16, walk, 2 ); walk += 2; 377 tr_torrentSetSpeedLimit( tor, TR_UP, i16 ); 378 memcpy( &i8, walk, 1 ); walk += 1; 379 tr_torrentSetSpeedMode( tor, TR_UP, (tr_speedlimit)i8 ); 380 381 tr_free( buf ); 382 return TR_OK; 383 } 384 385 386 static int 387 loadPriorities( tr_torrent * tor, 388 FILE * file ) 389 { 390 const size_t n = tor->info.fileCount; 391 const size_t len = 2 * n; 392 int *dnd = NULL, dndCount = 0; 393 int *dl = NULL, dlCount = 0; 394 char * buf = tr_new0( char, len ); 395 char * walk = buf; 396 size_t i; 397 398 if( len != fread( buf, 1, len, file ) ) { 399 tr_inf( "Couldn't read from resume file" ); 400 free( buf ); 401 return TR_ERROR_IO_OTHER; 402 } 403 404 /* set file priorities */ 405 for( i=0; i<n; ++i ) { 406 tr_priority_t priority; 407 const char ch = *walk++; 408 switch( ch ) { 409 case 'l': priority = TR_PRI_LOW; break; 410 case 'h': priority = TR_PRI_HIGH; break; 411 default: priority = TR_PRI_NORMAL; break; 412 } 413 tor->info.files[i].priority = priority; 414 } 415 416 /* set the dnd flags */ 417 dl = tr_new( int, len ); 418 dnd = tr_new( int, len ); 419 for( i=0; i<n; ++i ) 420 if( *walk++ == 't' ) /* 't' means the DND flag is true */ 421 dnd[dndCount++] = i; 422 else 423 dl[dlCount++] = i; 424 425 if( dndCount ) 426 tr_torrentSetFileDLs ( tor, dnd, dndCount, FALSE ); 427 if( dlCount ) 428 tr_torrentSetFileDLs ( tor, dl, dlCount, TRUE ); 429 430 tr_free( dnd ); 431 tr_free( dl ); 432 tr_free( buf ); 433 return TR_OK; 434 } 435 436 static int 437 fastResumeLoadProgress( const tr_torrent * tor, 438 tr_bitfield * uncheckedPieces, 439 FILE * file ) 440 { 441 const size_t len = FR_PROGRESS_LEN( tor ); 442 uint8_t * buf = calloc( len, 1 ); 443 int ret = 0; 334 /*** 335 **** 336 ***/ 337 338 static uint64_t 339 internalIdToPublicBitfield( uint8_t id ) 340 { 341 uint64_t ret = 0; 342 343 switch( id ) 344 { 345 case FR_ID_PROGRESS_SLOTS: ret = 0; break; 346 case FR_ID_DOWNLOADED: ret = TR_FR_DOWNLOADED; break; 347 case FR_ID_UPLOADED: ret = TR_FR_UPLOADED; break; 348 case FR_ID_PEERS_OLD: ret = TR_FR_PEERS; break; 349 case FR_ID_PROGRESS: ret = TR_FR_PROGRESS; break; 350 case FR_ID_PRIORITY: ret = TR_FR_PRIORITY; break; 351 case FR_ID_SPEED: ret = TR_FR_SPEEDLIMIT; break; 352 case FR_ID_RUN: ret = TR_FR_RUN; break; 353 case FR_ID_CORRUPT: ret = TR_FR_CORRUPT; break; 354 case FR_ID_PEERS: ret = TR_FR_PEERS; break; 355 case FR_ID_DESTINATION: ret = TR_FR_DESTINATION; break; 356 case FR_ID_PEX: ret = TR_FR_PEX; break; 357 } 358 359 return ret; 360 } 361 362 static void 363 readBytes( void * target, const uint8_t ** source, size_t byteCount ) 364 { 365 memcpy( target, *source, byteCount ); 366 *source += byteCount; 367 } 368 369 static uint64_t 370 parseDownloaded( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 371 { 372 if( len != sizeof(uint64_t) ) 373 return 0; 374 readBytes( &tor->downloadedPrev, &buf, sizeof(uint64_t) ); 375 return TR_FR_DOWNLOADED; 376 } 377 378 static uint64_t 379 parseUploaded( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 380 { 381 if( len != sizeof(uint64_t) ) 382 return 0; 383 readBytes( &tor->uploadedPrev, &buf, sizeof(uint64_t) ); 384 return TR_FR_UPLOADED; 385 } 386 387 static uint64_t 388 parseCorrupt( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 389 { 390 if( len != sizeof(uint64_t) ) 391 return 0; 392 readBytes( &tor->corruptPrev, &buf, sizeof(uint64_t) ); 393 return TR_FR_CORRUPT; 394 } 395 396 static uint64_t 397 parseProgress( const tr_torrent * tor, 398 const uint8_t * buf, 399 uint32_t len, 400 tr_bitfield * uncheckedPieces ) 401 { 444 402 int i; 445 446 if( len != fread( buf, 1, len, file ) ) 447 { 448 tr_inf( "Couldn't read from resume file" ); 449 tr_bitfieldAddRange( uncheckedPieces, 0, tor->info.pieceCount ); 450 ret = TR_ERROR_IO_OTHER; 451 } 452 else 403 uint64_t ret = 0; 404 405 if( len == FR_PROGRESS_LEN( tor ) ) 453 406 { 454 407 int n; … … 457 410 /* compare file mtimes */ 458 411 tr_time_t * curMTimes = getMTimes( tor, &n ); 459 uint8_t * walk = buf;412 const uint8_t * walk = buf; 460 413 const tr_time_t * oldMTimes = (const tr_time_t *) walk; 461 414 for( i=0; i<n; ++i ) { … … 474 427 memset( &bitfield, 0, sizeof bitfield ); 475 428 bitfield.len = FR_BLOCK_BITFIELD_LEN( tor ); 476 bitfield.bits = walk;429 bitfield.bits = (uint8_t*) walk; 477 430 tr_cpBlockBitfieldSet( tor->completion, &bitfield ); 431 432 ret = TR_FR_PROGRESS; 478 433 } 479 434 … … 484 439 tr_cpPieceRem( tor->completion, i ); 485 440 486 487 free( buf ); 488 return ret; 489 } 490 491 static uint64_t 492 fastResumeLoadOld( tr_torrent * tor, 493 tr_bitfield * uncheckedPieces, 494 FILE * file ) 441 return ret; 442 } 443 444 static uint64_t 445 parsePriorities( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 495 446 { 496 447 uint64_t ret = 0; 497 448 498 /* Check the size */ 499 const int size = 4 + FR_PROGRESS_LEN( tor ); 500 fseek( file, 0, SEEK_END ); 501 if( ftell( file ) != size ) 502 { 503 tr_inf( "Wrong size for resume file (%d bytes, %d expected)", 504 (int)ftell( file ), size ); 505 fclose( file ); 506 return 1; 507 } 508 509 /* load progress information */ 510 fseek( file, 4, SEEK_SET ); 511 if( fastResumeLoadProgress( tor, uncheckedPieces, file ) ) 512 { 513 fclose( file ); 514 return 1; 515 } 516 517 fclose( file ); 518 519 ret |= TR_FR_PROGRESS; 520 tr_inf( "Fast resuming successful (version 0)" ); 521 522 return ret; 523 } 524 525 static uint64_t 526 internalIdToPublicBitfield( uint8_t id ) 449 if( len == (uint32_t)(2 * tor->info.fileCount) ) 450 { 451 const size_t n = tor->info.fileCount; 452 const size_t len = 2 * n; 453 int *dnd = NULL, dndCount = 0; 454 int *dl = NULL, dlCount = 0; 455 size_t i; 456 const uint8_t * walk = buf; 457 458 /* set file priorities */ 459 for( i=0; i<n; ++i ) { 460 tr_priority_t priority; 461 const char ch = *walk++; 462 switch( ch ) { 463 case 'l': priority = TR_PRI_LOW; break; 464 case 'h': priority = TR_PRI_HIGH; break; 465 default: priority = TR_PRI_NORMAL; break; 466 } 467 tor->info.files[i].priority = priority; 468 } 469 470 /* set the dnd flags */ 471 dl = tr_new( int, len ); 472 dnd = tr_new( int, len ); 473 for( i=0; i<n; ++i ) 474 if( *walk++ == 't' ) /* 't' means the DND flag is true */ 475 dnd[dndCount++] = i; 476 else 477 dl[dlCount++] = i; 478 479 if( dndCount ) 480 tr_torrentSetFileDLs ( tor, dnd, dndCount, FALSE ); 481 if( dlCount ) 482 tr_torrentSetFileDLs ( tor, dl, dlCount, TRUE ); 483 484 tr_free( dnd ); 485 tr_free( dl ); 486 487 ret = TR_FR_PRIORITY; 488 } 489 490 return ret; 491 } 492 493 static uint64_t 494 parseSpeedLimit( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 527 495 { 528 496 uint64_t ret = 0; 529 497 530 switch( id ) 531 { 532 case FR_ID_PROGRESS_SLOTS: ret = 0; break; 533 case FR_ID_DOWNLOADED: ret = TR_FR_DOWNLOADED; break; 534 case FR_ID_UPLOADED: ret = TR_FR_UPLOADED; break; 535 case FR_ID_PEERS_OLD: ret = TR_FR_PEERS; break; 536 case FR_ID_PROGRESS: ret = TR_FR_PROGRESS; break; 537 case FR_ID_PRIORITY: ret = TR_FR_PRIORITY; break; 538 case FR_ID_SPEED: ret = TR_FR_SPEEDLIMIT; break; 539 case FR_ID_RUN: ret = TR_FR_RUN; break; 540 case FR_ID_CORRUPT: ret = TR_FR_CORRUPT; break; 541 case FR_ID_PEERS: ret = TR_FR_PEERS; break; 542 case FR_ID_DESTINATION: ret = TR_FR_DESTINATION; break; 543 case FR_ID_PEX: ret = TR_FR_PEX; break; 498 if( len == FR_SPEED_LEN ) 499 { 500 uint8_t i8; 501 uint16_t i16; 502 503 readBytes( &i16, &buf, sizeof(i16) ); 504 tr_torrentSetSpeedLimit( tor, TR_DOWN, i16 ); 505 readBytes( &i8, &buf, sizeof(i8) ); 506 tr_torrentSetSpeedMode( tor, TR_DOWN, (tr_speedlimit)i8 ); 507 readBytes( &i16, &buf, sizeof(i16) ); 508 tr_torrentSetSpeedLimit( tor, TR_UP, i16 ); 509 readBytes( &i8, &buf, sizeof(i8) ); 510 tr_torrentSetSpeedMode( tor, TR_UP, (tr_speedlimit)i8 ); 511 512 ret = TR_FR_SPEEDLIMIT; 513 } 514 515 return ret; 516 } 517 518 static uint64_t 519 parseRun( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 520 { 521 if( len != 1 ) 522 return 0; 523 tor->isRunning = *buf=='t'; 524 return TR_FR_RUN; 525 } 526 527 static uint64_t 528 parsePex( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 529 { 530 if( len != 1 ) 531 return 0; 532 tor->pexDisabled = *buf!='t'; 533 return TR_FR_PEX; 534 } 535 536 static uint64_t 537 parsePeers( tr_torrent * tor, const uint8_t * buf, uint32_t len ) 538 { 539 uint64_t ret = 0; 540 541 if( !tor->info.isPrivate ) 542 { 543 const int count = len / sizeof(tr_pex); 544 tr_peerMgrAddPex( tor->handle->peerMgr, 545 tor->info.hash, 546 TR_PEER_FROM_CACHE, 547 (tr_pex*)buf, count ); 548 tr_dbg( "found %i peers in resume file", count ); 549 ret = TR_FR_PEERS; 550 } 551 552 return ret; 553 } 554 555 static uint64_t 556 parseDestination( tr_torrent * tor, const uint8_t * buf, uint32_t len, 557 const char * destination, int argIsFallback ) 558 { 559 if( argIsFallback ) 560 tor->destination = tr_strdup( len ? (const char*)buf : destination ); 561 else 562 tor->destination = tr_strdup( len ? destination : (const char*)buf ); 563 564 return TR_FR_DESTINATION; 565 } 566 567 static uint64_t 568 parseVersion1( tr_torrent * tor, const uint8_t * buf, const uint8_t * end, 569 uint64_t fieldsToLoad, 570 tr_bitfield * uncheckedPieces, 571 const char * destination, int argIsFallback ) 572 { 573 uint64_t ret = 0; 574 575 while( end-buf >= 5 ) 576 { 577 uint8_t id; 578 uint32_t len; 579 readBytes( &id, &buf, sizeof(id) ); 580 readBytes( &len, &buf, sizeof(len) ); 581 582 if( fieldsToLoad & internalIdToPublicBitfield( id ) ) switch( id ) 583 { 584 case FR_ID_DOWNLOADED: ret |= parseDownloaded( tor, buf, len ); break; 585 case FR_ID_UPLOADED: ret |= parseUploaded( tor, buf, len ); break; 586 case FR_ID_PROGRESS: ret |= parseProgress( tor, buf, len, uncheckedPieces ); break; 587 case FR_ID_PRIORITY: ret |= parsePriorities( tor, buf, len ); break; 588 case FR_ID_SPEED: ret |= parseSpeedLimit( tor, buf, len ); break; 589 case FR_ID_RUN: ret |= parseRun( tor, buf, len ); break; 590 case FR_ID_CORRUPT: ret |= parseCorrupt( tor, buf, len ); break; 591 case FR_ID_PEERS: ret |= parsePeers( tor, buf, len ); break; 592 case FR_ID_PEX: ret |= parsePex( tor, buf, len ); break; 593 case FR_ID_DESTINATION: ret |= parseDestination( tor, buf, len, destination, argIsFallback ); break; 594 default: tr_dbg( "Skipping unknown resume code %d", (int)id ); break; 595 } 596 597 buf += len; 598 } 599 600 return ret; 601 } 602 603 static uint8_t* 604 loadResumeFile( const tr_torrent * tor, size_t * len ) 605 { 606 uint8_t * ret = NULL; 607 char path[MAX_PATH_LENGTH]; 608 const char * cacheDir = tr_getCacheDirectory (); 609 const char * hash = tor->info.hashString; 610 611 if( !ret && tor->handle->tag ) 612 { 613 char base[1024]; 614 snprintf( base, sizeof(base), "%s-%s", hash, tor->handle->tag ); 615 tr_buildPath( path, sizeof(path), cacheDir, base, NULL ); 616 ret = tr_loadFile( path, len ); 617 } 618 619 if( !ret ) 620 { 621 tr_buildPath( path, sizeof(path), cacheDir, hash, NULL ); 622 ret = tr_loadFile( path, len ); 544 623 } 545 624 … … 554 633 int argIsFallback ) 555 634 { 556 char path[MAX_PATH_LENGTH]; 557 FILE * file; 558 int version = 0; 559 uint8_t id; 560 uint32_t len; 561 uint64_t ret = 0; 562 563 assert( tor != NULL ); 564 assert( uncheckedPieces != NULL ); 565 566 /* Open resume file */ 567 fastResumeFileName( path, sizeof path, tor, 1 ); 568 file = fopen( path, "rb" ); 569 if( !file ) 570 { 571 if( ENOENT == errno ) 572 { 573 fastResumeFileName( path, sizeof path, tor, 0 ); 574 file = fopen( path, "rb" ); 575 if( !file ) 576 { 577 fastResumeFileName( path, sizeof path, tor, 1 ); 578 tr_inf( "Couldn't open '%s' for reading", path ); 579 return ret; 580 } 635 uint64_t ret = 0; 636 size_t size = 0; 637 uint8_t * buf = loadResumeFile( tor, &size ); 638 639 if( !buf ) 640 tr_inf( "Couldn't read resume file for '%s'", tor->info.name ); 641 else { 642 const uint8_t * walk = buf; 643 const uint8_t * end = walk + size; 644 if( end - walk >= 4 ) { 645 uint32_t version; 646 readBytes( &version, &walk, sizeof(version) ); 647 if( version == 1 ) 648 ret |= parseVersion1 ( tor, walk, end, fieldsToLoad, uncheckedPieces, destination, argIsFallback ); 649 else 650 tr_inf( "Unsupported resume file %d for '%s'", version, tor->info.name ); 581 651 } 582 } 583 584 tr_dbg( "Resume file '%s' loaded", path ); 585 586 /* Check format version */ 587 fread( &version, 4, 1, file ); 588 if( 0 == version ) 589 { 590 return fastResumeLoadOld( tor, uncheckedPieces, file ); 591 } 592 if( 1 != version ) 593 { 594 tr_inf( "Resume file has version %d, not supported", version ); 595 fclose( file ); 596 return ret; 597 } 598 599 /* read each block of data */ 600 while( 1 == fread( &id, 1, 1, file ) && 1 == fread( &len, 4, 1, file ) ) 601 { 602 if( fieldsToLoad & internalIdToPublicBitfield( id ) ) switch( id ) 603 { 604 case FR_ID_PROGRESS: 605 /* read progress data */ 606 if( (uint32_t)FR_PROGRESS_LEN( tor ) == len ) 607 { 608 const int rret = fastResumeLoadProgress( tor, uncheckedPieces, file ); 609 610 if( rret && ( feof(file) || ferror(file) ) ) 611 { 612 fclose( file ); 613 return ret; 614 } 615 616 ret |= TR_FR_PROGRESS; 617 continue; 618 } 619 break; 620 621 case FR_ID_PRIORITY: 622 623 /* read priority data */ 624 if( len == (uint32_t)(2 * tor->info.fileCount) ) 625 { 626 const int rret = loadPriorities( tor, file ); 627 628 if( rret && ( feof(file) || ferror(file) ) ) 629 { 630 fclose( file ); 631 return ret; 632 } 633 634 ret |= TR_FR_PRIORITY; 635 continue; 636 } 637 break; 638 639 case FR_ID_SPEED: 640 /* read speed data */ 641 if( len == FR_SPEED_LEN ) 642 { 643 const int rret = loadSpeeds( tor, file ); 644 645 if( rret && ( feof(file) || ferror(file) ) ) 646 { 647 fclose( file ); 648 return ret; 649 } 650 651 ret |= TR_FR_SPEEDLIMIT; 652 continue; 653 } 654 break; 655 656 case FR_ID_DESTINATION: 657 { 658 const int rret = loadDestination( tor, file, destination, argIsFallback ); 659 660 if( rret && ( feof(file) || ferror(file) ) ) 661 { 662 fclose( file ); 663 return ret; 664 } 665 666 ret |= TR_FR_DESTINATION; 667 continue; 668 } 669 break; 670 671 case FR_ID_RUN: 672 { 673 char ch; 674 if( fread( &ch, 1, 1, file ) != 1 ) 675 { 676 fclose( file ); 677 return ret; 678 } 679 tor->isRunning = ch=='t'; 680 ret |= TR_FR_RUN; 681 continue; 682 } 683 684 case FR_ID_PEX: 685 { 686 char ch; 687 if( fread( &ch, 1, 1, file ) != 1 ) 688 { 689 fclose( file ); 690 return ret; 691 } 692 tor->pexDisabled = ch!='t'; 693 ret |= TR_FR_PEX; 694 continue; 695 } 696 697 case FR_ID_DOWNLOADED: 698 /* read download total */ 699 if( 8 == len) 700 { 701 if( 1 != fread( &tor->downloadedPrev, 8, 1, file ) ) 702 { 703 fclose( file ); 704 return ret; 705 } 706 tor->downloadedCur = 0; 707 ret |= TR_FR_DOWNLOADED; 708 continue; 709 } 710 break; 711 712 case FR_ID_UPLOADED: 713 /* read upload total */ 714 if( 8 == len) 715 { 716 if( 1 != fread( &tor->uploadedPrev, 8, 1, file ) ) 717 { 718 fclose( file ); 719 return ret; 720 } 721 tor->uploadedCur = 0; 722 ret |= TR_FR_UPLOADED; 723 continue; 724 } 725 break; 726 727 case FR_ID_CORRUPT: 728 /* read upload total */ 729 if( 8 == len ) 730 { 731 if( 1 != fread( &tor->corruptPrev, 8, 1, file ) ) 732 { 733 fclose( file ); 734 return ret; 735 } 736 tor->corruptCur = 0; 737 ret |= TR_FR_CORRUPT; 738 continue; 739 } 740 break; 741 742 case FR_ID_PEERS_OLD: 743 if( !tor->info.isPrivate ) 744 { 745 uint8_t * buf = malloc( len ); 746 if( 1 != fread( buf, len, 1, file ) ) 747 { 748 free( buf ); 749 fclose( file ); 750 return ret; 751 } 752 753 tr_peerMgrAddPeers( tor->handle->peerMgr, 754 tor->info.hash, 755 TR_PEER_FROM_CACHE, 756 buf, len / 6 ); 757 758 tr_dbg( "found %i peers in resume file", len/6 ); 759 free( buf ); 760 ret |= TR_FR_PEERS; 761 } 762 763 case FR_ID_PEERS: 764 if( !tor->info.isPrivate ) 765 { 766 const int count = len / sizeof(tr_pex); 767 tr_pex * pex = tr_new0( tr_pex, count ); 768 if( 1 != fread( pex, sizeof(tr_pex), count, file ) ) 769 { 770 free( pex ); 771 fclose( file ); 772 return ret; 773 } 774 775 tr_peerMgrAddPex( tor->handle->peerMgr, 776 tor->info.hash, 777 TR_PEER_FROM_CACHE, 778 pex, count ); 779 780 tr_dbg( "found %i peers in resume file", len/6 ); 781 free( pex ); 782 ret |= TR_FR_PEERS; 783 } 784 continue; 785 786 default: 787 break; 788 } 789 790 /* if we didn't read the data, seek past it */ 791 tr_dbg( "Skipping resume data type %02x, %u bytes", id, len ); 792 fseek( file, len, SEEK_CUR ); 793 } 794 795 fclose( file ); 652 653 tr_free( buf ); 654 } 655 796 656 return ret; 797 657 }
Note: See TracChangeset
for help on using the changeset viewer.