source: trunk/libtransmission/inout.c @ 3424

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

remove dead code tr_file_stat::completionStatus

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