source: trunk/libtransmission/inout.c @ 12545

Last change on this file since 12545 was 12462, checked in by jordan, 11 years ago

(trunk libT) #4283 "Remove 'Prefetch failed' error message" -- done.

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