source: trunk/libtransmission/inout.c @ 2154

Last change on this file since 2154 was 2154, checked in by charles, 15 years ago
  • fix error checking large files reported by Gimp_
  • portability changes to pathname/filename building
  • small gratuitous changes
  • 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 <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    for( i=0; i<tor->info.pieceCount; ++i ) 
232    {
233        if( tor->status & TR_STATUS_STOPPING )
234            break;
235
236        if( !tr_bitfieldHas( io->uncheckedPieces, i ) )
237            continue;
238
239        tr_inf ( "Checking piece %d because it's not in fast-resume", i );
240
241        if( !checkPiece( tor, i ) )
242           tr_cpPieceAdd( tor->completion, i );
243        else
244           tr_cpPieceRem( tor->completion, i );
245
246        tr_bitfieldRem( io->uncheckedPieces, i );
247    }
248}
249
250/****
251*****  Life Cycle
252****/
253
254tr_io_t*
255tr_ioInit( tr_torrent_t * tor )
256{
257    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
258    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
259    io->tor = tor;
260    checkFiles( io );
261    return io;
262}
263
264void
265tr_ioSync( tr_io_t * io )
266{
267    int i;
268    const tr_info_t * info = &io->tor->info;
269
270    for( i=0; i<info->fileCount; ++i )
271        tr_fdFileClose( io->tor->destination, info->files[i].name );
272
273    if( tr_bitfieldIsEmpty( io->uncheckedPieces ) )
274        fastResumeSave( io->tor );
275}
276
277void
278tr_ioClose( tr_io_t * io )
279{
280    tr_ioSync( io );
281
282    tr_bitfieldFree( io->uncheckedPieces );
283    free( io );
284}
285
286
287/* try to load the fast resume file */
288void
289tr_ioLoadResume( tr_torrent_t * tor )
290{
291    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
292    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
293    io->tor = tor;
294    fastResumeLoad( tor, io->uncheckedPieces );
295    tor->ioLoaded = 1;
296    tr_bitfieldFree( io->uncheckedPieces );
297    free( io );
298}
299
300void tr_ioRemoveResume( tr_torrent_t * tor )
301{
302    if( !tor->io )
303        fastResumeRemove( tor );
304}
305
306int
307tr_ioHash( tr_io_t * io, int pieceIndex )
308{
309    int i;
310
311    tr_torrent_t * tor = io->tor;
312    const int success = !checkPiece( tor, pieceIndex );
313    if( success )
314    {
315        tr_inf( "Piece %d hash OK", pieceIndex );
316        tr_cpPieceAdd( tor->completion, pieceIndex );
317    }
318    else
319    {
320        tr_err( "Piece %d hash FAILED", pieceIndex );
321        tr_cpPieceRem( tor->completion, pieceIndex );
322    }
323
324    /* Assign blame or credit to peers */
325    for( i = 0; i < tor->peerCount; ++i )
326        tr_peerBlame( tor->peers[i], pieceIndex, success );
327
328    return 0;
329}
Note: See TracBrowser for help on using the repository browser.