source: trunk/libtransmission/inout.c @ 7061

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

(libT) #1424: one-liner to fix win32 breakage

  • Property svn:keywords set to Date Rev Author Id
File size: 7.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 7061 2008-11-06 04:25:29Z 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 #define lseek _lseeki64
39 #if defined(read)
40    #undef read
41 #endif
42 #define read  _read
43 
44 #if defined(write)
45    #undef write
46 #endif
47 #define write _write
48#endif
49
50enum { TR_IO_READ, TR_IO_WRITE };
51
52/* returns 0 on success, or an errno on failure */
53static int
54readOrWriteBytes( const tr_torrent * tor,
55                  int                ioMode,
56                  tr_file_index_t    fileIndex,
57                  uint64_t           fileOffset,
58                  void *             buf,
59                  size_t             buflen )
60{
61    const tr_info * info = &tor->info;
62    const tr_file * file = &info->files[fileIndex];
63
64    typedef size_t ( *iofunc )( int, void *, size_t );
65    iofunc          func = ioMode ==
66                           TR_IO_READ ? (iofunc)read : (iofunc)write;
67    char          * path;
68    struct stat     sb;
69    int             fd = -1;
70    int             err;
71    int             fileExists;
72
73    assert( fileIndex < info->fileCount );
74    assert( !file->length || ( fileOffset < file->length ) );
75    assert( fileOffset + buflen <= file->length );
76
77    path = tr_buildPath( tor->downloadDir, file->name, NULL );
78    fileExists = !stat( path, &sb );
79    tr_free( path );
80
81    if( !file->length )
82        return 0;
83
84    if( ( ioMode == TR_IO_READ ) && !fileExists ) /* does file exist? */
85        err = errno;
86    else if( ( fd = tr_fdFileCheckout ( tor->downloadDir, file->name, ioMode == TR_IO_WRITE, !file->dnd, file->length ) ) < 0 )
87        err = errno;
88    else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ( (off_t)-1 ) )
89        err = errno;
90    else if( func( fd, buf, buflen ) != buflen )
91        err = errno;
92    else
93        err = 0;
94
95    if( ( !err ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE ) )
96        tr_statsFileCreated( tor->session );
97
98    if( fd >= 0 )
99        tr_fdFileReturn( fd );
100
101    return err;
102}
103
104static int
105compareOffsetToFile( const void * a,
106                     const void * b )
107{
108    const uint64_t  offset = *(const uint64_t*)a;
109    const tr_file * file = b;
110
111    if( offset < file->offset ) return -1;
112    if( offset >= file->offset + file->length ) return 1;
113    return 0;
114}
115
116void
117tr_ioFindFileLocation( const tr_torrent * tor,
118                       tr_piece_index_t   pieceIndex,
119                       uint32_t           pieceOffset,
120                       tr_file_index_t *  fileIndex,
121                       uint64_t *         fileOffset )
122{
123    const uint64_t  offset = tr_pieceOffset( tor, pieceIndex, pieceOffset,
124                                             0 );
125    const tr_file * file;
126
127    file = bsearch( &offset,
128                    tor->info.files, tor->info.fileCount, sizeof( tr_file ),
129                    compareOffsetToFile );
130
131    *fileIndex = file - tor->info.files;
132    *fileOffset = offset - file->offset;
133
134    assert( *fileIndex < tor->info.fileCount );
135    assert( *fileOffset < file->length );
136    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
137}
138
139#ifdef WIN32
140/* return 0 on success, or an errno on failure */
141static int
142ensureMinimumFileSize( const tr_torrent * tor,
143                       tr_file_index_t    fileIndex,
144                       uint64_t           minBytes )
145{
146    int             fd;
147    int             err;
148    struct stat     sb;
149    const tr_file * file = &tor->info.files[fileIndex];
150
151    assert( 0 <= fileIndex && fileIndex < tor->info.fileCount );
152    assert( minBytes <= file->length );
153
154    fd = tr_fdFileCheckout( tor->downloadDir,
155                            file->name, TRUE, !file->dnd, file->length );
156
157    if( fd < 0 ) /* bad fd */
158        err = errno;
159    else if( fstat ( fd, &sb ) ) /* how big is the file? */
160        err = errno;
161    else if( sb.st_size >= (off_t)minBytes ) /* already big enough */
162        err = 0;
163    else if( !ftruncate( fd, minBytes ) )  /* grow it */
164        err = 0;
165    else /* couldn't grow it */
166        err = errno;
167
168    if( fd >= 0 )
169        tr_fdFileReturn( fd );
170
171    return err;
172}
173
174#endif
175
176/* returns 0 on success, or an errno on failure */
177static int
178readOrWritePiece( const tr_torrent * tor,
179                  int                ioMode,
180                  tr_piece_index_t   pieceIndex,
181                  uint32_t           pieceOffset,
182                  uint8_t *          buf,
183                  size_t             buflen )
184{
185    int             err = 0;
186    tr_file_index_t fileIndex;
187    uint64_t        fileOffset;
188    const tr_info * info = &tor->info;
189
190    if( pieceIndex >= tor->info.pieceCount )
191        return EINVAL;
192    if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
193        return EINVAL;
194
195    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
196                           &fileIndex, &fileOffset );
197
198    while( buflen && !err )
199    {
200        const tr_file * file = &info->files[fileIndex];
201        const uint64_t  bytesThisPass = MIN( buflen,
202                                             file->length - fileOffset );
203
204#ifdef WIN32
205        if( ioMode == TR_IO_WRITE )
206            err = ensureMinimumFileSize( tor, fileIndex,
207                                         fileOffset + bytesThisPass );
208        if( !err )
209#endif
210        err = readOrWriteBytes( tor, ioMode,
211                                fileIndex, fileOffset, buf, bytesThisPass );
212        buf += bytesThisPass;
213        buflen -= bytesThisPass;
214        ++fileIndex;
215        fileOffset = 0;
216    }
217
218    return err;
219}
220
221int
222tr_ioRead( const tr_torrent * tor,
223           tr_piece_index_t   pieceIndex,
224           uint32_t           begin,
225           uint32_t           len,
226           uint8_t *          buf )
227{
228    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
229}
230
231int
232tr_ioWrite( const tr_torrent * tor,
233            tr_piece_index_t   pieceIndex,
234            uint32_t           begin,
235            uint32_t           len,
236            const uint8_t *    buf )
237{
238    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
239                             (uint8_t*)buf,
240                             len );
241}
242
243/****
244*****
245****/
246
247static int
248recalculateHash( const tr_torrent * tor,
249                 tr_piece_index_t   pieceIndex,
250                 uint8_t *          setme )
251{
252    size_t   bytesLeft;
253    uint32_t offset = 0;
254    int      success = TRUE;
255    SHA_CTX  sha;
256
257    assert( tor );
258    assert( setme );
259    assert( pieceIndex < tor->info.pieceCount );
260
261    SHA1_Init( &sha );
262    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
263
264    while( bytesLeft )
265    {
266        uint8_t   buf[8192];
267        const int len = MIN( bytesLeft, sizeof( buf ) );
268        success = !tr_ioRead( tor, pieceIndex, offset, len, buf );
269        if( !success )
270            break;
271        SHA1_Update( &sha, buf, len );
272        offset += len;
273        bytesLeft -= len;
274    }
275
276    if( success )
277        SHA1_Final( setme, &sha );
278
279    return success;
280}
281
282int
283tr_ioTestPiece( const tr_torrent * tor,
284                int                pieceIndex )
285{
286    uint8_t hash[SHA_DIGEST_LENGTH];
287    const int recalculated = recalculateHash( tor, pieceIndex, hash );
288    return recalculated && !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
289}
290
Note: See TracBrowser for help on using the repository browser.