source: trunk/libtransmission/inout.c @ 2348

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

fix crash on zero-byte torrents

  • 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 <sys/types.h>
12#include <sys/stat.h>
13#include <unistd.h>
14#include "transmission.h"
15#include "fastresume.h"
16#include "fdlimit.h"
17
18struct tr_io_s
19{
20    tr_torrent_t * tor;
21};
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, ioMode==TR_IO_WRITE )) < 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    findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
139
140    while( buflen && !ret )
141    {
142        const tr_file_t * file = &info->files[fileIndex];
143        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
144
145        if( ioMode == TR_IO_WRITE )
146            ret = ensureMinimumFileSize( tor, fileIndex,
147                                         fileOffset + bytesThisPass );
148        if( !ret )
149            ret = readOrWriteBytes( tor, ioMode,
150                                    fileIndex, fileOffset, buf, bytesThisPass );
151        buf += bytesThisPass;
152        buflen -= bytesThisPass;
153        fileIndex++;
154        fileOffset = 0;
155    }
156
157    return ret;
158}
159
160int
161tr_ioRead( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
162{
163    return readOrWritePiece ( io->tor, TR_IO_READ, pieceIndex, begin, buf, len );
164}
165
166int
167tr_ioWrite( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
168{
169    return readOrWritePiece ( io->tor, TR_IO_WRITE, pieceIndex, begin, buf, len );
170}
171
172/****
173*****
174****/
175
176static int
177tr_ioRecalculateHash ( tr_torrent_t  * tor,
178                       int             pieceIndex,
179                       uint8_t       * setme )
180{
181    int n;
182    int ret;
183    uint8_t * buf;
184    const tr_info_t * info;
185
186    assert( tor != NULL );
187    assert( setme != NULL );
188    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
189
190    info = &tor->info;
191    n = tr_pieceSize( pieceIndex );
192
193    buf = malloc( n );
194    ret = readOrWritePiece ( tor, TR_IO_READ, pieceIndex, 0, buf, n );
195    if( !ret ) {
196        SHA1( buf, n, setme );
197    }
198    free( buf );
199
200    return ret;
201}
202
203static int
204checkPiece ( tr_torrent_t * tor, int pieceIndex )
205{
206    uint8_t hash[SHA_DIGEST_LENGTH];
207    int ret = tr_ioRecalculateHash( tor, pieceIndex, hash )
208           || memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
209    tr_dbg ("torrent [%s] piece %d hash check: %s",
210            tor->info.name, pieceIndex, (ret?"FAILED":"OK"));
211    return ret;
212}
213
214int
215tr_ioCheckFiles( tr_torrent_t * tor, int mode )
216{
217    int i;
218    tr_bitfield_t * uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
219
220    tr_cpReset( tor->completion );
221
222    tr_bitfieldClear( uncheckedPieces );
223
224    if( (mode==TR_RECHECK_FORCE) || fastResumeLoad( tor, uncheckedPieces ) )
225        tr_bitfieldAddRange( uncheckedPieces, 0, tor->info.pieceCount );
226
227    if( tr_bitfieldIsEmpty( uncheckedPieces ) ) {
228        tr_bitfieldFree( uncheckedPieces );
229        return TR_OK;
230    }
231
232    if( mode == TR_RECHECK_FAST ) {
233        tr_bitfieldFree( uncheckedPieces );
234        return TR_ERROR_IO_OTHER;
235    }
236
237    tr_inf( "Verifying some pieces of \"%s\"", tor->info.name );
238
239    for( i=0; i<tor->info.pieceCount; ++i ) 
240    {
241        if( !tr_bitfieldHas( uncheckedPieces, i ) )
242            continue;
243
244        tr_dbg ( "Checking piece %d because it's not in fast-resume", i );
245
246        tr_torrentSetHasPiece( tor, i, !checkPiece( tor, i ) );
247
248        tr_bitfieldRem( uncheckedPieces, i );
249    }
250
251    fastResumeSave( tor );
252    tr_bitfieldFree( uncheckedPieces );
253    return TR_OK;
254}
255
256/****
257*****  Life Cycle
258****/
259
260tr_io_t*
261tr_ioInitFast( tr_torrent_t * tor )
262{
263    tr_io_t * io = tr_calloc( 1, sizeof( tr_io_t ) );
264    io->tor = tor;
265
266    if( tr_ioCheckFiles( tor, TR_RECHECK_FAST ) )
267    {
268        tr_free( io );
269        io = NULL;
270    }
271
272    return io;
273}
274
275
276void
277tr_ioSync( tr_io_t * io )
278{
279    if( io != NULL )
280    {
281        int i;
282        const tr_info_t * info = &io->tor->info;
283
284        for( i=0; i<info->fileCount; ++i )
285            tr_fdFileClose( io->tor->destination, info->files[i].name );
286
287        fastResumeSave( io->tor );
288    }
289}
290
291void
292tr_ioClose( tr_io_t * io )
293{
294    if( io != NULL )
295    {
296        tr_ioSync( io );
297        tr_free( io );
298    }
299}
300
301
302/* try to load the fast resume file */
303int
304tr_ioLoadResume( tr_torrent_t * tor )
305{
306    return tr_ioCheckFiles ( tor, TR_RECHECK_FAST );
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.