source: branches/encryption/libtransmission/inout.c @ 2988

Last change on this file since 2988 was 2988, checked in by charles, 15 years ago

move the torrent recheck into its own thread. we're getting close to getting rid of all the torrent threads =)

  • Property svn:keywords set to Date Rev Author Id
File size: 11.4 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
3 *
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.
9 *
10 * $Id: inout.c 2988 2007-09-09 00:10:52Z charles $
11 */
12
13#include <assert.h>
14#include <stdlib.h>
15#include <string.h>
16
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <unistd.h>
20
21#include "transmission.h"
22#include "completion.h"
23#include "crypto.h"
24#include "fdlimit.h"
25#include "inout.h"
26#include "list.h"
27#include "net.h"
28#include "platform.h"
29#include "peer-mgr.h"
30#include "utils.h"
31
32struct tr_io_s
33{
34    tr_torrent * tor;
35};
36
37/****
38*****  Low-level IO functions
39****/
40
41enum { TR_IO_READ, TR_IO_WRITE };
42
43static int
44readOrWriteBytes ( const tr_torrent    * tor,
45                   int                   ioMode,
46                   int                   fileIndex,
47                   uint64_t              fileOffset,
48                   void                * buf,
49                   size_t                buflen )
50{
51    const tr_info_t * info = &tor->info;
52    const tr_file_t * file = &info->files[fileIndex];
53    typedef size_t (* iofunc) ( int, void *, size_t );
54    iofunc func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write;
55    char path[MAX_PATH_LENGTH];
56    struct stat sb;
57    int fd = -1;
58    int ret;
59
60    assert ( 0<=fileIndex && fileIndex<info->fileCount );
61    assert ( !file->length || (fileOffset < file->length));
62    assert ( fileOffset + buflen <= file->length );
63
64    tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL );
65
66    if( !file->length )
67        return 0;
68    else if ((ioMode==TR_IO_READ) && stat( path, &sb ) ) /* fast check to make sure file exists */
69        ret = tr_ioErrorFromErrno ();
70    else if ((fd = tr_fdFileOpen ( tor->destination, file->name, ioMode==TR_IO_WRITE )) < 0)
71        ret = fd;
72    else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
73        ret = TR_ERROR_IO_OTHER;
74    else if( func( fd, buf, buflen ) != buflen )
75        ret = tr_ioErrorFromErrno ();
76    else
77        ret = TR_OK;
78 
79    if( fd >= 0 )
80        tr_fdFileRelease( fd );
81
82    return ret;
83}
84
85static void
86findFileLocation ( const tr_torrent * tor,
87                   int                  pieceIndex,
88                   int                  pieceOffset,
89                   int                * fileIndex,
90                   uint64_t           * fileOffset )
91{
92    const tr_info_t * info = &tor->info;
93
94    int i;
95    uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
96
97    assert ( 0<=pieceIndex && pieceIndex < info->pieceCount );
98    assert ( 0<=tor->info.pieceSize );
99    assert ( pieceOffset < tr_torPieceCountBytes( tor, pieceIndex ) );
100    assert ( piecePos < info->totalSize );
101
102    for ( i=0; info->files[i].length<=piecePos; ++i )
103      piecePos -= info->files[i].length;
104
105    *fileIndex = i;
106    *fileOffset = piecePos;
107
108    assert ( 0<=*fileIndex && *fileIndex<info->fileCount );
109    assert ( *fileOffset < info->files[i].length );
110}
111
112static int
113ensureMinimumFileSize ( const tr_torrent  * tor,
114                        int                   fileIndex,
115                        uint64_t              minSize ) /* in bytes */
116{
117    int fd;
118    int ret;
119    struct stat sb;
120    const tr_file_t * file = &tor->info.files[fileIndex];
121
122    assert ( 0<=fileIndex && fileIndex<tor->info.fileCount );
123    assert ( minSize <= file->length );
124
125    fd = tr_fdFileOpen( tor->destination, file->name, TRUE );
126    if( fd < 0 ) /* bad fd */
127        ret = fd;
128    else if (fstat (fd, &sb) ) /* how big is the file? */
129        ret = tr_ioErrorFromErrno ();
130    else if (sb.st_size >= (off_t)minSize) /* already big enough */
131        ret = TR_OK;
132    else if (!ftruncate( fd, minSize )) /* grow it */
133        ret = TR_OK;
134    else /* couldn't grow it */
135        ret = tr_ioErrorFromErrno ();
136
137    if( fd >= 0 )
138        tr_fdFileRelease( fd );
139
140    return ret;
141}
142
143static int
144readOrWritePiece ( tr_torrent       * tor,
145                   int                ioMode,
146                   int                pieceIndex,
147                   int                pieceOffset,
148                   uint8_t          * buf,
149                   size_t             buflen )
150{
151    int ret = 0;
152    int fileIndex;
153    uint64_t fileOffset;
154    const tr_info_t * info = &tor->info;
155
156    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
157    assert( buflen <= (size_t) tr_torPieceCountBytes( tor, pieceIndex ) );
158
159    findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
160
161    while( buflen && !ret )
162    {
163        const tr_file_t * file = &info->files[fileIndex];
164        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
165
166        if( ioMode == TR_IO_WRITE )
167            ret = ensureMinimumFileSize( tor, fileIndex,
168                                         fileOffset + bytesThisPass );
169        if( !ret )
170            ret = readOrWriteBytes( tor, ioMode,
171                                    fileIndex, fileOffset, buf, bytesThisPass );
172        buf += bytesThisPass;
173        buflen -= bytesThisPass;
174        fileIndex++;
175        fileOffset = 0;
176    }
177
178    return ret;
179}
180
181int
182tr_ioRead( tr_torrent * tor, int pieceIndex, int begin, int len, uint8_t * buf )
183{
184    return readOrWritePiece ( tor, TR_IO_READ, pieceIndex, begin, buf, len );
185}
186
187int
188tr_ioWrite( tr_torrent * tor, int pieceIndex, int begin, int len, uint8_t * buf )
189{
190    return readOrWritePiece ( tor, TR_IO_WRITE, pieceIndex, begin, buf, len );
191}
192
193/****
194*****
195****/
196
197static int
198tr_ioRecalculateHash ( tr_torrent    * tor,
199                       int             pieceIndex,
200                       uint8_t       * setme )
201{
202    int n;
203    int ret;
204    uint8_t * buf;
205    const tr_info_t * info;
206
207    assert( tor != NULL );
208    assert( setme != NULL );
209    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
210
211    info = &tor->info;
212    n = tr_torPieceCountBytes( tor, pieceIndex );
213
214    buf = malloc( n );
215    ret = readOrWritePiece ( tor, TR_IO_READ, pieceIndex, 0, buf, n );
216    if( !ret )
217        tr_sha1( setme, buf, n, NULL );
218    free( buf );
219
220    return ret;
221}
222
223static int
224checkPiece ( tr_torrent * tor, int pieceIndex )
225{
226    uint8_t hash[SHA_DIGEST_LENGTH];
227    int ret = tr_ioRecalculateHash( tor, pieceIndex, hash )
228           || memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
229    tr_dbg ("torrent [%s] piece %d hash check: %s",
230            tor->info.name, pieceIndex, (ret?"FAILED":"OK"));
231    return ret;
232}
233
234void
235tr_ioCheckFiles( tr_torrent * tor )
236{
237    assert( tor != NULL );
238    assert( tor->completion != NULL );
239    assert( tor->info.pieceCount > 0 );
240
241    if( tor->uncheckedPieces != NULL )
242    {
243        int i;
244
245        /* remove the unchecked pieces from completion... */
246        for( i=0; i<tor->info.pieceCount; ++i ) 
247            if( tr_bitfieldHas( tor->uncheckedPieces, i ) )
248                tr_cpPieceRem( tor->completion, i );
249
250        tr_inf( "Verifying some pieces of \"%s\"", tor->info.name );
251
252        for( i=0; i<tor->info.pieceCount; ++i ) 
253        {
254            if( !tr_bitfieldHas( tor->uncheckedPieces, i ) )
255                continue;
256
257            tr_torrentSetHasPiece( tor, i, !checkPiece( tor, i ) );
258            tr_bitfieldRem( tor->uncheckedPieces, i );
259        }
260
261        tr_bitfieldFree( tor->uncheckedPieces );
262        tor->uncheckedPieces = NULL;
263        tor->fastResumeDirty = TRUE;
264    }
265}
266
267/****
268*****  Life Cycle
269****/
270
271tr_io_t*
272tr_ioNew ( tr_torrent * tor )
273{
274    tr_io_t * io = tr_calloc( 1, sizeof( tr_io_t ) );
275    io->tor = tor;
276    return io;
277}
278
279
280void
281tr_ioSync( tr_io_t * io )
282{
283    if( io != NULL )
284    {
285        int i;
286        const tr_info_t * info = &io->tor->info;
287
288        for( i=0; i<info->fileCount; ++i )
289            tr_fdFileClose( io->tor->destination, info->files[i].name );
290    }
291}
292
293void
294tr_ioClose( tr_io_t * io )
295{
296    if( io != NULL )
297    {
298        tr_ioSync( io );
299        tr_free( io );
300    }
301}
302
303int
304tr_ioHash( tr_io_t * io, int pieceIndex )
305{
306    int ret;
307    tr_torrent * tor = io->tor;
308    const int success = !checkPiece( tor, pieceIndex );
309
310    if( success )
311    {
312        tr_dbg( "Piece %d hash OK", pieceIndex );
313        tr_cpPieceAdd( tor->completion, pieceIndex );
314        ret = TR_OK;
315    }
316    else
317    {
318        tr_err( "Piece %d hash FAILED", pieceIndex );
319        tr_cpPieceRem( tor->completion, pieceIndex );
320        ret = TR_ERROR;
321    }
322
323    tr_peerMgrSetBlame( tor->handle->peerMgr, tor->info.hash, pieceIndex, success );
324
325    return ret;
326}
327
328/**
329***
330**/
331
332struct recheck_node
333{
334    tr_torrent * torrent;
335    tr_recheck_done_cb recheck_done_cb;
336};
337
338struct recheck_node currentNode;
339
340static tr_list * recheckList = NULL;
341
342static tr_thread_t * recheckThread = NULL;
343
344static int stopCurrent = FALSE;
345
346static void
347recheckThreadFunc( void * unused UNUSED )
348{
349    for( ;; )
350    {
351        int i;
352        tr_torrent * tor;
353
354        struct recheck_node * node = (struct recheck_node*) recheckList ? recheckList->data : NULL;
355        if( node == NULL )
356            break;
357
358        currentNode = *node;
359        tor = currentNode.torrent;
360        tr_list_remove_data( &recheckList, node );
361        tr_free( node );
362
363        if( tor->uncheckedPieces == NULL )
364            continue;
365
366        /* remove the unchecked pieces from completion... */
367        for( i=0; i<tor->info.pieceCount; ++i ) 
368            if( tr_bitfieldHas( tor->uncheckedPieces, i ) )
369                tr_cpPieceRem( tor->completion, i );
370
371        tr_inf( "Verifying some pieces of \"%s\"", tor->info.name );
372
373        for( i=0; i<tor->info.pieceCount && !stopCurrent; ++i ) 
374        {
375            if( !tr_bitfieldHas( tor->uncheckedPieces, i ) )
376                continue;
377
378            tr_torrentSetHasPiece( tor, i, !checkPiece( tor, i ) );
379            tr_bitfieldRem( tor->uncheckedPieces, i );
380        }
381
382        if( stopCurrent )
383        {
384            stopCurrent = FALSE;
385            (currentNode.recheck_done_cb)( tor, TR_RECHECK_ABORTED );
386        }
387        else
388        {
389            tr_bitfieldFree( tor->uncheckedPieces );
390            tor->uncheckedPieces = NULL;
391            tor->fastResumeDirty = TRUE;
392            (currentNode.recheck_done_cb)( tor, TR_RECHECK_DONE );
393        }
394    }
395
396    recheckThread = NULL;
397}
398
399void
400tr_ioRecheckAdd( tr_torrent          * tor,
401                 tr_recheck_done_cb    recheck_done_cb )
402{
403    if( tor->uncheckedPieces == NULL )
404    {
405        (*recheck_done_cb)(tor, TR_RECHECK_DONE );
406    }
407    else
408    {
409        struct recheck_node * node;
410        node = tr_new( struct recheck_node, 1 );
411        node->torrent = tor;
412        node->recheck_done_cb = recheck_done_cb;
413        tr_list_append( &recheckList, node );
414
415        if( recheckThread == NULL )
416            recheckThread = tr_threadNew( recheckThreadFunc, NULL, "recheckThreadFunc" );
417    }
418}
419
420static int
421compareRecheckByTorrent( const void * va, const void * vb )
422{
423    const struct recheck_node * a = ( const struct recheck_node * ) va;
424    const struct recheck_node * b = ( const struct recheck_node * ) vb;
425    return a->torrent - b->torrent;
426}
427
428void
429tr_ioRecheckRemove( tr_torrent * tor )
430{
431    if( tor == currentNode.torrent )
432        stopCurrent = TRUE;
433    else {
434        struct recheck_node tmp;
435        tmp.torrent = tor;
436        struct recheck_node * node = tr_list_remove( &recheckList, &tmp, compareRecheckByTorrent );
437        if( node != NULL ) {
438            (node->recheck_done_cb)( tor, TR_RECHECK_ABORTED );
439            tr_free( node );
440        }
441    }
442}
Note: See TracBrowser for help on using the repository browser.