source: trunk/libtransmission/inout.c @ 9544

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

(trunk libT) a very minor commit. remove trailing spaces from lines and other stuff like that

  • Property svn:keywords set to Date Rev Author Id
File size: 9.5 KB
Line 
1/*
2 * This file Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.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 9544 2009-11-22 03:57:36Z charles $
11 */
12
13#ifdef HAVE_LSEEK64
14 #define _LARGEFILE64_SOURCE
15#endif
16
17#include <assert.h>
18#include <errno.h>
19#include <stdlib.h> /* realloc */
20#include <string.h> /* memcmp */
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25
26#include <openssl/sha.h>
27
28#include "transmission.h"
29#include "crypto.h"
30#include "fdlimit.h"
31#include "inout.h"
32#include "platform.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 #if defined(read)
43  #undef read
44 #endif
45 #define read  _read
46
47 #if defined(write)
48  #undef write
49 #endif
50 #define write _write
51#endif
52
53enum { TR_IO_READ, TR_IO_PREFETCH,
54       /* Any operations that require write access must follow TR_IO_WRITE. */
55       TR_IO_WRITE
56};
57
58int64_t
59tr_lseek( int fd, int64_t offset, int whence )
60{
61#if defined( HAVE_LSEEK64 )
62    return lseek64( fd, (off64_t)offset, whence );
63#elif defined( WIN32 )
64    return _lseeki64( fd, offset, whence );
65#else
66    return lseek( fd, (off_t)offset, whence );
67#endif
68}
69
70/* returns 0 on success, or an errno on failure */
71static int
72readOrWriteBytes( tr_session       * session,
73                  const tr_torrent * tor,
74                  int                ioMode,
75                  tr_file_index_t    fileIndex,
76                  uint64_t           fileOffset,
77                  void *             buf,
78                  size_t             buflen )
79{
80    const tr_info * info = &tor->info;
81    const tr_file * file = &info->files[fileIndex];
82
83    int             fd = -1;
84    int             err = 0;
85    const tr_bool doWrite = ioMode >= TR_IO_WRITE;
86
87    assert( fileIndex < info->fileCount );
88    assert( !file->length || ( fileOffset < file->length ) );
89    assert( fileOffset + buflen <= file->length );
90
91    if( !file->length )
92        return 0;
93
94    fd = tr_fdFileGetCached( session, tr_torrentId( tor ), fileIndex, doWrite );
95
96    if( fd < 0 )
97    {
98        /* the fd cache doesn't have this file...
99         * we'll need to open it and maybe create it */
100        char * subpath;
101        const char * base;
102        tr_bool fileExists;
103        tr_preallocation_mode preallocationMode;
104
105        fileExists = tr_torrentFindFile2( tor, fileIndex, &base, &subpath );
106
107        if( !fileExists )
108        {
109            base = tr_torrentGetCurrentDir( tor );
110
111            if( tr_sessionIsIncompleteFileNamingEnabled( tor->session ) )
112                subpath = tr_torrentBuildPartial( tor, fileIndex );
113            else
114                subpath = tr_strdup( file->name );
115        }
116
117        if( ( file->dnd ) || ( ioMode < TR_IO_WRITE ) )
118            preallocationMode = TR_PREALLOCATE_NONE;
119        else
120            preallocationMode = tor->session->preallocationMode;
121
122        if( ( ioMode < TR_IO_WRITE ) && !fileExists ) /* does file exist? */
123        {
124            err = ENOENT;
125        }
126        else
127        {
128            char * filename = tr_buildPath( base, subpath, NULL );
129
130            if( ( fd = tr_fdFileCheckout( session, tor->uniqueId, fileIndex, filename,
131                                          doWrite, preallocationMode, file->length ) ) < 0 )
132            {
133                err = errno;
134                tr_torerr( tor, "tr_fdFileCheckout failed for \"%s\": %s", filename, tr_strerror( err ) );
135            }
136
137            tr_free( filename );
138        }
139
140        if( doWrite && !err )
141            tr_statsFileCreated( tor->session );
142
143        tr_free( subpath );
144    }
145
146    if( !err )
147    {
148        if( ioMode == TR_IO_READ ) {
149            int rc = tr_pread(fd, buf, buflen, fileOffset);
150            if(rc < 0) {
151                err = errno;
152                tr_torerr( tor, "read failed for \"%s\": %s",
153                           file->name, tr_strerror( err ) );
154            }
155        } else if( ioMode == TR_IO_PREFETCH ) {
156            int rc = tr_prefetch(fd, fileOffset, buflen);
157            if(rc < 0) {
158                err = errno;
159                tr_torerr( tor, "prefetch failed for \"%s\": %s",
160                           file->name, tr_strerror( err ) );
161            }
162        } else if( ioMode == TR_IO_WRITE ) {
163            int rc = tr_pwrite(fd, buf, buflen, fileOffset);
164            if(rc < 0) {
165                err = errno;
166                tr_torerr( tor, "write failed for \"%s\": %s",
167                           file->name, tr_strerror( err ) );
168            }
169        } else
170            abort();
171    }
172
173    return err;
174}
175
176static int
177compareOffsetToFile( const void * a,
178                     const void * b )
179{
180    const uint64_t  offset = *(const uint64_t*)a;
181    const tr_file * file = b;
182
183    if( offset < file->offset ) return -1;
184    if( offset >= file->offset + file->length ) return 1;
185    return 0;
186}
187
188void
189tr_ioFindFileLocation( const tr_torrent * tor,
190                       tr_piece_index_t   pieceIndex,
191                       uint32_t           pieceOffset,
192                       tr_file_index_t  * fileIndex,
193                       uint64_t         * fileOffset )
194{
195    const uint64_t  offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
196    const tr_file * file;
197
198    file = bsearch( &offset,
199                    tor->info.files, tor->info.fileCount, sizeof( tr_file ),
200                    compareOffsetToFile );
201
202    *fileIndex = file - tor->info.files;
203    *fileOffset = offset - file->offset;
204
205    assert( *fileIndex < tor->info.fileCount );
206    assert( *fileOffset < file->length );
207    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
208}
209
210/* returns 0 on success, or an errno on failure */
211static int
212readOrWritePiece( tr_torrent       * tor,
213                  int                ioMode,
214                  tr_piece_index_t   pieceIndex,
215                  uint32_t           pieceOffset,
216                  uint8_t          * buf,
217                  size_t             buflen )
218{
219    int             err = 0;
220    tr_file_index_t fileIndex;
221    uint64_t        fileOffset;
222    const tr_info * info = &tor->info;
223
224    if( pieceIndex >= tor->info.pieceCount )
225        return EINVAL;
226    if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
227        return EINVAL;
228
229    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
230                           &fileIndex, &fileOffset );
231
232    while( buflen && !err )
233    {
234        const tr_file * file = &info->files[fileIndex];
235        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
236
237        err = readOrWriteBytes( tor->session, tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass );
238        buf += bytesThisPass;
239        buflen -= bytesThisPass;
240        ++fileIndex;
241        fileOffset = 0;
242
243        if( err ) {
244            char * path = tr_buildPath( tor->downloadDir, file->name, NULL );
245            tr_torrentSetLocalError( tor, "%s (%s)", tr_strerror( err ), path );
246            tr_free( path );
247        }
248    }
249
250    return err;
251}
252
253int
254tr_ioRead( tr_torrent       * tor,
255           tr_piece_index_t   pieceIndex,
256           uint32_t           begin,
257           uint32_t           len,
258           uint8_t          * buf )
259{
260    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
261}
262
263int
264tr_ioPrefetch( tr_torrent       * tor,
265               tr_piece_index_t   pieceIndex,
266               uint32_t           begin,
267               uint32_t           len)
268{
269    return readOrWritePiece( tor, TR_IO_PREFETCH, pieceIndex, begin,
270                             NULL, len );
271}
272
273int
274tr_ioWrite( tr_torrent       * tor,
275            tr_piece_index_t   pieceIndex,
276            uint32_t           begin,
277            uint32_t           len,
278            const uint8_t    * buf )
279{
280    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
281                             (uint8_t*)buf,
282                             len );
283}
284
285/****
286*****
287****/
288
289static tr_bool
290recalculateHash( tr_torrent       * tor,
291                 tr_piece_index_t   pieceIndex,
292                 void             * buffer,
293                 size_t             buflen,
294                 uint8_t          * setme )
295{
296    size_t   bytesLeft;
297    uint32_t offset = 0;
298    tr_bool  success = TRUE;
299    uint8_t  stackbuf[MAX_STACK_ARRAY_SIZE];
300    SHA_CTX  sha;
301
302    /* fallback buffer */
303    if( ( buffer == NULL ) || ( buflen < 1 ) )
304    {
305        buffer = stackbuf;
306        buflen = sizeof( stackbuf );
307    }
308
309    assert( tor != NULL );
310    assert( pieceIndex < tor->info.pieceCount );
311    assert( buffer != NULL );
312    assert( buflen > 0 );
313    assert( setme != NULL );
314
315    SHA1_Init( &sha );
316    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
317
318    while( bytesLeft )
319    {
320        const int len = MIN( bytesLeft, buflen );
321        success = !tr_ioRead( tor, pieceIndex, offset, len, buffer );
322        if( !success )
323            break;
324        SHA1_Update( &sha, buffer, len );
325        offset += len;
326        bytesLeft -= len;
327    }
328
329    if( success )
330        SHA1_Final( setme, &sha );
331
332    return success;
333}
334
335tr_bool
336tr_ioTestPiece( tr_torrent        * tor,
337                tr_piece_index_t    pieceIndex,
338                void              * buffer,
339                size_t              buflen )
340{
341    uint8_t hash[SHA_DIGEST_LENGTH];
342
343    return recalculateHash( tor, pieceIndex, buffer, buflen, hash )
344           && !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
345}
Note: See TracBrowser for help on using the repository browser.