source: trunk/libtransmission/inout.c @ 12204

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

(trunk) #4138 "use stdbool.h instead of tr_bool" -- done.

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