source: trunk/libtransmission/inout.c @ 12918

Last change on this file since 12918 was 12582, checked in by jordan, 10 years ago

(trunk libt) #4305 "New Torrent via RPC Error:No data found when subfolder does not exist" -- revert r12076 s.t. parent directories are created as necessary when saving local data to disk.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.8 KB
RevLine 
[2149]1/*
[11709]2 * This file Copyright (C) Mnemosyne LLC
[1]3 *
[11599]4 * This file is licensed by the GPL version 2. Works owned by the
[2149]5 * Transmission project are granted a special exemption to clause 2(b)
[6795]6 * so that the bulk of its code can remain under the MIT license.
[2149]7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
[2846]9 *
10 * $Id: inout.c 12582 2011-07-25 17:48:14Z jordan $
[2149]11 */
[1]12
[3775]13#include <assert.h>
[5065]14#include <errno.h>
[12223]15#include <stdlib.h> /* bsearch() */
16#include <string.h> /* memcmp() */
[2544]17
[6730]18#include <openssl/sha.h>
19
[1]20#include "transmission.h"
[12228]21#include "cache.h" /* tr_cacheReadBlock() */
[2311]22#include "fdlimit.h"
[2555]23#include "inout.h"
[10982]24#include "peer-common.h" /* MAX_BLOCK_SIZE */
[12228]25#include "stats.h" /* tr_statsFileCreated() */
[4324]26#include "torrent.h"
[2555]27#include "utils.h"
[1]28
[2149]29/****
30*****  Low-level IO functions
31****/
[1]32
[9495]33enum { TR_IO_READ, TR_IO_PREFETCH,
34       /* Any operations that require write access must follow TR_IO_WRITE. */
35       TR_IO_WRITE
36};
[3105]37
[6842]38/* returns 0 on success, or an errno on failure */
39static int
[9387]40readOrWriteBytes( tr_session       * session,
[11600]41                  tr_torrent       * tor,
[6795]42                  int                ioMode,
43                  tr_file_index_t    fileIndex,
44                  uint64_t           fileOffset,
[11600]45                  void             * buf,
[6795]46                  size_t             buflen )
[346]47{
[12175]48    int fd;
49    int err = 0;
[12204]50    const bool doWrite = ioMode >= TR_IO_WRITE;
[12175]51    const tr_info * const info = &tor->info;
52    const tr_file * const file = &info->files[fileIndex];
[6795]53
[5329]54    assert( fileIndex < info->fileCount );
[6795]55    assert( !file->length || ( fileOffset < file->length ) );
[3674]56    assert( fileOffset + buflen <= file->length );
[346]57
[2149]58    if( !file->length )
[6842]59        return 0;
[4734]60
[12175]61    /***
62    ****  Find the fd
63    ***/
64
[9387]65    fd = tr_fdFileGetCached( session, tr_torrentId( tor ), fileIndex, doWrite );
[9328]66    if( fd < 0 )
67    {
[12175]68        /* it's not cached, so open/create it now */
[9335]69        char * subpath;
70        const char * base;
[9544]71
[12175]72        /* see if the file exists... */
[12296]73        if( !tr_torrentFindFile2( tor, fileIndex, &base, &subpath, NULL ) )
[12175]74        {
75            /* we can't read a file that doesn't exist... */
76            if( !doWrite )
77                err = ENOENT;
[9335]78
[12175]79            /* figure out where the file should go, so we can create it */
[9341]80            base = tr_torrentGetCurrentDir( tor );
[12175]81            subpath = tr_sessionIsIncompleteFileNamingEnabled( tor->session )
82                    ? tr_torrentBuildPartial( tor, fileIndex )
83                    : tr_strdup( file->name );
[9328]84
85        }
86
[12175]87        if( !err )
[9328]88        {
[12175]89            /* open (and maybe create) the file */
[9346]90            char * filename = tr_buildPath( base, subpath, NULL );
[12175]91            const int prealloc = file->dnd || !doWrite
92                               ? TR_PREALLOCATE_NONE
93                               : tor->session->preallocationMode;
94            if((( fd = tr_fdFileCheckout( session, tor->uniqueId, fileIndex,
[12582]95                                          filename, doWrite,
[12175]96                                          prealloc, file->length ))) < 0 )
[9346]97            {
98                err = errno;
[12175]99                tr_torerr( tor, "tr_fdFileCheckout failed for \"%s\": %s",
100                           filename, tr_strerror( err ) );
[9346]101            }
[12175]102            else if( doWrite )
103            {
104                /* make a note that we just created a file */
105                tr_statsFileCreated( tor->session );
106            }
[9346]107
[9335]108            tr_free( filename );
[9328]109        }
110
[9335]111        tr_free( subpath );
[9066]112    }
[9328]113
[12175]114    /***
115    ****  Use the fd
116    ***/
117
[11811]118    if( !err )
119    {
[9495]120        if( ioMode == TR_IO_READ ) {
[9573]121            const int rc = tr_pread( fd, buf, buflen, fileOffset );
122            if( rc < 0 ) {
[9495]123                err = errno;
124                tr_torerr( tor, "read failed for \"%s\": %s",
125                           file->name, tr_strerror( err ) );
126            }
127        } else if( ioMode == TR_IO_WRITE ) {
[9573]128            const int rc = tr_pwrite( fd, buf, buflen, fileOffset );
129            if( rc < 0 ) {
[9495]130                err = errno;
131                tr_torerr( tor, "write failed for \"%s\": %s",
132                           file->name, tr_strerror( err ) );
133            }
[12462]134        } else if( ioMode == TR_IO_PREFETCH ) {
135            tr_prefetch( fd, fileOffset, buflen );
[9573]136        } else {
[9495]137            abort();
[9573]138        }
[9066]139    }
[3988]140
[4734]141    return err;
[346]142}
143
[5654]144static int
[11310]145compareOffsetToFile( const void * a, const void * b )
[5654]146{
[6795]147    const uint64_t  offset = *(const uint64_t*)a;
[5654]148    const tr_file * file = b;
149
150    if( offset < file->offset ) return -1;
[5655]151    if( offset >= file->offset + file->length ) return 1;
[5654]152    return 0;
153}
154
[6073]155void
156tr_ioFindFileLocation( const tr_torrent * tor,
157                       tr_piece_index_t   pieceIndex,
158                       uint32_t           pieceOffset,
[7131]159                       tr_file_index_t  * fileIndex,
160                       uint64_t         * fileOffset )
[1824]161{
[7131]162    const uint64_t  offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
[5654]163    const tr_file * file;
[1824]164
[11310]165    assert( tr_isTorrent( tor ) );
[11312]166    assert( offset < tor->info.totalSize );
167
168    file = bsearch( &offset,
169                    tor->info.files, tor->info.fileCount, sizeof( tr_file ),
170                    compareOffsetToFile );
171
[11310]172    assert( file != NULL );
173
[5654]174    *fileIndex = file - tor->info.files;
175    *fileOffset = offset - file->offset;
[1]176
[5654]177    assert( *fileIndex < tor->info.fileCount );
178    assert( *fileOffset < file->length );
179    assert( tor->info.files[*fileIndex].offset + *fileOffset == offset );
[1]180}
181
[6842]182/* returns 0 on success, or an errno on failure */
183static int
[8912]184readOrWritePiece( tr_torrent       * tor,
[6795]185                  int                ioMode,
186                  tr_piece_index_t   pieceIndex,
187                  uint32_t           pieceOffset,
[8912]188                  uint8_t          * buf,
[6795]189                  size_t             buflen )
[2149]190{
[6842]191    int             err = 0;
[5329]192    tr_file_index_t fileIndex;
[6795]193    uint64_t        fileOffset;
[3105]194    const tr_info * info = &tor->info;
[2076]195
[5645]196    if( pieceIndex >= tor->info.pieceCount )
[6842]197        return EINVAL;
[1961]198
[6073]199    tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
200                           &fileIndex, &fileOffset );
[1961]201
[4734]202    while( buflen && !err )
[1356]203    {
[3105]204        const tr_file * file = &info->files[fileIndex];
[9066]205        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
[1]206
[9387]207        err = readOrWriteBytes( tor->session, tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass );
[2149]208        buf += bytesThisPass;
209        buflen -= bytesThisPass;
[5645]210        ++fileIndex;
[2149]211        fileOffset = 0;
[8912]212
[11600]213        if( ( err != 0 ) && (ioMode == TR_IO_WRITE ) && ( tor->error != TR_STAT_LOCAL_ERROR ) )
[10023]214        {
[8912]215            char * path = tr_buildPath( tor->downloadDir, file->name, NULL );
216            tr_torrentSetLocalError( tor, "%s (%s)", tr_strerror( err ), path );
217            tr_free( path );
218        }
[1]219    }
220
[4734]221    return err;
[1]222}
223
[6842]224int
[8912]225tr_ioRead( tr_torrent       * tor,
[6795]226           tr_piece_index_t   pieceIndex,
227           uint32_t           begin,
228           uint32_t           len,
[8912]229           uint8_t          * buf )
[1]230{
[5645]231    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
[1]232}
233
[6842]234int
[9495]235tr_ioPrefetch( tr_torrent       * tor,
236               tr_piece_index_t   pieceIndex,
237               uint32_t           begin,
238               uint32_t           len)
239{
240    return readOrWritePiece( tor, TR_IO_PREFETCH, pieceIndex, begin,
241                             NULL, len );
242}
243
244int
[8912]245tr_ioWrite( tr_torrent       * tor,
[6795]246            tr_piece_index_t   pieceIndex,
247            uint32_t           begin,
248            uint32_t           len,
[8912]249            const uint8_t    * buf )
[1]250{
[6795]251    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin,
252                             (uint8_t*)buf,
253                             len );
[1]254}
255
[2149]256/****
257*****
258****/
259
[12204]260static bool
261recalculateHash( tr_torrent * tor, tr_piece_index_t pieceIndex, uint8_t * setme )
[1]262{
[7351]263    size_t   bytesLeft;
[6730]264    uint32_t offset = 0;
[12204]265    bool  success = true;
[11273]266    const size_t buflen = tor->blockSize;
[10961]267    void * buffer = tr_valloc( buflen );
[6795]268    SHA_CTX  sha;
[5241]269
[7559]270    assert( tor != NULL );
[5329]271    assert( pieceIndex < tor->info.pieceCount );
[7559]272    assert( buffer != NULL );
273    assert( buflen > 0 );
274    assert( setme != NULL );
[1]275
[6730]276    SHA1_Init( &sha );
[7046]277    bytesLeft = tr_torPieceCountBytes( tor, pieceIndex );
[1]278
[10552]279    tr_ioPrefetch( tor, pieceIndex, offset, bytesLeft );
280
[6730]281    while( bytesLeft )
282    {
[7559]283        const int len = MIN( bytesLeft, buflen );
[10798]284        success = !tr_cacheReadBlock( tor->session->cache, tor, pieceIndex, offset, len, buffer );
[6841]285        if( !success )
[6730]286            break;
[7559]287        SHA1_Update( &sha, buffer, len );
[6730]288        offset += len;
289        bytesLeft -= len;
[4842]290    }
[6730]291
[6841]292    if( success )
[6730]293        SHA1_Final( setme, &sha );
[1]294
[10798]295    tr_free( buffer );
[6841]296    return success;
[1]297}
298
[12204]299bool
[10273]300tr_ioTestPiece( tr_torrent * tor, tr_piece_index_t piece )
[1]301{
[2149]302    uint8_t hash[SHA_DIGEST_LENGTH];
[7559]303
[10273]304    return recalculateHash( tor, piece, hash )
305           && !memcmp( hash, tor->info.pieces[piece].hash, SHA_DIGEST_LENGTH );
[3105]306}
Note: See TracBrowser for help on using the repository browser.