1/* 2 * This file Copyright (C) Mnemosyne LLC 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 12582 2011-07-25 17:48:14Z jordan $ 11 */ 12 13#include <assert.h> 14#include <errno.h> 15#include <stdlib.h> /* bsearch() */ 16#include <string.h> /* memcmp() */ 17 18#include <openssl/sha.h> 19 20#include "transmission.h" 21#include "cache.h" /* tr_cacheReadBlock() */ 22#include "fdlimit.h" 23#include "inout.h" 24#include "peer-common.h" /* MAX_BLOCK_SIZE */ 25#include "stats.h" /* tr_statsFileCreated() */ 26#include "torrent.h" 27#include "utils.h" 28 29/**** 30***** Low-level IO functions 31****/ 32 33enum { TR_IO_READ, TR_IO_PREFETCH, 34 /* Any operations that require write access must follow TR_IO_WRITE. */ 35 TR_IO_WRITE 36}; 37 38/* returns 0 on success, or an errno on failure */ 39static int 40readOrWriteBytes( tr_session * session, 41 tr_torrent * tor, 42 int ioMode, 43 tr_file_index_t fileIndex, 44 uint64_t fileOffset, 45 void * buf, 46 size_t buflen ) 47{ 48 int fd; 49 int err = 0; 50 const bool doWrite = ioMode >= TR_IO_WRITE; 51 const tr_info * const info = &tor->info; 52 const tr_file * const file = &info->files[fileIndex]; 53 54 assert( fileIndex < info->fileCount ); 55 assert( !file->length || ( fileOffset < file->length ) ); 56 assert( fileOffset + buflen <= file->length ); 57 58 if( !file->length ) 59 return 0; 60 61 /*** 62 **** Find the fd 63 ***/ 64 65 fd = tr_fdFileGetCached( session, tr_torrentId( tor ), fileIndex, doWrite ); 66 if( fd < 0 ) 67 { 68 /* it's not cached, so open/create it now */ 69 char * subpath; 70 const char * base; 71 72 /* see if the file exists... */ 73 if( !tr_torrentFindFile2( tor, fileIndex, &base, &subpath, NULL ) ) 74 { 75 /* we can't read a file that doesn't exist... */ 76 if( !doWrite ) 77 err = ENOENT; 78 79 /* figure out where the file should go, so we can create it */ 80 base = tr_torrentGetCurrentDir( tor ); 81 subpath = tr_sessionIsIncompleteFileNamingEnabled( tor->session ) 82 ? tr_torrentBuildPartial( tor, fileIndex ) 83 : tr_strdup( file->name ); 84 85 } 86 87 if( !err ) 88 { 89 /* open (and maybe create) the file */ 90 char * filename = tr_buildPath( base, subpath, NULL ); 91 const int prealloc = file->dnd || !doWrite 92 ? TR_PREALLOCATE_NONE 93 : tor->session->preallocationMode; 94 if((( fd = tr_fdFileCheckout( session, tor->uniqueId, fileIndex, 95 filename, doWrite, 96 prealloc, file->length ))) < 0 ) 97 { 98 err = errno; 99 tr_torerr( tor, "tr_fdFileCheckout failed for \"%s\": %s", 100 filename, tr_strerror( err ) ); 101 } 102 else if( doWrite ) 103 { 104 /* make a note that we just created a file */ 105 tr_statsFileCreated( tor->session ); 106 } 107 108 tr_free( filename ); 109 } 110 111 tr_free( subpath ); 112 } 113 114 /*** 115 **** Use the fd 116 ***/ 117 118 if( !err ) 119 { 120 if( ioMode == TR_IO_READ ) { 121 const int rc = tr_pread( fd, buf, buflen, fileOffset ); 122 if( rc < 0 ) { 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 ) { 128 const int rc = tr_pwrite( fd, buf, buflen, fileOffset ); 129 if( rc < 0 ) { 130 err = errno; 131 tr_torerr( tor, "write failed for \"%s\": %s", 132 file->name, tr_strerror( err ) ); 133 } 134 } else if( ioMode == TR_IO_PREFETCH ) { 135 tr_prefetch( fd, fileOffset, buflen ); 136 } else { 137 abort(); 138 } 139 } 140 141 return err; 142} 143 144static int 145compareOffsetToFile( const void * a, const void * b ) 146{ 147 const uint64_t offset = *(const uint64_t*)a; 148 const tr_file * file = b; 149 150 if( offset < file->offset ) return -1; 151 if( offset >= file->offset + file->length ) return 1; 152 return 0; 153} 154 155void 156tr_ioFindFileLocation( const tr_torrent * tor, 157 tr_piece_index_t pieceIndex, 158 uint32_t pieceOffset, 159 tr_file_index_t * fileIndex, 160 uint64_t * fileOffset ) 161{ 162 const uint64_t offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 ); 163 const tr_file * file; 164 165 assert( tr_isTorrent( tor ) ); 166 assert( offset < tor->info.totalSize ); 167 168 file = bsearch( &offset, 169 tor->info.files, tor->info.fileCount, sizeof( tr_file ), 170 compareOffsetToFile ); 171 172 assert( file != NULL ); 173 174 *fileIndex = file - tor->info.files; 175 *fileOffset = offset - file->offset; 176 177 assert( *fileIndex < tor->info.fileCount ); 178 assert( *fileOffset < file->length ); 179 assert( tor->info.files[*fileIndex].offset + *fileOffset == offset ); 180} 181 182/* returns 0 on success, or an errno on failure */ 183static int 184readOrWritePiece( tr_torrent * tor, 185 int ioMode, 186 tr_piece_index_t pieceIndex, 187 uint32_t pieceOffset, 188 uint8_t * buf, 189 size_t buflen ) 190{ 191 int err = 0; 192 tr_file_index_t fileIndex; 193 uint64_t fileOffset; 194 const tr_info * info = &tor->info; 195 196 if( pieceIndex >= tor->info.pieceCount ) 197 return EINVAL; 198 199 tr_ioFindFileLocation( tor, pieceIndex, pieceOffset, 200 &fileIndex, &fileOffset ); 201 202 while( buflen && !err ) 203 { 204 const tr_file * file = &info->files[fileIndex]; 205 const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset ); 206 207 err = readOrWriteBytes( tor->session, tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass ); 208 buf += bytesThisPass; 209 buflen -= bytesThisPass; 210 ++fileIndex; 211 fileOffset = 0; 212 213 if( ( err != 0 ) && (ioMode == TR_IO_WRITE ) && ( tor->error != TR_STAT_LOCAL_ERROR ) ) 214 { 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 } 219 } 220 221 return err; 222} 223 224int 225tr_ioRead( tr_torrent * tor, 226 tr_piece_index_t pieceIndex, 227 uint32_t begin, 228 uint32_t len, 229 uint8_t * buf ) 230{ 231 return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len ); 232} 233 234int 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 245tr_ioWrite( tr_torrent * tor, 246 tr_piece_index_t pieceIndex, 247 uint32_t begin, 248 uint32_t len, 249 const uint8_t * buf ) 250{ 251 return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin, 252 (uint8_t*)buf, 253 len ); 254} 255 256/**** 257***** 258****/ 259 260static bool 261recalculateHash( tr_torrent * tor, tr_piece_index_t pieceIndex, uint8_t * setme ) 262{ 263 size_t bytesLeft; 264 uint32_t offset = 0; 265 bool success = true; 266 const size_t buflen = tor->blockSize; 267 void * buffer = tr_valloc( buflen ); 268 SHA_CTX sha; 269 270 assert( tor != NULL ); 271 assert( pieceIndex < tor->info.pieceCount ); 272 assert( buffer != NULL ); 273 assert( buflen > 0 ); 274 assert( setme != NULL ); 275 276 SHA1_Init( &sha ); 277 bytesLeft = tr_torPieceCountBytes( tor, pieceIndex ); 278 279 tr_ioPrefetch( tor, pieceIndex, offset, bytesLeft ); 280 281 while( bytesLeft ) 282 { 283 const int len = MIN( bytesLeft, buflen ); 284 success = !tr_cacheReadBlock( tor->session->cache, tor, pieceIndex, offset, len, buffer ); 285 if( !success ) 286 break; 287 SHA1_Update( &sha, buffer, len ); 288 offset += len; 289 bytesLeft -= len; 290 } 291 292 if( success ) 293 SHA1_Final( setme, &sha ); 294 295 tr_free( buffer ); 296 return success; 297} 298 299bool 300tr_ioTestPiece( tr_torrent * tor, tr_piece_index_t piece ) 301{ 302 uint8_t hash[SHA_DIGEST_LENGTH]; 303 304 return recalculateHash( tor, piece, hash ) 305 && !memcmp( hash, tor->info.pieces[piece].hash, SHA_DIGEST_LENGTH ); 306} 307