source: trunk/libtransmission/inout.c @ 11310

Last change on this file since 11310 was 11310, checked in by charles, 12 years ago

(trunk libT) #3631 "crash in tr_ioFindFileLocation" -- add tracer messages to try & smoke out the error

  • Property svn:keywords set to Date Rev Author Id
File size: 10.0 KB
Line 
1/*
2 * This file Copyright (C) 2007-2010 Mnemosyne LLC
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 11310 2010-10-14 04:21:14Z charles $
11 */
12
13#ifdef HAVE_LSEEK64
14 #define _LARGEFILE64_SOURCE
15#endif
16
17#include <assert.h>
18#include <errno.h>
19#include <stdlib.h> /* realloc */
20#include <string.h> /* memcmp */
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25
26#include <openssl/sha.h>
27
28#include "transmission.h"
29#include "cache.h"
30#include "crypto.h"
31#include "fdlimit.h"
32#include "inout.h"
33#include "peer-common.h" /* MAX_BLOCK_SIZE */
34#include "platform.h"
35#include "stats.h"
36#include "torrent.h"
37#include "utils.h"
38
39/****
40*****  Low-level IO functions
41****/
42
43#ifdef WIN32
44 #if defined(read)
45  #undef read
46 #endif
47 #define read  _read
48
49 #if defined(write)
50  #undef write
51 #endif
52 #define write _write
53#endif
54
55enum { TR_IO_READ, TR_IO_PREFETCH,
56       /* Any operations that require write access must follow TR_IO_WRITE. */
57       TR_IO_WRITE
58};
59
60/* returns 0 on success, or an errno on failure */
61static int
62readOrWriteBytes( tr_session       * session,
63                  const tr_torrent * tor,
64                  int                ioMode,
65                  tr_file_index_t    fileIndex,
66                  uint64_t           fileOffset,
67                  void *             buf,
68                  size_t             buflen )
69{
70    const tr_info * info = &tor->info;
71    const tr_file * file = &info->files[fileIndex];
72
73    int             fd = -1;
74    int             err = 0;
75    const tr_bool doWrite = ioMode >= TR_IO_WRITE;
76
77//if( doWrite )
78//    fprintf( stderr, "in file %s at offset %zu, writing %zu bytes; file length is %zu\n", file->name, (size_t)fileOffset, buflen, (size_t)file->length );
79
80    assert( fileIndex < info->fileCount );
81    assert( !file->length || ( fileOffset < file->length ) );
82    assert( fileOffset + buflen <= file->length );
83
84    if( !file->length )
85        return 0;
86
87    fd = tr_fdFileGetCached( session, tr_torrentId( tor ), fileIndex, doWrite );
88
89    if( fd < 0 )
90    {
91        /* the fd cache doesn't have this file...
92         * we'll need to open it and maybe create it */
93        char * subpath;
94        const char * base;
95        tr_bool fileExists;
96        tr_preallocation_mode preallocationMode;
97
98        fileExists = tr_torrentFindFile2( tor, fileIndex, &base, &subpath );
99
100        if( !fileExists )
101        {
102            base = tr_torrentGetCurrentDir( tor );
103
104            if( tr_sessionIsIncompleteFileNamingEnabled( tor->session ) )
105                subpath = tr_torrentBuildPartial( tor, fileIndex );
106            else
107                subpath = tr_strdup( file->name );
108        }
109
110        if( ( file->dnd ) || ( ioMode < TR_IO_WRITE ) )
111            preallocationMode = TR_PREALLOCATE_NONE;
112        else
113            preallocationMode = tor->session->preallocationMode;
114
115        if( ( ioMode < TR_IO_WRITE ) && !fileExists ) /* does file exist? */
116        {
117            err = ENOENT;
118        }
119        else
120        {
121            char * filename = tr_buildPath( base, subpath, NULL );
122
123            if( ( fd = tr_fdFileCheckout( session, tor->uniqueId, fileIndex, filename,
124                                          doWrite, preallocationMode, file->length ) ) < 0 )
125            {
126                err = errno;
127                tr_torerr( tor, "tr_fdFileCheckout failed for \"%s\": %s", filename, tr_strerror( err ) );
128            }
129
130            tr_free( filename );
131        }
132
133        if( doWrite && !err )
134            tr_statsFileCreated( tor->session );
135
136        tr_free( subpath );
137    }
138
139    if( !err )
140    {
141        if( ioMode == TR_IO_READ ) {
142            const int rc = tr_pread( fd, buf, buflen, fileOffset );
143            if( rc < 0 ) {
144                err = errno;
145                tr_torerr( tor, "read failed for \"%s\": %s",
146                           file->name, tr_strerror( err ) );
147            }
148        } else if( ioMode == TR_IO_PREFETCH ) {
149            const int rc = tr_prefetch( fd, fileOffset, buflen );
150            if( rc < 0 ) {
151                tr_tordbg( tor, "prefetch failed for \"%s\": %s",
152                           file->name, tr_strerror( err ) );
153            }
154        } else if( ioMode == TR_IO_WRITE ) {
155            const int rc = tr_pwrite( fd, buf, buflen, fileOffset );
156            if( rc < 0 ) {
157                err = errno;
158                tr_torerr( tor, "write failed for \"%s\": %s",
159                           file->name, tr_strerror( err ) );
160            }
161        } else {
162            abort();
163        }
164    }
165
166    return err;
167}
168
169static int
170compareOffsetToFile( const void * a, const void * b )
171{
172    const uint64_t  offset = *(const uint64_t*)a;
173    const tr_file * file = b;
174
175    if( offset < file->offset ) return -1;
176    if( offset >= file->offset + file->length ) return 1;
177    return 0;
178}
179
180void
181tr_ioFindFileLocation( const tr_torrent * tor,
182                       tr_piece_index_t   pieceIndex,
183                       uint32_t           pieceOffset,
184                       tr_file_index_t  * fileIndex,
185                       uint64_t         * fileOffset )
186{
187    const uint64_t  offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
188    const tr_file * file;
189
190    assert( tr_isTorrent( tor ) );
191    assert( offset < tor->info.totalSize );
192
193    file = bsearch( &offset,
194                    tor->info.files, tor->info.fileCount, sizeof( tr_file ),
195                    compareOffsetToFile );
196
197if( file == NULL ) {
198unsigned int i;
199fprintf( stderr, "%s", "transmission error\n" ); 
200fprintf( stderr, "pieceIndex %d\n", (int)pieceIndex );
201fprintf( stderr, "pieceOffset %d\n", (int)pieceOffset );
202fprintf( stderr, "couldn't find file matching offset %"PRIu64"\n", offset );
203fprintf( stderr, "total torrent size %"PRIu64"\n", tor->info.totalSize );
204fprintf( stderr, "%u files\n", (unsigned int)tor->info.fileCount );
205for( i=0; i<tor->info.fileCount; ++i ) fprintf( stderr, "file #%u offset %"PRIu64" length %"PRIu64"\n", i, tor->info.files[i].offset, tor->info.files[i].length );
206}
207
208    assert( file != NULL );
209
210    *fileIndex = file - tor->info.files;
211    *fileOffset = offset - file->offset;
212
213    assert( *fileIndex < tor->info.fileCount );
214    assert( *fileOffset < file->length );
215    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
216}
217
218/* returns 0 on success, or an errno on failure */
219static int
220readOrWritePiece( tr_torrent       * tor,
221                  int                ioMode,
222                  tr_piece_index_t   pieceIndex,
223                  uint32_t           pieceOffset,
224                  uint8_t          * buf,
225                  size_t             buflen )
226{
227    int             err = 0;
228    tr_file_index_t fileIndex;
229    uint64_t        fileOffset;
230    const tr_info * info = &tor->info;
231
232    if( pieceIndex >= tor->info.pieceCount )
233        return EINVAL;
234    //if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
235    //    return EINVAL;
236
237    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
238                           &fileIndex, &fileOffset );
239
240    while( buflen && !err )
241    {
242        const tr_file * file = &info->files[fileIndex];
243        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
244
245        err = readOrWriteBytes( tor->session, tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass );
246        buf += bytesThisPass;
247        buflen -= bytesThisPass;
248//fprintf( stderr, "++fileIndex to %d\n", (int)fileIndex );
249        ++fileIndex;
250        fileOffset = 0;
251
252        if( ( err != 0 ) && (ioMode == TR_IO_WRITE ) )
253        {
254            char * path = tr_buildPath( tor->downloadDir, file->name, NULL );
255            tr_torrentSetLocalError( tor, "%s (%s)", tr_strerror( err ), path );
256            tr_free( path );
257        }
258    }
259
260    return err;
261}
262
263int
264tr_ioRead( tr_torrent       * tor,
265           tr_piece_index_t   pieceIndex,
266           uint32_t           begin,
267           uint32_t           len,
268           uint8_t          * buf )
269{
270    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
271}
272
273int
274tr_ioPrefetch( tr_torrent       * tor,
275               tr_piece_index_t   pieceIndex,
276               uint32_t           begin,
277               uint32_t           len)
278{
279    return readOrWritePiece( tor, TR_IO_PREFETCH, pieceIndex, begin,
280                             NULL, len );
281}
282
283int
284tr_ioWrite( tr_torrent       * tor,
285            tr_piece_index_t   pieceIndex,
286            uint32_t           begin,
287            uint32_t           len,
288            const uint8_t    * buf )
289{
290    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
291                             (uint8_t*)buf,
292                             len );
293}
294
295/****
296*****
297****/
298
299static tr_bool
300recalculateHash( tr_torrent       * tor,
301                 tr_piece_index_t   pieceIndex,
302                 uint8_t          * setme )
303{
304    size_t   bytesLeft;
305    uint32_t offset = 0;
306    tr_bool  success = TRUE;
307    const size_t buflen = tor->blockSize;
308    void * buffer = tr_valloc( buflen );
309    SHA_CTX  sha;
310
311    assert( tor != NULL );
312    assert( pieceIndex < tor->info.pieceCount );
313    assert( buffer != NULL );
314    assert( buflen > 0 );
315    assert( setme != NULL );
316
317    SHA1_Init( &sha );
318    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
319
320    tr_ioPrefetch( tor, pieceIndex, offset, bytesLeft );
321
322    while( bytesLeft )
323    {
324        const int len = MIN( bytesLeft, buflen );
325        success = !tr_cacheReadBlock( tor->session->cache, tor, pieceIndex, offset, len, buffer );
326        if( !success )
327            break;
328        SHA1_Update( &sha, buffer, len );
329        offset += len;
330        bytesLeft -= len;
331    }
332
333    if( success )
334        SHA1_Final( setme, &sha );
335
336    tr_free( buffer );
337    return success;
338}
339
340tr_bool
341tr_ioTestPiece( tr_torrent * tor, tr_piece_index_t piece )
342{
343    uint8_t hash[SHA_DIGEST_LENGTH];
344
345    return recalculateHash( tor, piece, hash )
346           && !memcmp( hash, tor->info.pieces[piece].hash, SHA_DIGEST_LENGTH );
347}
Note: See TracBrowser for help on using the repository browser.