source: branches/2.0x/libtransmission/inout.c @ 10962

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

(2.0x libT) #3399 "tr_ioTestPiece() can be faster" -- fixed

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