source: trunk/libtransmission/inout.c @ 5286

Last change on this file since 5286 was 5286, checked in by charles, 14 years ago

add a tr_errno specifically for failed checksums so they can be differentiated from "Generic error"

  • Property svn:keywords set to Date Rev Author Id
File size: 7.4 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 5286 2008-03-18 17:02:08Z 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 "transmission.h"
23#include "crypto.h"
24#include "fdlimit.h"
25#include "inout.h"
26#include "platform.h"
27#include "stats.h"
28#include "torrent.h"
29#include "utils.h"
30
31/****
32*****  Low-level IO functions
33****/
34
35#ifdef WIN32
36#define lseek _lseeki64
37#endif
38
39enum { TR_IO_READ, TR_IO_WRITE };
40
41static tr_errno
42readOrWriteBytes( const tr_torrent  * tor,
43                  int                 ioMode,
44                  int                 fileIndex,
45                  uint64_t            fileOffset,
46                  void              * buf,
47                  size_t              buflen )
48{
49    const tr_info * info = &tor->info;
50    const tr_file * file = &info->files[fileIndex];
51    typedef size_t (* iofunc) ( int, void *, size_t );
52    iofunc func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write;
53    char path[MAX_PATH_LENGTH];
54    struct stat sb;
55    int fd = -1;
56    int err;
57    int fileExists;
58
59    assert( 0<=fileIndex && fileIndex<info->fileCount );
60    assert( !file->length || (fileOffset < file->length));
61    assert( fileOffset + buflen <= file->length );
62
63    tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL );
64    fileExists = !stat( path, &sb );
65
66    if( !file->length )
67        return TR_OK;
68
69    if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */
70        err = tr_ioErrorFromErrno( errno );
71    else if ((fd = tr_fdFileCheckout ( tor->destination, file->name, ioMode==TR_IO_WRITE )) < 0)
72        err = fd;
73    else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
74        err = tr_ioErrorFromErrno( errno );
75    else if( func( fd, buf, buflen ) != buflen )
76        err = tr_ioErrorFromErrno( errno );
77    else
78        err = TR_OK;
79
80    if( ( err==TR_OK ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE) )
81        tr_statsFileCreated( tor->handle );
82 
83    if( fd >= 0 )
84        tr_fdFileReturn( fd );
85
86    return err;
87}
88
89static tr_errno
90findFileLocation( const tr_torrent * tor,
91                  int                pieceIndex,
92                  int                pieceOffset,
93                  int              * fileIndex,
94                  uint64_t         * fileOffset )
95{
96    const tr_info * info = &tor->info;
97
98    int i;
99    uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
100
101    if( pieceIndex < 0 || pieceIndex >= info->pieceCount )
102        return TR_ERROR_ASSERT;
103    if( pieceOffset >= tr_torPieceCountBytes( tor, pieceIndex ) )
104        return TR_ERROR_ASSERT;
105    if( piecePos >= info->totalSize )
106        return TR_ERROR_ASSERT;
107
108    for( i=0; info->files[i].length<=piecePos; ++i )
109        piecePos -= info->files[i].length;
110
111    *fileIndex = i;
112    *fileOffset = piecePos;
113
114    assert( 0<=*fileIndex && *fileIndex<info->fileCount );
115    assert( *fileOffset < info->files[i].length );
116    return 0;
117}
118
119#ifdef WIN32
120static tr_errno
121ensureMinimumFileSize( const tr_torrent  * tor,
122                       int                 fileIndex,
123                       uint64_t            minBytes )
124{
125    int fd;
126    tr_errno err;
127    struct stat sb;
128    const tr_file * file = &tor->info.files[fileIndex];
129
130    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
131    assert( minBytes <= file->length );
132
133    fd = tr_fdFileCheckout( tor->destination, file->name, TRUE );
134    if( fd < 0 ) /* bad fd */
135        err = fd;
136    else if (fstat (fd, &sb) ) /* how big is the file? */
137        err = tr_ioErrorFromErrno( errno );
138    else if (sb.st_size >= (off_t)minBytes) /* already big enough */
139        err = TR_OK;
140    else if ( !ftruncate( fd, minBytes ) ) /* grow it */
141        err = TR_OK;
142    else /* couldn't grow it */
143        err = tr_ioErrorFromErrno( errno );
144
145    if( fd >= 0 )
146        tr_fdFileReturn( fd );
147
148    return err;
149}
150#endif
151
152static tr_errno
153readOrWritePiece( tr_torrent  * tor,
154                  int           ioMode,
155                  int           pieceIndex,
156                  int           pieceOffset,
157                  uint8_t     * buf,
158                  size_t        buflen )
159{
160    tr_errno err = 0;
161    int fileIndex;
162    uint64_t fileOffset;
163    const tr_info * info = &tor->info;
164
165    if( pieceIndex < 0 || pieceIndex >= tor->info.pieceCount )
166        err = TR_ERROR_ASSERT;
167    else if( buflen > ( size_t ) tr_torPieceCountBytes( tor, pieceIndex ) )
168        err = TR_ERROR_ASSERT;
169
170    if( !err )
171        err = findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
172
173    while( buflen && !err )
174    {
175        const tr_file * file = &info->files[fileIndex];
176        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
177
178#ifdef WIN32
179        if( ioMode == TR_IO_WRITE )
180            err = ensureMinimumFileSize( tor, fileIndex,
181                                         fileOffset + bytesThisPass );
182        if( !err )
183#endif
184            err = readOrWriteBytes( tor, ioMode,
185                                    fileIndex, fileOffset, buf, bytesThisPass );
186        buf += bytesThisPass;
187        buflen -= bytesThisPass;
188        fileIndex++;
189        fileOffset = 0;
190    }
191
192    return err;
193}
194
195tr_errno
196tr_ioRead( const tr_torrent  * tor,
197           int                 pieceIndex,
198           int                 begin,
199           int                 len,
200           uint8_t           * buf )
201{
202    return readOrWritePiece( (tr_torrent*)tor, TR_IO_READ, pieceIndex, begin, buf, len );
203}
204
205tr_errno
206tr_ioWrite( tr_torrent     * tor,
207            int              pieceIndex,
208            int              begin,
209            int              len,
210            const uint8_t  * buf )
211{
212    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin, (uint8_t*)buf, len );
213}
214
215/****
216*****
217****/
218
219static tr_errno
220tr_ioRecalculateHash( const tr_torrent  * tor,
221                      int                 pieceIndex,
222                      uint8_t           * setme )
223{
224    static uint8_t * buf = NULL;
225    static int buflen = 0;
226    static tr_lock * lock = NULL;
227
228    int n;
229    tr_errno err;
230    const tr_info * info;
231
232    /* only check one block at a time to prevent disk thrashing.
233     * this also lets us reuse the same buffer each time. */
234    if( lock == NULL )
235        lock = tr_lockNew( );
236
237    tr_lockLock( lock );
238
239    assert( tor != NULL );
240    assert( setme != NULL );
241    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
242
243    info = &tor->info;
244    n = tr_torPieceCountBytes( tor, pieceIndex );
245
246    if( buflen < n ) {
247        buflen = n;
248        buf = tr_renew( uint8_t, buf, buflen );
249    }
250       
251    err = tr_ioRead( tor, pieceIndex, 0, n, buf );
252    if( !err )
253        tr_sha1( setme, buf, n, NULL );
254
255    tr_lockUnlock( lock );
256    return 0;
257}
258
259tr_errno
260tr_ioTestPiece( const tr_torrent * tor, int pieceIndex )
261{
262    int err;
263    uint8_t hash[SHA_DIGEST_LENGTH];
264
265    err  = tr_ioRecalculateHash( tor, pieceIndex, hash );
266
267    if( !err && memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH ) )
268        err = TR_ERROR_IO_CHECKSUM;
269
270    tr_tordbg (tor, "piece %d hash check: %s",
271            pieceIndex, ( err ? "FAILED" : "OK" ));
272
273    return err;
274}
Note: See TracBrowser for help on using the repository browser.