source: trunk/libtransmission/inout.c @ 2185

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

add color-coded lines to the gtk debug window -- info is black, debug is grey, errors are red...

  • Property svn:keywords set to Date Rev Author Id
File size: 8.6 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
11#include <stdlib.h> /* for calloc */
12#include "transmission.h"
13
14struct tr_io_s
15{
16    tr_torrent_t * tor;
17
18    tr_bitfield_t * uncheckedPieces;
19};
20
21#include "fastresume.h"
22
23/****
24*****  Low-level IO functions
25****/
26
27enum { TR_IO_READ, TR_IO_WRITE };
28
29static int
30readOrWriteBytes ( const tr_torrent_t  * tor,
31                   int                   ioMode,
32                   int                   fileIndex,
33                   uint64_t              fileOffset,
34                   void                * buf,
35                   size_t                buflen )
36{
37    const tr_info_t * info = &tor->info;
38    const tr_file_t * file = &info->files[fileIndex];
39    int fd, ret;
40    typedef size_t (* iofunc) ( int, void *, size_t );
41    iofunc func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write;
42
43    assert ( 0<=fileIndex && fileIndex<info->fileCount );
44    assert ( !file->length || (fileOffset < file->length));
45    assert ( fileOffset + buflen <= file->length );
46
47    if( !file->length )
48        return 0;
49    else if ((fd = tr_fdFileOpen ( tor->destination, file->name, TRUE )) < 0)
50        ret = fd;
51    else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
52        ret = TR_ERROR_IO_OTHER;
53    else if( func( fd, buf, buflen ) != buflen )
54        ret = tr_ioErrorFromErrno ();
55    else
56        ret = TR_OK;
57 
58    if( fd >= 0 )
59        tr_fdFileRelease( fd );
60
61    return ret;
62}
63
64static void
65findFileLocation ( const tr_torrent_t * tor,
66                   int                  pieceIndex,
67                   int                  pieceOffset,
68                   int                * fileIndex,
69                   uint64_t           * fileOffset )
70{
71    const tr_info_t * info = &tor->info;
72
73    int i;
74    uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
75
76    assert ( 0<=pieceIndex && pieceIndex < info->pieceCount );
77    assert ( 0<=tor->info.pieceSize );
78    assert ( pieceOffset < tr_pieceSize(pieceIndex) );
79    assert ( piecePos < info->totalSize );
80
81    for ( i=0; info->files[i].length<=piecePos; ++i )
82      piecePos -= info->files[i].length;
83
84    *fileIndex = i;
85    *fileOffset = piecePos;
86
87    assert ( 0<=*fileIndex && *fileIndex<info->fileCount );
88    assert ( *fileOffset < info->files[i].length );
89}
90
91static int
92ensureMinimumFileSize ( const tr_torrent_t  * tor,
93                        int                   fileIndex,
94                        uint64_t              minSize ) /* in bytes */
95{
96    int fd;
97    int ret;
98    struct stat sb;
99    const tr_file_t * file = &tor->info.files[fileIndex];
100
101    assert ( 0<=fileIndex && fileIndex<tor->info.fileCount );
102    assert ( minSize <= file->length );
103
104    fd = tr_fdFileOpen( tor->destination, file->name, TRUE );
105    if( fd < 0 ) /* bad fd */
106        ret = fd;
107    else if (fstat (fd, &sb) ) /* how big is the file? */
108        ret = tr_ioErrorFromErrno ();
109    else if ((size_t)sb.st_size >= minSize) /* already big enough */
110        ret = TR_OK;
111    else if (!ftruncate( fd, minSize )) /* grow it */
112        ret = TR_OK;
113    else /* couldn't grow it */
114        ret = tr_ioErrorFromErrno ();
115
116    if( fd >= 0 )
117        tr_fdFileRelease( fd );
118
119    return ret;
120}
121
122static int
123readOrWritePiece ( tr_torrent_t       * tor,
124                   int                  ioMode,
125                   int                  pieceIndex,
126                   int                  pieceOffset,
127                   uint8_t            * buf,
128                   size_t               buflen )
129{
130    int ret = 0;
131    int fileIndex;
132    uint64_t fileOffset;
133    const tr_info_t * info = &tor->info;
134
135    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
136    assert( buflen <= (size_t) tr_pieceSize( pieceIndex ) );
137
138    /* Release the torrent lock so the UI can still update itself if
139       this blocks for a while */
140    tr_lockUnlock( &tor->lock );
141
142    findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
143
144    while( buflen && !ret )
145    {
146        const tr_file_t * file = &info->files[fileIndex];
147        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
148
149        if( ioMode == TR_IO_WRITE )
150            ret = ensureMinimumFileSize( tor, fileIndex,
151                                         fileOffset + bytesThisPass );
152        if( !ret )
153            ret = readOrWriteBytes( tor, ioMode,
154                                    fileIndex, fileOffset, buf, bytesThisPass );
155        buf += bytesThisPass;
156        buflen -= bytesThisPass;
157        fileIndex++;
158        fileOffset = 0;
159    }
160
161    tr_lockLock( &tor->lock );
162
163    return ret;
164}
165
166int
167tr_ioRead( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
168{
169    return readOrWritePiece ( io->tor, TR_IO_READ, pieceIndex, begin, buf, len );
170}
171
172int
173tr_ioWrite( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
174{
175    return readOrWritePiece ( io->tor, TR_IO_WRITE, pieceIndex, begin, buf, len );
176}
177
178/****
179*****
180****/
181
182static int
183tr_ioRecalculateHash ( tr_torrent_t  * tor,
184                       int             pieceIndex,
185                       uint8_t       * setme )
186{
187    int n;
188    int ret;
189    uint8_t * buf;
190    const tr_info_t * info;
191
192    assert( tor != NULL );
193    assert( setme != NULL );
194    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
195
196    info = &tor->info;
197    n = tr_pieceSize( pieceIndex );
198
199    buf = malloc( n );
200    ret = readOrWritePiece ( tor, TR_IO_READ, pieceIndex, 0, buf, n );
201    if( !ret ) {
202        SHA1( buf, n, setme );
203    }
204    free( buf );
205
206    return ret;
207}
208
209static int
210checkPiece ( tr_torrent_t * tor, int pieceIndex )
211{
212    uint8_t hash[SHA_DIGEST_LENGTH];
213    int ret = tr_ioRecalculateHash( tor, pieceIndex, hash )
214           || memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
215    tr_dbg ("torrent [%s] piece %d hash check: %s",
216            tor->info.name, pieceIndex, (ret?"FAILED":"OK"));
217    return ret;
218}
219
220static void
221checkFiles( tr_io_t * io )
222{
223    int i;
224    tr_torrent_t * tor = io->tor;
225
226    tr_bitfieldClear( io->uncheckedPieces );
227
228    if( fastResumeLoad( io->tor, io->uncheckedPieces ) )
229        tr_bitfieldAddRange( io->uncheckedPieces, 0, tor->info.pieceCount-1 );
230
231    if( !tr_bitfieldIsEmpty( io->uncheckedPieces ) )
232        tr_inf( "Rechecking portions of \"%s\"", tor->info.name );
233
234    for( i=0; i<tor->info.pieceCount; ++i ) 
235    {
236        if( tor->status & TR_STATUS_STOPPING )
237            break;
238
239        if( !tr_bitfieldHas( io->uncheckedPieces, i ) )
240            continue;
241
242        tr_dbg ( "Checking piece %d because it's not in fast-resume", i );
243
244        if( !checkPiece( tor, i ) )
245           tr_cpPieceAdd( tor->completion, i );
246        else
247           tr_cpPieceRem( tor->completion, i );
248
249        tr_bitfieldRem( io->uncheckedPieces, i );
250    }
251}
252
253/****
254*****  Life Cycle
255****/
256
257tr_io_t*
258tr_ioInit( tr_torrent_t * tor )
259{
260    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
261    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
262    io->tor = tor;
263    checkFiles( io );
264    return io;
265}
266
267void
268tr_ioSync( tr_io_t * io )
269{
270    int i;
271    const tr_info_t * info = &io->tor->info;
272
273    for( i=0; i<info->fileCount; ++i )
274        tr_fdFileClose( io->tor->destination, info->files[i].name );
275
276    if( tr_bitfieldIsEmpty( io->uncheckedPieces ) )
277        fastResumeSave( io->tor );
278}
279
280void
281tr_ioClose( tr_io_t * io )
282{
283    tr_ioSync( io );
284
285    tr_bitfieldFree( io->uncheckedPieces );
286    free( io );
287}
288
289
290/* try to load the fast resume file */
291void
292tr_ioLoadResume( tr_torrent_t * tor )
293{
294    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
295    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
296    io->tor = tor;
297    fastResumeLoad( tor, io->uncheckedPieces );
298    tor->ioLoaded = 1;
299    tr_bitfieldFree( io->uncheckedPieces );
300    free( io );
301}
302
303void tr_ioRemoveResume( tr_torrent_t * tor )
304{
305    if( !tor->io )
306        fastResumeRemove( tor );
307}
308
309int
310tr_ioHash( tr_io_t * io, int pieceIndex )
311{
312    int i;
313
314    tr_torrent_t * tor = io->tor;
315    const int success = !checkPiece( tor, pieceIndex );
316    if( success )
317    {
318        tr_inf( "Piece %d hash OK", pieceIndex );
319        tr_cpPieceAdd( tor->completion, pieceIndex );
320    }
321    else
322    {
323        tr_err( "Piece %d hash FAILED", pieceIndex );
324        tr_cpPieceRem( tor->completion, pieceIndex );
325    }
326
327    /* Assign blame or credit to peers */
328    for( i = 0; i < tor->peerCount; ++i )
329        tr_peerBlame( tor->peers[i], pieceIndex, success );
330
331    return 0;
332}
Note: See TracBrowser for help on using the repository browser.