source: trunk/libtransmission/inout.c @ 10961

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

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

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