source: branches/1.4x/libtransmission/inout.c @ 7339

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

(1.4x libT) #1580: increase the read buffer for the "verify local data" reader to make it a little faster

  • Property svn:keywords set to Date Rev Author Id
File size: 7.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 7339 2008-12-10 03:50:39Z 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#ifdef TR_EMBEDDED
34 #define TR_HASH_BUFSIZE (16*1024)
35#else
36 #define TR_HASH_BUFSIZE (64*1024)
37#endif
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_WRITE };
56
57static int64_t
58tr_lseek( int fd, int64_t offset, int whence )
59{
60#if defined(_LARGEFILE_SOURCE)
61    return lseek64( fd, (off64_t)offset, whence );
62#elif defined(WIN32)
63    return _lseeki64( fd, offset, whence );
64#else
65    return lseek( fd, (off_t)offset, whence );
66#endif
67}
68
69/* returns 0 on success, or an errno on failure */
70static int
71readOrWriteBytes( const tr_torrent * tor,
72                  int                ioMode,
73                  tr_file_index_t    fileIndex,
74                  uint64_t           fileOffset,
75                  void *             buf,
76                  size_t             buflen )
77{
78    const tr_info * info = &tor->info;
79    const tr_file * file = &info->files[fileIndex];
80
81    typedef size_t ( *iofunc )( int, void *, size_t );
82    iofunc          func = ioMode ==
83                           TR_IO_READ ? (iofunc)read : (iofunc)write;
84    char          * path;
85    struct stat     sb;
86    int             fd = -1;
87    int             err;
88    int             fileExists;
89
90    assert( fileIndex < info->fileCount );
91    assert( !file->length || ( fileOffset < file->length ) );
92    assert( fileOffset + buflen <= file->length );
93
94    path = tr_buildPath( tor->downloadDir, file->name, NULL );
95    fileExists = !stat( path, &sb );
96    tr_free( path );
97
98    if( !file->length )
99        return 0;
100
101    if( ( ioMode == TR_IO_READ ) && !fileExists ) /* does file exist? */
102        err = errno;
103    else if( ( fd = tr_fdFileCheckout ( tor->downloadDir, file->name, ioMode == TR_IO_WRITE, !file->dnd, file->length ) ) < 0 )
104        err = errno;
105    else if( tr_lseek( fd, (int64_t)fileOffset, SEEK_SET ) == -1 )
106        err = errno;
107    else if( func( fd, buf, buflen ) != buflen )
108        err = errno;
109    else
110        err = 0;
111
112    if( ( !err ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE ) )
113        tr_statsFileCreated( tor->session );
114
115    if( fd >= 0 )
116        tr_fdFileReturn( fd );
117
118    return err;
119}
120
121static int
122compareOffsetToFile( const void * a,
123                     const void * b )
124{
125    const uint64_t  offset = *(const uint64_t*)a;
126    const tr_file * file = b;
127
128    if( offset < file->offset ) return -1;
129    if( offset >= file->offset + file->length ) return 1;
130    return 0;
131}
132
133void
134tr_ioFindFileLocation( const tr_torrent * tor,
135                       tr_piece_index_t   pieceIndex,
136                       uint32_t           pieceOffset,
137                       tr_file_index_t  * fileIndex,
138                       uint64_t         * fileOffset )
139{
140    const uint64_t  offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
141    const tr_file * file;
142
143    file = bsearch( &offset,
144                    tor->info.files, tor->info.fileCount, sizeof( tr_file ),
145                    compareOffsetToFile );
146
147    *fileIndex = file - tor->info.files;
148    *fileOffset = offset - file->offset;
149
150    assert( *fileIndex < tor->info.fileCount );
151    assert( *fileOffset < file->length );
152    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
153}
154
155/* returns 0 on success, or an errno on failure */
156static int
157readOrWritePiece( const tr_torrent * tor,
158                  int                ioMode,
159                  tr_piece_index_t   pieceIndex,
160                  uint32_t           pieceOffset,
161                  uint8_t *          buf,
162                  size_t             buflen )
163{
164    int             err = 0;
165    tr_file_index_t fileIndex;
166    uint64_t        fileOffset;
167    const tr_info * info = &tor->info;
168
169    if( pieceIndex >= tor->info.pieceCount )
170        return EINVAL;
171    if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
172        return EINVAL;
173
174    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
175                           &fileIndex, &fileOffset );
176
177    while( buflen && !err )
178    {
179        const tr_file * file = &info->files[fileIndex];
180        const uint64_t  bytesThisPass = MIN( buflen,
181                                             file->length - fileOffset );
182
183        err = readOrWriteBytes( tor, ioMode,
184                                fileIndex, fileOffset, buf, bytesThisPass );
185        buf += bytesThisPass;
186        buflen -= bytesThisPass;
187        ++fileIndex;
188        fileOffset = 0;
189    }
190
191    return err;
192}
193
194int
195tr_ioRead( const tr_torrent * tor,
196           tr_piece_index_t   pieceIndex,
197           uint32_t           begin,
198           uint32_t           len,
199           uint8_t *          buf )
200{
201    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
202}
203
204int
205tr_ioWrite( const tr_torrent * tor,
206            tr_piece_index_t   pieceIndex,
207            uint32_t           begin,
208            uint32_t           len,
209            const uint8_t *    buf )
210{
211    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
212                             (uint8_t*)buf,
213                             len );
214}
215
216/****
217*****
218****/
219
220static int
221recalculateHash( const tr_torrent * tor,
222                 tr_piece_index_t   pieceIndex,
223                 uint8_t *          setme )
224{
225    static uint8_t * buf = NULL;
226    size_t bytesLeft;
227    uint32_t offset = 0;
228    int success = TRUE;
229    SHA_CTX  sha;
230
231    if( buf == NULL )
232        buf = tr_new( uint8_t, TR_HASH_BUFSIZE );
233
234    assert( tor );
235    assert( setme );
236    assert( pieceIndex < tor->info.pieceCount );
237
238    SHA1_Init( &sha );
239    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
240
241    while( bytesLeft )
242    {
243        const int len = MIN( bytesLeft, TR_HASH_BUFSIZE );
244        success = !tr_ioRead( tor, pieceIndex, offset, len, buf );
245        if( !success )
246            break;
247        SHA1_Update( &sha, buf, len );
248        offset += len;
249        bytesLeft -= len;
250    }
251
252    if( success )
253        SHA1_Final( setme, &sha );
254
255    return success;
256}
257
258int
259tr_ioTestPiece( const tr_torrent * tor,
260                int                pieceIndex )
261{
262    uint8_t hash[SHA_DIGEST_LENGTH];
263    const int recalculated = recalculateHash( tor, pieceIndex, hash );
264    return recalculated && !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
265}
266
Note: See TracBrowser for help on using the repository browser.