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

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

in win32, #define lseek _lseeki64

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