source: trunk/libtransmission/inout.c @ 3988

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

plug in the stats.filesAdded field

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