source: trunk/libtransmission/inout.c @ 5042

Last change on this file since 5042 was 5042, checked in by charles, 14 years ago

modify "verify local data" to not lose the blocks in incomplete pieces

  • Property svn:keywords set to Date Rev Author Id
File size: 8.0 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 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 * $Id: inout.c 5042 2008-02-15 16:00:46Z charles $
11 */
12
13#include <assert.h>
14#include <stdlib.h>
15#include <string.h>
16
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <unistd.h>
20
21#include <openssl/sha.h>
22
23#include "transmission.h"
24#include "completion.h"
25#include "crypto.h"
26#include "fastresume.h"
27#include "fdlimit.h"
28#include "inout.h"
29#include "list.h"
30#include "platform.h"
31#include "peer-mgr.h"
32#include "stats.h"
33#include "torrent.h"
34#include "utils.h"
35
36/****
37*****  Low-level IO functions
38****/
39
40#ifdef WIN32
41#define lseek _lseeki64
42#endif
43
44enum { TR_IO_READ, TR_IO_WRITE };
45
46static tr_errno
47readOrWriteBytes( const tr_torrent  * tor,
48                  int                 ioMode,
49                  int                 fileIndex,
50                  uint64_t            fileOffset,
51                  void              * buf,
52                  size_t              buflen )
53{
54    const tr_info * info = &tor->info;
55    const tr_file * file = &info->files[fileIndex];
56    typedef size_t (* iofunc) ( int, void *, size_t );
57    iofunc func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write;
58    char path[MAX_PATH_LENGTH];
59    struct stat sb;
60    int fd = -1;
61    int err;
62    int fileExists;
63
64    assert( 0<=fileIndex && fileIndex<info->fileCount );
65    assert( !file->length || (fileOffset < file->length));
66    assert( fileOffset + buflen <= file->length );
67
68    tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL );
69    fileExists = !stat( path, &sb );
70
71    if( !file->length )
72        return TR_OK;
73
74    if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */
75        err = tr_ioErrorFromErrno( errno );
76    else if ((fd = tr_fdFileCheckout ( tor->destination, file->name, ioMode==TR_IO_WRITE )) < 0)
77        err = fd;
78    else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
79        err = tr_ioErrorFromErrno( errno );
80    else if( func( fd, buf, buflen ) != buflen )
81        err = tr_ioErrorFromErrno( errno );
82    else
83        err = TR_OK;
84
85    if( ( err==TR_OK ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE) )
86        tr_statsFileCreated( tor->handle );
87 
88    if( fd >= 0 )
89        tr_fdFileReturn( fd );
90
91    return err;
92}
93
94static tr_errno
95findFileLocation( const tr_torrent * tor,
96                  int                pieceIndex,
97                  int                pieceOffset,
98                  int              * fileIndex,
99                  uint64_t         * fileOffset )
100{
101    const tr_info * info = &tor->info;
102
103    int i;
104    uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
105
106    if( pieceIndex < 0 || pieceIndex >= info->pieceCount )
107        return TR_ERROR_ASSERT;
108    if( pieceOffset >= tr_torPieceCountBytes( tor, pieceIndex ) )
109        return TR_ERROR_ASSERT;
110    if( piecePos >= info->totalSize )
111        return TR_ERROR_ASSERT;
112
113    for( i=0; info->files[i].length<=piecePos; ++i )
114        piecePos -= info->files[i].length;
115
116    *fileIndex = i;
117    *fileOffset = piecePos;
118
119    assert( 0<=*fileIndex && *fileIndex<info->fileCount );
120    assert( *fileOffset < info->files[i].length );
121    return 0;
122}
123
124#ifdef WIN32
125static tr_errno
126ensureMinimumFileSize( const tr_torrent  * tor,
127                       int                 fileIndex,
128                       uint64_t            minBytes )
129{
130    int fd;
131    tr_errno err;
132    struct stat sb;
133    const tr_file * file = &tor->info.files[fileIndex];
134
135    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
136    assert( minBytes <= file->length );
137
138    fd = tr_fdFileCheckout( tor->destination, file->name, TRUE );
139    if( fd < 0 ) /* bad fd */
140        err = fd;
141    else if (fstat (fd, &sb) ) /* how big is the file? */
142        err = tr_ioErrorFromErrno( errno );
143    else if (sb.st_size >= (off_t)minBytes) /* already big enough */
144        err = TR_OK;
145    else if ( !ftruncate( fd, minBytes ) ) /* grow it */
146        err = TR_OK;
147    else /* couldn't grow it */
148        err = tr_ioErrorFromErrno( errno );
149
150    if( fd >= 0 )
151        tr_fdFileReturn( fd );
152
153    return err;
154}
155#endif
156
157static tr_errno
158readOrWritePiece( tr_torrent  * tor,
159                  int           ioMode,
160                  int           pieceIndex,
161                  int           pieceOffset,
162                  uint8_t     * buf,
163                  size_t        buflen )
164{
165    tr_errno err = 0;
166    int fileIndex;
167    uint64_t fileOffset;
168    const tr_info * info = &tor->info;
169
170    if( pieceIndex < 0 || pieceIndex >= tor->info.pieceCount )
171        err = TR_ERROR_ASSERT;
172    else if( buflen > ( size_t ) tr_torPieceCountBytes( tor, pieceIndex ) )
173        err = TR_ERROR_ASSERT;
174
175    if( !err )
176        err = findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
177
178    while( buflen && !err )
179    {
180        const tr_file * file = &info->files[fileIndex];
181        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
182
183#ifdef WIN32
184        if( ioMode == TR_IO_WRITE )
185            err = ensureMinimumFileSize( tor, fileIndex,
186                                         fileOffset + bytesThisPass );
187        if( !err )
188#endif
189            err = readOrWriteBytes( tor, ioMode,
190                                    fileIndex, fileOffset, buf, bytesThisPass );
191        buf += bytesThisPass;
192        buflen -= bytesThisPass;
193        fileIndex++;
194        fileOffset = 0;
195    }
196
197    return err;
198}
199
200tr_errno
201tr_ioRead( const tr_torrent  * tor,
202           int                 pieceIndex,
203           int                 begin,
204           int                 len,
205           uint8_t           * buf )
206{
207    return readOrWritePiece( (tr_torrent*)tor, TR_IO_READ, pieceIndex, begin, buf, len );
208}
209
210tr_errno
211tr_ioWrite( tr_torrent     * tor,
212            int              pieceIndex,
213            int              begin,
214            int              len,
215            const uint8_t  * buf )
216{
217    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin, (uint8_t*)buf, len );
218}
219
220/****
221*****
222****/
223
224static int
225tr_ioRecalculateHash( const tr_torrent  * tor,
226                      int                 pieceIndex,
227                      uint8_t           * setme )
228{
229    int offset;
230    int bytesLeft;
231    uint8_t buf[4096];
232    const tr_info * info;
233    SHA_CTX sha;
234
235    assert( tor != NULL );
236    assert( setme != NULL );
237    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
238
239    info = &tor->info;
240    offset = 0;
241    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
242    SHA1_Init( &sha );
243
244    while( bytesLeft > 0 )
245    {
246        const int bytesThisPass = MIN( bytesLeft, (int)sizeof(buf) );
247        int err = tr_ioRead( tor, pieceIndex, offset, bytesThisPass, buf );
248        if( err )
249            return err;
250        SHA1_Update( &sha, buf, bytesThisPass );
251        bytesLeft -= bytesThisPass;
252        offset += bytesThisPass;
253    }
254
255    SHA1_Final( setme, &sha );
256    return 0;
257}
258
259int
260tr_ioTestPiece( const tr_torrent * tor, int pieceIndex )
261{
262    uint8_t hash[SHA_DIGEST_LENGTH];
263    const int ret = tr_ioRecalculateHash( tor, pieceIndex, hash )
264        || memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
265    tr_dbg ("torrent [%s] piece %d hash check: %s",
266            tor->info.name, pieceIndex, ( ret ? "FAILED" : "OK" ));
267    return ret;
268}
269
270int
271tr_ioHash( tr_torrent * tor, int pieceIndex )
272{
273    int ret;
274    const int success = !tr_ioTestPiece( tor, pieceIndex );
275
276    if( success )
277    {
278        tr_dbg( "Piece %d hash OK", pieceIndex );
279        tr_cpPieceAdd( tor->completion, pieceIndex );
280        ret = TR_OK;
281    }
282    else
283    {
284        tr_err( "Piece %d hash FAILED", pieceIndex );
285        tr_cpPieceRem( tor->completion, pieceIndex );
286        ret = TR_ERROR;
287    }
288
289    tr_peerMgrSetBlame( tor->handle->peerMgr, tor->info.hash,
290                        pieceIndex, success );
291
292    return ret;
293}
Note: See TracBrowser for help on using the repository browser.