source: trunk/libtransmission/inout.c @ 2254

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

fix r2252 copy-and-paste bug. Thanks BentMyWookie?

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