source: trunk/libtransmission/inout.c @ 2149

Last change on this file since 2149 was 2149, checked in by livings124, 15 years ago

Merge file selection and torrent creation into the main branch.

The new code for these features is under a new license.

  • 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 <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                   size_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, 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, size_t pieceOffset,
67                   int * fileIndex, size_t * fileOffset )
68{
69    const tr_info_t * info = &tor->info;
70
71    int i;
72    uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
73
74    assert ( 0<=pieceIndex && pieceIndex < info->pieceCount );
75    assert ( pieceOffset < (size_t)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                        size_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                   size_t               pieceOffset,
124                   uint8_t            * buf,
125                   size_t               buflen )
126{
127    int ret = 0;
128    int fileIndex;
129    size_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    /* Release the torrent lock so the UI can still update itself if
136       this blocks for a while */
137    tr_lockUnlock( &tor->lock );
138
139    findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
140
141    while( buflen && !ret )
142    {
143        const tr_file_t * file = &info->files[fileIndex];
144        const size_t bytesThisPass = MIN( buflen, file->length - fileOffset );
145
146        if( ioMode == TR_IO_WRITE )
147            ret = ensureMinimumFileSize( tor, fileIndex,
148                                         fileOffset + bytesThisPass );
149        if( !ret )
150            ret = readOrWriteBytes( tor, ioMode,
151                                    fileIndex, fileOffset, buf, bytesThisPass );
152        buf += bytesThisPass;
153        buflen -= bytesThisPass;
154        fileIndex++;
155        fileOffset = 0;
156    }
157
158    tr_lockLock( &tor->lock );
159
160    return ret;
161}
162
163int
164tr_ioRead( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
165{
166    return readOrWritePiece ( io->tor, TR_IO_READ, pieceIndex, begin, buf, len );
167}
168
169int
170tr_ioWrite( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf )
171{
172    return readOrWritePiece ( io->tor, TR_IO_WRITE, pieceIndex, begin, buf, len );
173}
174
175/****
176*****
177****/
178
179static int
180tr_ioRecalculateHash ( tr_torrent_t  * tor,
181                       int             pieceIndex,
182                       uint8_t       * setme )
183{
184    int n;
185    int ret;
186    uint8_t * buf;
187    const tr_info_t * info;
188
189    assert( tor != NULL );
190    assert( setme != NULL );
191    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
192
193    info = &tor->info;
194    n = tr_pieceSize( pieceIndex );
195
196    buf = malloc( n );
197    ret = readOrWritePiece ( tor, TR_IO_READ, pieceIndex, 0, buf, n );
198    if( !ret ) {
199        SHA1( buf, n, setme );
200    }
201    free( buf );
202
203    return ret;
204}
205
206static int
207checkPiece ( tr_torrent_t * tor, int pieceIndex )
208{
209    uint8_t hash[SHA_DIGEST_LENGTH];
210    int ret = tr_ioRecalculateHash( tor, pieceIndex, hash )
211           || memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
212    tr_dbg ("torrent [%s] piece %d hash check: %s",
213            tor->info.name, pieceIndex, (ret?"FAILED":"OK"));
214    return ret;
215}
216
217static void
218checkFiles( tr_io_t * io )
219{
220    int i;
221    tr_torrent_t * tor = io->tor;
222
223    tr_bitfieldClear( io->uncheckedPieces );
224
225    if ( fastResumeLoad( io->tor, io->uncheckedPieces ) )
226        tr_bitfieldAddRange( io->uncheckedPieces, 0, tor->info.pieceCount-1 );
227
228    for( i=0; i<tor->info.pieceCount; ++i ) 
229    {
230        if( tor->status & TR_STATUS_STOPPING )
231            break;
232
233        if( !tr_bitfieldHas( io->uncheckedPieces, i ) )
234            continue;
235
236        tr_inf ( "Checking piece %d because it's not in fast-resume", i );
237
238        if( !checkPiece( tor, i ) )
239           tr_cpPieceAdd( tor->completion, i );
240        else
241           tr_cpPieceRem( tor->completion, i );
242
243        tr_bitfieldRem( io->uncheckedPieces, i );
244    }
245}
246
247/****
248*****  Life Cycle
249****/
250
251tr_io_t*
252tr_ioInit( tr_torrent_t * tor )
253{
254    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
255    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
256    io->tor = tor;
257    checkFiles( io );
258    return io;
259}
260
261void
262tr_ioSync( tr_io_t * io )
263{
264    int i;
265    const tr_info_t * info = &io->tor->info;
266
267    for( i=0; i<info->fileCount; ++i )
268        tr_fdFileClose( io->tor->destination, info->files[i].name );
269
270    if( tr_bitfieldIsEmpty( io->uncheckedPieces ) )
271        fastResumeSave( io->tor );
272}
273
274void
275tr_ioClose( tr_io_t * io )
276{
277    tr_ioSync( io );
278
279    tr_bitfieldFree( io->uncheckedPieces );
280    free( io );
281}
282
283
284/* try to load the fast resume file */
285void
286tr_ioLoadResume( tr_torrent_t * tor )
287{
288    tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
289    io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
290    io->tor = tor;
291    fastResumeLoad( tor, io->uncheckedPieces );
292    tor->ioLoaded = 1;
293    tr_bitfieldFree( io->uncheckedPieces );
294    free( io );
295}
296
297void tr_ioRemoveResume( tr_torrent_t * tor )
298{
299    if( !tor->io )
300        fastResumeRemove( tor );
301}
302
303int
304tr_ioHash( tr_io_t * io, int pieceIndex )
305{
306    int i;
307
308    tr_torrent_t * tor = io->tor;
309    const int success = !checkPiece( tor, pieceIndex );
310    if( success )
311    {
312        tr_inf( "Piece %d hash OK", pieceIndex );
313        tr_cpPieceAdd( tor->completion, pieceIndex );
314    }
315    else
316    {
317        tr_err( "Piece %d hash FAILED", pieceIndex );
318        tr_cpPieceRem( tor->completion, pieceIndex );
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 0;
326}
Note: See TracBrowser for help on using the repository browser.