/* returns 0 on success, or an errno on failure */ static int readOrWriteBytes (tr_session * session, tr_torrent * tor, int ioMode, tr_file_index_t fileIndex, uint64_t fileOffset, void * buf, size_t buflen) { int fd; int err = 0; const bool doWrite = ioMode >= TR_IO_WRITE; const tr_info * const info = &tor->info; const tr_file * const file = &info->files[fileIndex]; assert (fileIndex < info->fileCount); assert (!file->length || (fileOffset < file->length)); assert (fileOffset + buflen <= file->length); if (!file->length) return 0; /*** **** Find the fd ***/ fd = tr_fdFileGetCached (session, tr_torrentId (tor), fileIndex, doWrite); if (fd < 0) { /* it's not cached, so open/create it now */ char * subpath; const char * base; /* see if the file exists... */ if (!tr_torrentFindFile2 (tor, fileIndex, &base, &subpath, NULL)) { /* we can't read a file that doesn't exist... */ if (!doWrite) err = ENOENT; /* figure out where the file should go, so we can create it */ base = tr_torrentGetCurrentDir (tor); subpath = tr_sessionIsIncompleteFileNamingEnabled (tor->session) ? tr_torrentBuildPartial (tor, fileIndex) : tr_strdup (file->name); } if (!err) { /* open (and maybe create) the file */ char * filename = tr_buildPath (base, subpath, NULL); const int prealloc = file->dnd || !doWrite ? TR_PREALLOCATE_NONE : tor->session->preallocationMode; if (((fd = tr_fdFileCheckout (session, tor->uniqueId, fileIndex, filename, doWrite, prealloc, file->length))) < 0) { err = errno; tr_torerr (tor, "tr_fdFileCheckout failed for \"%s\": %s", filename, tr_strerror (err)); } else if (doWrite) { /* make a note that we just created a file */ tr_statsFileCreated (tor->session); } tr_free (filename); } tr_free (subpath); } /*** **** Use the fd ***/ if (!err) { if (ioMode == TR_IO_READ) { const int rc = tr_pread (fd, buf, buflen, fileOffset); if (rc < 0) { err = errno; tr_torerr (tor, "read failed for \"%s\": %s", file->name, tr_strerror (err)); } } else if (ioMode == TR_IO_WRITE) { const int rc = tr_pwrite (fd, buf, buflen, fileOffset); if (rc < 0) { err = errno; tr_torerr (tor, "write failed for \"%s\": %s", file->name, tr_strerror (err)); } } else if (ioMode == TR_IO_PREFETCH) { tr_prefetch (fd, fileOffset, buflen); } else { abort (); } } return err; }
static tr_bool verifyTorrent( tr_torrent * tor, tr_bool * stopFlag ) { time_t end; SHA_CTX sha; int fd = -1; int64_t filePos = 0; tr_bool changed = 0; tr_bool hadPiece = 0; time_t lastSleptAt = 0; uint32_t piecePos = 0; tr_file_index_t fileIndex = 0; tr_file_index_t prevFileIndex = !fileIndex; tr_piece_index_t pieceIndex = 0; const time_t begin = tr_time( ); const size_t buflen = 1024 * 128; /* 128 KiB buffer */ uint8_t * buffer = tr_valloc( buflen ); SHA1_Init( &sha ); tr_tordbg( tor, "%s", "verifying torrent..." ); tr_torrentSetChecked( tor, 0 ); while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) ) { uint32_t leftInPiece; uint32_t bytesThisPass; uint64_t leftInFile; const tr_file * file = &tor->info.files[fileIndex]; /* if we're starting a new piece... */ if( piecePos == 0 ) hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex ); /* if we're starting a new file... */ if( !filePos && (fd<0) && (fileIndex!=prevFileIndex) ) { char * filename = tr_torrentFindFile( tor, fileIndex ); fd = filename == NULL ? -1 : tr_open_file_for_scanning( filename ); tr_free( filename ); prevFileIndex = fileIndex; } /* figure out how much we can read this pass */ leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos; leftInFile = file->length - filePos; bytesThisPass = MIN( leftInFile, leftInPiece ); bytesThisPass = MIN( bytesThisPass, buflen ); /* read a bit */ if( fd >= 0 ) { const ssize_t numRead = tr_pread( fd, buffer, bytesThisPass, filePos ); if( numRead > 0 ) { bytesThisPass = (uint32_t)numRead; SHA1_Update( &sha, buffer, bytesThisPass ); #if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED posix_fadvise( fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED ); #endif } } /* move our offsets */ leftInPiece -= bytesThisPass; leftInFile -= bytesThisPass; piecePos += bytesThisPass; filePos += bytesThisPass; /* if we're finishing a piece... */ if( leftInPiece == 0 ) { time_t now; tr_bool hasPiece; uint8_t hash[SHA_DIGEST_LENGTH]; SHA1_Final( hash, &sha ); hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH ); if( hasPiece || hadPiece ) { tr_torrentSetHasPiece( tor, pieceIndex, hasPiece ); changed |= hasPiece != hadPiece; } tr_torrentSetPieceChecked( tor, pieceIndex ); now = tr_time( ); tor->anyDate = now; /* sleeping even just a few msec per second goes a long * way towards reducing IO load... */ if( lastSleptAt != now ) { lastSleptAt = now; tr_wait_msec( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY ); } SHA1_Init( &sha ); ++pieceIndex; piecePos = 0; } /* if we're finishing a file... */ if( leftInFile == 0 ) { if( fd >= 0 ) { tr_close_file( fd ); fd = -1; } ++fileIndex; filePos = 0; } } /* cleanup */ if( fd >= 0 ) tr_close_file( fd ); free( buffer ); /* stopwatch */ end = tr_time( ); tr_tordbg( tor, "Verification is done. It took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)", (int)(end-begin), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(end-begin))) ); return changed; }
/* returns 0 on success, or an errno on failure */ static int readOrWriteBytes( tr_session * session, tr_torrent * tor, int ioMode, tr_file_index_t fileIndex, uint64_t fileOffset, void * buf, size_t buflen ) { const tr_info * info = &tor->info; const tr_file * file = &info->files[fileIndex]; int fd = -1; int err = 0; const tr_bool doWrite = ioMode >= TR_IO_WRITE; //if( doWrite ) // fprintf( stderr, "in file %s at offset %zu, writing %zu bytes; file length is %zu\n", file->name, (size_t)fileOffset, buflen, (size_t)file->length ); assert( fileIndex < info->fileCount ); assert( !file->length || ( fileOffset < file->length ) ); assert( fileOffset + buflen <= file->length ); if( !file->length ) return 0; fd = tr_fdFileGetCached( session, tr_torrentId( tor ), fileIndex, doWrite ); if( fd < 0 ) { /* the fd cache doesn't have this file... * we'll need to open it and maybe create it */ char * subpath; const char * base; tr_bool fileExists; tr_preallocation_mode preallocationMode; fileExists = tr_torrentFindFile2( tor, fileIndex, &base, &subpath ); if( !fileExists ) { base = tr_torrentGetCurrentDir( tor ); if( tr_sessionIsIncompleteFileNamingEnabled( tor->session ) ) subpath = tr_torrentBuildPartial( tor, fileIndex ); else subpath = tr_strdup( file->name ); } if( ( file->dnd ) || ( ioMode < TR_IO_WRITE ) ) preallocationMode = TR_PREALLOCATE_NONE; else preallocationMode = tor->session->preallocationMode; if( ( ioMode < TR_IO_WRITE ) && !fileExists ) /* does file exist? */ { err = ENOENT; } else { char * filename = tr_buildPath( base, subpath, NULL ); if( ( fd = tr_fdFileCheckout( session, tor->uniqueId, fileIndex, filename, doWrite, preallocationMode, file->length ) ) < 0 ) { err = errno; tr_torerr( tor, "tr_fdFileCheckout failed for \"%s\": %s", filename, tr_strerror( err ) ); } tr_free( filename ); } if( doWrite && !err ) tr_statsFileCreated( tor->session ); tr_free( subpath ); } if( !err ) { /* check & see if someone deleted the file while it was in our cache */ struct stat sb; const tr_bool file_disappeared = fstat( fd, &sb ) || sb.st_nlink < 1; if( file_disappeared ) { tr_torrentSetLocalError( tor, "Please Verify Local Data! A file disappeared: \"%s\"", tor->info.files[fileIndex].name ); err = ENOENT; } if( ioMode == TR_IO_READ ) { const int rc = tr_pread( fd, buf, buflen, fileOffset ); if( rc < 0 ) { err = errno; tr_torerr( tor, "read failed for \"%s\": %s", file->name, tr_strerror( err ) ); } } else if( ioMode == TR_IO_PREFETCH ) { const int rc = tr_prefetch( fd, fileOffset, buflen ); if( rc < 0 ) { /* (don't set "err" here... it's okay for prefetch to fail) */ tr_tordbg( tor, "prefetch failed for \"%s\": %s", file->name, tr_strerror( errno ) ); } } else if( ioMode == TR_IO_WRITE ) { const int rc = tr_pwrite( fd, buf, buflen, fileOffset ); if( rc < 0 ) { err = errno; tr_torerr( tor, "write failed for \"%s\": %s", file->name, tr_strerror( err ) ); } } else { abort(); } } return err; }