source: branches/0.8x/libtransmission/inout.c @ 2979

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

fix a nasty size width truncation bug, reported by Eric in http://transmission.m0k.org/forum/viewtopic.php?t=2304&start=16

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