source: trunk/libtransmission/inout.c @ 7249

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

(libT) extra assertions and safeguards for #1523: crash in tr_fdFileCheckout() when starting daemon

  • Property svn:keywords set to Date Rev Author Id
File size: 6.8 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
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 7249 2008-12-03 01:25:45Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15#include <stdlib.h> /* realloc */
16#include <string.h> /* memcmp */
17
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <unistd.h>
21
22#include <openssl/sha.h>
23
24#include "transmission.h"
25#include "crypto.h"
26#include "fdlimit.h"
27#include "inout.h"
28#include "platform.h"
29#include "stats.h"
30#include "torrent.h"
31#include "utils.h"
32
33/****
34*****  Low-level IO functions
35****/
36
37#ifdef WIN32
38 #if defined(read)
39  #undef read
40 #endif
41 #define read  _read
42 
43 #if defined(write)
44  #undef write
45 #endif
46 #define write _write
47#endif
48
49enum { TR_IO_READ, TR_IO_WRITE };
50
51static int64_t
52tr_lseek( int fd, int64_t offset, int whence )
53{
54#if defined(_LARGEFILE_SOURCE)
55    return lseek64( fd, (off64_t)offset, whence );
56#elif defined(WIN32)
57    return _lseeki64( fd, offset, whence );
58#else
59    return lseek( fd, (off_t)offset, whence );
60#endif
61}
62
63/* returns 0 on success, or an errno on failure */
64static int
65readOrWriteBytes( const tr_torrent * tor,
66                  int                ioMode,
67                  tr_file_index_t    fileIndex,
68                  uint64_t           fileOffset,
69                  void *             buf,
70                  size_t             buflen )
71{
72    const tr_info * info = &tor->info;
73    const tr_file * file = &info->files[fileIndex];
74
75    typedef size_t ( *iofunc )( int, void *, size_t );
76    iofunc          func = ioMode ==
77                           TR_IO_READ ? (iofunc)read : (iofunc)write;
78    char          * path;
79    struct stat     sb;
80    int             fd = -1;
81    int             err;
82    int             fileExists;
83
84    assert( tor->downloadDir && *tor->downloadDir );
85    assert( fileIndex < info->fileCount );
86    assert( !file->length || ( fileOffset < file->length ) );
87    assert( fileOffset + buflen <= file->length );
88
89    path = tr_buildPath( tor->downloadDir, file->name, NULL );
90    fileExists = !stat( path, &sb );
91    tr_free( path );
92
93    if( !file->length )
94        return 0;
95
96    if( ( ioMode == TR_IO_READ ) && !fileExists ) /* does file exist? */
97        err = errno;
98    else if( ( fd = tr_fdFileCheckout ( tor->downloadDir, file->name, ioMode == TR_IO_WRITE, !file->dnd, file->length ) ) < 0 )
99        err = errno;
100    else if( tr_lseek( fd, (int64_t)fileOffset, SEEK_SET ) == -1 )
101        err = errno;
102    else if( func( fd, buf, buflen ) != buflen )
103        err = errno;
104    else
105        err = 0;
106
107    if( ( !err ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE ) )
108        tr_statsFileCreated( tor->session );
109
110    if( fd >= 0 )
111        tr_fdFileReturn( fd );
112
113    return err;
114}
115
116static int
117compareOffsetToFile( const void * a,
118                     const void * b )
119{
120    const uint64_t  offset = *(const uint64_t*)a;
121    const tr_file * file = b;
122
123    if( offset < file->offset ) return -1;
124    if( offset >= file->offset + file->length ) return 1;
125    return 0;
126}
127
128void
129tr_ioFindFileLocation( const tr_torrent * tor,
130                       tr_piece_index_t   pieceIndex,
131                       uint32_t           pieceOffset,
132                       tr_file_index_t  * fileIndex,
133                       uint64_t         * fileOffset )
134{
135    const uint64_t  offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
136    const tr_file * file;
137
138    file = bsearch( &offset,
139                    tor->info.files, tor->info.fileCount, sizeof( tr_file ),
140                    compareOffsetToFile );
141
142    *fileIndex = file - tor->info.files;
143    *fileOffset = offset - file->offset;
144
145    assert( *fileIndex < tor->info.fileCount );
146    assert( *fileOffset < file->length );
147    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
148}
149
150/* returns 0 on success, or an errno on failure */
151static int
152readOrWritePiece( const tr_torrent * tor,
153                  int                ioMode,
154                  tr_piece_index_t   pieceIndex,
155                  uint32_t           pieceOffset,
156                  uint8_t *          buf,
157                  size_t             buflen )
158{
159    int             err = 0;
160    tr_file_index_t fileIndex;
161    uint64_t        fileOffset;
162    const tr_info * info = &tor->info;
163
164    if( pieceIndex >= tor->info.pieceCount )
165        return EINVAL;
166    if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
167        return EINVAL;
168
169    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
170                           &fileIndex, &fileOffset );
171
172    while( buflen && !err )
173    {
174        const tr_file * file = &info->files[fileIndex];
175        const uint64_t  bytesThisPass = MIN( buflen, file->length - fileOffset );
176
177        err = readOrWriteBytes( tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass );
178        buf += bytesThisPass;
179        buflen -= bytesThisPass;
180        ++fileIndex;
181        fileOffset = 0;
182    }
183
184    return err;
185}
186
187int
188tr_ioRead( const tr_torrent * tor,
189           tr_piece_index_t   pieceIndex,
190           uint32_t           begin,
191           uint32_t           len,
192           uint8_t *          buf )
193{
194    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
195}
196
197int
198tr_ioWrite( const tr_torrent * tor,
199            tr_piece_index_t   pieceIndex,
200            uint32_t           begin,
201            uint32_t           len,
202            const uint8_t *    buf )
203{
204    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
205                             (uint8_t*)buf,
206                             len );
207}
208
209/****
210*****
211****/
212
213static int
214recalculateHash( const tr_torrent * tor,
215                 tr_piece_index_t   pieceIndex,
216                 uint8_t *          setme )
217{
218    size_t   bytesLeft;
219    uint32_t offset = 0;
220    int      success = TRUE;
221    SHA_CTX  sha;
222
223    assert( tor );
224    assert( setme );
225    assert( pieceIndex < tor->info.pieceCount );
226
227    SHA1_Init( &sha );
228    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
229
230    while( bytesLeft )
231    {
232        uint8_t   buf[8192];
233        const int len = MIN( bytesLeft, sizeof( buf ) );
234        success = !tr_ioRead( tor, pieceIndex, offset, len, buf );
235        if( !success )
236            break;
237        SHA1_Update( &sha, buf, len );
238        offset += len;
239        bytesLeft -= len;
240    }
241
242    if( success )
243        SHA1_Final( setme, &sha );
244
245    return success;
246}
247
248int
249tr_ioTestPiece( const tr_torrent * tor,
250                int                pieceIndex )
251{
252    uint8_t hash[SHA_DIGEST_LENGTH];
253    const int recalculated = recalculateHash( tor, pieceIndex, hash );
254    return recalculated && !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
255}
256
Note: See TracBrowser for help on using the repository browser.