source: trunk/libtransmission/inout.c @ 5065

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

cleanup #includes for errno & strerror

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