source: trunk/libtransmission/inout.c @ 2462

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

set default torrent ul/dl speed limits from global ul/dl speed. as a side effect, totally decouples fastresume from inout.

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