source: trunk/libtransmission/inout.c @ 10810

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

(trunk libT) silence a cache log message

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