source: trunk/libtransmission/inout.c @ 5081

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

remove unnecessary #includes

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