source: trunk/libtransmission/inout.c @ 9066

Last change on this file since 9066 was 9066, checked in by charles, 13 years ago

add better logging to track down tr_ioRead() error messages reported by Rabbitbunny

  • Property svn:keywords set to Date Rev Author Id
File size: 7.9 KB
Line 
1/*
2 * This file Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.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 9066 2009-09-08 06:25:40Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15#include <stdlib.h> /* realloc */
16#include <string.h> /* memcmp */
17
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <unistd.h>
21
22#include <openssl/sha.h>
23
24#include "transmission.h"
25#include "crypto.h"
26#include "fdlimit.h"
27#include "inout.h"
28#include "platform.h"
29#include "stats.h"
30#include "torrent.h"
31#include "utils.h"
32
33/****
34*****  Low-level IO functions
35****/
36
37#ifdef WIN32
38 #if defined(read)
39  #undef read
40 #endif
41 #define read  _read
42
43 #if defined(write)
44  #undef write
45 #endif
46 #define write _write
47#endif
48
49enum { TR_IO_READ, TR_IO_WRITE };
50
51int64_t
52tr_lseek( int fd, int64_t offset, int whence )
53{
54#if defined(_LARGEFILE_SOURCE)
55    return lseek64( fd, (off64_t)offset, whence );
56#elif defined(WIN32)
57    return _lseeki64( fd, offset, whence );
58#else
59    return lseek( fd, (off_t)offset, whence );
60#endif
61}
62
63/* returns 0 on success, or an errno on failure */
64static int
65readOrWriteBytes( const tr_torrent * tor,
66                  int                ioMode,
67                  tr_file_index_t    fileIndex,
68                  uint64_t           fileOffset,
69                  void *             buf,
70                  size_t             buflen )
71{
72    const tr_info * info = &tor->info;
73    const tr_file * file = &info->files[fileIndex];
74    tr_preallocation_mode preallocationMode;
75
76    typedef size_t ( *iofunc )( int, void *, size_t );
77    iofunc          func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write;
78    struct stat     sb;
79    int             fd = -1;
80    int             err;
81    int             fileExists;
82
83    assert( tor->downloadDir && *tor->downloadDir );
84    assert( fileIndex < info->fileCount );
85    assert( !file->length || ( fileOffset < file->length ) );
86    assert( fileOffset + buflen <= file->length );
87
88    {
89        char path[MAX_PATH_LENGTH];
90        tr_snprintf( path, sizeof( path ), "%s%c%s", tor->downloadDir, TR_PATH_DELIMITER, file->name );
91        fileExists = !stat( path, &sb );
92    }
93
94    if( !file->length )
95        return 0;
96
97    if( ( file->dnd ) || ( ioMode != TR_IO_WRITE ) )
98        preallocationMode = TR_PREALLOCATE_NONE;
99    else
100        preallocationMode = tor->session->preallocationMode;
101
102    if( ( ioMode == TR_IO_READ ) && !fileExists ) /* does file exist? */
103        err = ENOENT;
104    else if( ( fd = tr_fdFileCheckout( tor->uniqueId, tor->downloadDir, file->name, ioMode == TR_IO_WRITE, preallocationMode, file->length ) ) < 0 ) {
105        err = errno;
106        tr_torerr( tor, "tr_fdFileCheckout failed for \"%s\": %s", file->name, tr_strerror( err ) );
107    }
108    else if( tr_lseek( fd, (int64_t)fileOffset, SEEK_SET ) == -1 ) {
109        err = errno;
110        tr_torerr( tor, "tr_lseek failed for \"%s\": %s", file->name, tr_strerror( err ) );
111    }
112    else if( func( fd, buf, buflen ) != buflen ) {
113        err = errno;
114        tr_torerr( tor, "read/write failed for \"%s\": %s", file->name, tr_strerror( err ) );
115    }
116    else
117        err = 0;
118
119    if( ( !err ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE ) )
120        tr_statsFileCreated( tor->session );
121
122    return err;
123}
124
125static int
126compareOffsetToFile( const void * a,
127                     const void * b )
128{
129    const uint64_t  offset = *(const uint64_t*)a;
130    const tr_file * file = b;
131
132    if( offset < file->offset ) return -1;
133    if( offset >= file->offset + file->length ) return 1;
134    return 0;
135}
136
137void
138tr_ioFindFileLocation( const tr_torrent * tor,
139                       tr_piece_index_t   pieceIndex,
140                       uint32_t           pieceOffset,
141                       tr_file_index_t  * fileIndex,
142                       uint64_t         * fileOffset )
143{
144    const uint64_t  offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
145    const tr_file * file;
146
147    file = bsearch( &offset,
148                    tor->info.files, tor->info.fileCount, sizeof( tr_file ),
149                    compareOffsetToFile );
150
151    *fileIndex = file - tor->info.files;
152    *fileOffset = offset - file->offset;
153
154    assert( *fileIndex < tor->info.fileCount );
155    assert( *fileOffset < file->length );
156    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
157}
158
159/* returns 0 on success, or an errno on failure */
160static int
161readOrWritePiece( tr_torrent       * tor,
162                  int                ioMode,
163                  tr_piece_index_t   pieceIndex,
164                  uint32_t           pieceOffset,
165                  uint8_t          * buf,
166                  size_t             buflen )
167{
168    int             err = 0;
169    tr_file_index_t fileIndex;
170    uint64_t        fileOffset;
171    const tr_info * info = &tor->info;
172
173    if( pieceIndex >= tor->info.pieceCount )
174        return EINVAL;
175    if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
176        return EINVAL;
177
178    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
179                           &fileIndex, &fileOffset );
180
181    while( buflen && !err )
182    {
183        const tr_file * file = &info->files[fileIndex];
184        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
185
186        err = readOrWriteBytes( tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass );
187        buf += bytesThisPass;
188        buflen -= bytesThisPass;
189        ++fileIndex;
190        fileOffset = 0;
191
192        if( err ) {
193            char * path = tr_buildPath( tor->downloadDir, file->name, NULL );
194            tr_torrentSetLocalError( tor, "%s (%s)", tr_strerror( err ), path );
195            tr_free( path );
196        }
197    }
198
199    return err;
200}
201
202int
203tr_ioRead( tr_torrent       * tor,
204           tr_piece_index_t   pieceIndex,
205           uint32_t           begin,
206           uint32_t           len,
207           uint8_t          * buf )
208{
209    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
210}
211
212int
213tr_ioWrite( tr_torrent       * tor,
214            tr_piece_index_t   pieceIndex,
215            uint32_t           begin,
216            uint32_t           len,
217            const uint8_t    * buf )
218{
219    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
220                             (uint8_t*)buf,
221                             len );
222}
223
224/****
225*****
226****/
227
228static tr_bool
229recalculateHash( tr_torrent       * tor,
230                 tr_piece_index_t   pieceIndex,
231                 void             * buffer,
232                 size_t             buflen,
233                 uint8_t          * setme )
234{
235    size_t   bytesLeft;
236    uint32_t offset = 0;
237    tr_bool  success = TRUE;
238    uint8_t  stackbuf[MAX_STACK_ARRAY_SIZE];
239    SHA_CTX  sha;
240
241    /* fallback buffer */
242    if( ( buffer == NULL ) || ( buflen < 1 ) )
243    {
244        buffer = stackbuf;
245        buflen = sizeof( stackbuf );
246    }
247
248    assert( tor != NULL );
249    assert( pieceIndex < tor->info.pieceCount );
250    assert( buffer != NULL );
251    assert( buflen > 0 );
252    assert( setme != NULL );
253
254    SHA1_Init( &sha );
255    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
256
257    while( bytesLeft )
258    {
259        const int len = MIN( bytesLeft, buflen );
260        success = !tr_ioRead( tor, pieceIndex, offset, len, buffer );
261        if( !success )
262            break;
263        SHA1_Update( &sha, buffer, len );
264        offset += len;
265        bytesLeft -= len;
266    }
267
268    if( success )
269        SHA1_Final( setme, &sha );
270
271    return success;
272}
273
274tr_bool
275tr_ioTestPiece( tr_torrent        * tor,
276                tr_piece_index_t    pieceIndex,
277                void              * buffer,
278                size_t              buflen )
279{
280    uint8_t hash[SHA_DIGEST_LENGTH];
281
282    return recalculateHash( tor, pieceIndex, buffer, buflen, hash )
283           && !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
284}
Note: See TracBrowser for help on using the repository browser.