source: trunk/libtransmission/inout.c @ 6425

Last change on this file since 6425 was 6425, checked in by charles, 13 years ago

minor text cleanup

  • Property svn:keywords set to Date Rev Author Id
File size: 7.6 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 6425 2008-08-01 16:43:22Z 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                  tr_file_index_t     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( fileIndex < info->fileCount );
60    assert( !file->length || (fileOffset < file->length));
61    assert( fileOffset + buflen <= file->length );
62
63    tr_buildPath ( path, sizeof(path), tor->downloadDir, 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->downloadDir,
72                                       file->name,
73                                       ioMode==TR_IO_WRITE )) < 0)
74        err = fd;
75    else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
76        err = tr_ioErrorFromErrno( errno );
77    else if( func( fd, buf, buflen ) != buflen )
78        err = tr_ioErrorFromErrno( errno );
79    else
80        err = TR_OK;
81
82    if( ( err==TR_OK ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE) )
83        tr_statsFileCreated( tor->handle );
84 
85    if( fd >= 0 )
86        tr_fdFileReturn( fd );
87
88    return err;
89}
90
91static int
92compareOffsetToFile( const void * a, const void * b )
93{
94    const uint64_t offset = *(const uint64_t*)a;
95    const tr_file * file = b;
96
97    if( offset < file->offset ) return -1;
98    if( offset >= file->offset + file->length ) return 1;
99    return 0;
100}
101
102void
103tr_ioFindFileLocation( const tr_torrent * tor,
104                       tr_piece_index_t   pieceIndex,
105                       uint32_t           pieceOffset,
106                       tr_file_index_t  * fileIndex,
107                       uint64_t         * fileOffset )
108{
109    const uint64_t offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
110    const tr_file * file;
111
112    file = bsearch( &offset,
113                    tor->info.files, tor->info.fileCount, sizeof(tr_file),
114                    compareOffsetToFile );
115   
116    *fileIndex = file - tor->info.files;
117    *fileOffset = offset - file->offset;
118
119    assert( *fileIndex < tor->info.fileCount );
120    assert( *fileOffset < file->length );
121    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
122}
123
124#ifdef WIN32
125static tr_errno
126ensureMinimumFileSize( const tr_torrent  * tor,
127                       tr_file_index_t     fileIndex,
128                       uint64_t            minBytes )
129{
130    int fd;
131    tr_errno err;
132    struct stat sb;
133    const tr_file * file = &tor->info.files[fileIndex];
134
135    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
136    assert( minBytes <= file->length );
137
138    fd = tr_fdFileCheckout( tor->downloadDir, file->name, TRUE );
139    if( fd < 0 ) /* bad fd */
140        err = fd;
141    else if (fstat (fd, &sb) ) /* how big is the file? */
142        err = tr_ioErrorFromErrno( errno );
143    else if (sb.st_size >= (off_t)minBytes) /* already big enough */
144        err = TR_OK;
145    else if ( !ftruncate( fd, minBytes ) ) /* grow it */
146        err = TR_OK;
147    else /* couldn't grow it */
148        err = tr_ioErrorFromErrno( errno );
149
150    if( fd >= 0 )
151        tr_fdFileReturn( fd );
152
153    return err;
154}
155#endif
156
157static tr_errno
158readOrWritePiece( const tr_torrent        * tor,
159                  int                       ioMode,
160                  tr_piece_index_t          pieceIndex,
161                  uint32_t                  pieceOffset,
162                  uint8_t                 * buf,
163                  size_t                    buflen )
164{
165    tr_errno err = 0;
166    tr_file_index_t fileIndex;
167    uint64_t fileOffset;
168    const tr_info * info = &tor->info;
169
170    if( pieceIndex >= tor->info.pieceCount )
171        return TR_ERROR_ASSERT;
172    if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
173        return TR_ERROR_ASSERT;
174
175    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
176                           &fileIndex, &fileOffset );
177
178    while( buflen && !err )
179    {
180        const tr_file * file = &info->files[fileIndex];
181        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
182
183#ifdef WIN32
184        if( ioMode == TR_IO_WRITE )
185            err = ensureMinimumFileSize( tor, fileIndex,
186                                         fileOffset + bytesThisPass );
187        if( !err )
188#endif
189            err = readOrWriteBytes( tor, ioMode,
190                                    fileIndex, fileOffset, buf, bytesThisPass );
191        buf += bytesThisPass;
192        buflen -= bytesThisPass;
193        ++fileIndex;
194        fileOffset = 0;
195    }
196
197    return err;
198}
199
200tr_errno
201tr_ioRead( const tr_torrent  * tor,
202           tr_piece_index_t    pieceIndex,
203           uint32_t            begin,
204           uint32_t            len,
205           uint8_t           * buf )
206{
207    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
208}
209
210tr_errno
211tr_ioWrite( const tr_torrent  * tor,
212            tr_piece_index_t    pieceIndex,
213            uint32_t            begin,
214            uint32_t            len,
215            const uint8_t     * buf )
216{
217    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin, (uint8_t*)buf, len );
218}
219
220/****
221*****
222****/
223
224static tr_errno
225recalculateHash( const tr_torrent  * tor,
226                 tr_piece_index_t    pieceIndex,
227                 uint8_t           * setme )
228{
229    static uint8_t * buf = NULL;
230    static int buflen = 0;
231    static tr_lock * lock = NULL;
232
233    int n;
234    tr_errno err;
235
236    /* only check one block at a time to prevent disk thrashing.
237     * this also lets us reuse the same buffer each time. */
238    if( lock == NULL )
239        lock = tr_lockNew( );
240
241    tr_lockLock( lock );
242
243    assert( tor );
244    assert( setme );
245    assert( pieceIndex < tor->info.pieceCount );
246
247    n = tr_torPieceCountBytes( tor, pieceIndex );
248
249    if( buflen < n ) {
250        buflen = n;
251        buf = tr_renew( uint8_t, buf, buflen );
252    }
253       
254    err = tr_ioRead( tor, pieceIndex, 0, n, buf );
255    if( !err )
256        tr_sha1( setme, buf, n, NULL );
257
258    tr_lockUnlock( lock );
259    return err;
260}
261
262tr_errno
263tr_ioTestPiece( const tr_torrent * tor, int pieceIndex )
264{
265    int err;
266    uint8_t hash[SHA_DIGEST_LENGTH];
267
268    err  = recalculateHash( tor, pieceIndex, hash );
269
270    if( !err && memcmp( hash, tor->info.pieces[pieceIndex].hash,
271                        SHA_DIGEST_LENGTH ) )
272        err = TR_ERROR_IO_CHECKSUM;
273
274    tr_tordbg (tor, "piece %d hash check: %s",
275            pieceIndex, ( err ? "FAILED" : "OK" ));
276
277    return err;
278}
Note: See TracBrowser for help on using the repository browser.