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: makemeta.c 12581 2011-07-24 20:18:33Z jordan $ 11 */ 12 13#include <assert.h> 14#include <errno.h> 15#include <stdio.h> /* FILE, stderr */ 16#include <stdlib.h> /* qsort */ 17#include <string.h> /* strcmp, strlen */ 18 19#include <sys/types.h> 20#include <sys/stat.h> 21#include <unistd.h> /* read() */ 22#include <dirent.h> 23 24#include <event2/util.h> /* evutil_ascii_strcasecmp() */ 25 26#include "transmission.h" 27#include "crypto.h" /* tr_sha1 */ 28#include "fdlimit.h" /* tr_open_file_for_scanning() */ 29#include "session.h" 30#include "bencode.h" 31#include "makemeta.h" 32#include "platform.h" /* threads, locks */ 33#include "utils.h" /* buildpath */ 34#include "version.h" 35 36/**** 37***** 38****/ 39 40struct FileList 41{ 42 struct FileList * next; 43 uint64_t size; 44 char * filename; 45}; 46 47static struct FileList* 48getFiles( const char * dir, 49 const char * base, 50 struct FileList * list ) 51{ 52 int i; 53 char * buf; 54 struct stat sb; 55 DIR * odir = NULL; 56 57 sb.st_size = 0; 58 59 buf = tr_buildPath( dir, base, NULL ); 60 i = stat( buf, &sb ); 61 if( i ) 62 { 63 tr_err( _( "Torrent Creator is skipping file \"%s\": %s" ), 64 buf, tr_strerror( errno ) ); 65 tr_free( buf ); 66 return list; 67 } 68 69 if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) ) 70 { 71 struct dirent *d; 72 for( d = readdir( odir ); d != NULL; d = readdir( odir ) ) 73 if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles */ 74 list = getFiles( buf, d->d_name, list ); 75 closedir( odir ); 76 } 77 else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) ) 78 { 79 struct FileList * node = tr_new( struct FileList, 1 ); 80 node->size = sb.st_size; 81 node->filename = tr_strdup( buf ); 82 node->next = list; 83 list = node; 84 } 85 86 tr_free( buf ); 87 return list; 88} 89 90static uint32_t 91bestPieceSize( uint64_t totalSize ) 92{ 93 const uint32_t KiB = 1024; 94 const uint32_t MiB = 1048576; 95 const uint32_t GiB = 1073741824; 96 97 if( totalSize >= ( 2 * GiB ) ) return 2 * MiB; 98 if( totalSize >= ( 1 * GiB ) ) return 1 * MiB; 99 if( totalSize >= ( 512 * MiB ) ) return 512 * KiB; 100 if( totalSize >= ( 350 * MiB ) ) return 256 * KiB; 101 if( totalSize >= ( 150 * MiB ) ) return 128 * KiB; 102 if( totalSize >= ( 50 * MiB ) ) return 64 * KiB; 103 return 32 * KiB; /* less than 50 meg */ 104} 105 106static int 107builderFileCompare( const void * va, const void * vb ) 108{ 109 const tr_metainfo_builder_file * a = va; 110 const tr_metainfo_builder_file * b = vb; 111 112 return evutil_ascii_strcasecmp( a->filename, b->filename ); 113} 114 115tr_metainfo_builder* 116tr_metaInfoBuilderCreate( const char * topFileArg ) 117{ 118 int i; 119 struct FileList * files; 120 struct FileList * walk; 121 char topFile[TR_PATH_MAX]; 122 tr_metainfo_builder * ret = tr_new0( tr_metainfo_builder, 1 ); 123 124 tr_realpath( topFileArg, topFile ); 125 126 ret->top = tr_strdup( topFile ); 127 128 { 129 struct stat sb; 130 stat( topFile, &sb ); 131 ret->isSingleFile = !S_ISDIR( sb.st_mode ); 132 } 133 134 /* build a list of files containing topFile and, 135 if it's a directory, all of its children */ 136 { 137 char * dir = tr_dirname( topFile ); 138 char * base = tr_basename( topFile ); 139 files = getFiles( dir, base, NULL ); 140 tr_free( base ); 141 tr_free( dir ); 142 } 143 144 for( walk = files; walk != NULL; walk = walk->next ) 145 ++ret->fileCount; 146 147 ret->files = tr_new0( tr_metainfo_builder_file, ret->fileCount ); 148 149 for( i = 0, walk = files; walk != NULL; ++i ) 150 { 151 struct FileList * tmp = walk; 152 tr_metainfo_builder_file * file = &ret->files[i]; 153 walk = walk->next; 154 file->filename = tmp->filename; 155 file->size = tmp->size; 156 ret->totalSize += tmp->size; 157 tr_free( tmp ); 158 } 159 160 qsort( ret->files, 161 ret->fileCount, 162 sizeof( tr_metainfo_builder_file ), 163 builderFileCompare ); 164 165 ret->pieceSize = bestPieceSize( ret->totalSize ); 166 ret->pieceCount = (int)( ret->totalSize / ret->pieceSize ); 167 if( ret->totalSize % ret->pieceSize ) 168 ++ret->pieceCount; 169 170 return ret; 171} 172 173void 174tr_metaInfoBuilderFree( tr_metainfo_builder * builder ) 175{ 176 if( builder ) 177 { 178 tr_file_index_t t; 179 int i; 180 for( t = 0; t < builder->fileCount; ++t ) 181 tr_free( builder->files[t].filename ); 182 tr_free( builder->files ); 183 tr_free( builder->top ); 184 tr_free( builder->comment ); 185 for( i = 0; i < builder->trackerCount; ++i ) 186 tr_free( builder->trackers[i].announce ); 187 tr_free( builder->trackers ); 188 tr_free( builder->outputFile ); 189 tr_free( builder ); 190 } 191} 192 193/**** 194***** 195****/ 196 197static uint8_t* 198getHashInfo( tr_metainfo_builder * b ) 199{ 200 uint32_t fileIndex = 0; 201 uint8_t *ret = tr_new0( uint8_t, SHA_DIGEST_LENGTH * b->pieceCount ); 202 uint8_t *walk = ret; 203 uint8_t *buf; 204 uint64_t totalRemain; 205 uint64_t off = 0; 206 int fd; 207 208 if( !b->totalSize ) 209 return ret; 210 211 buf = tr_valloc( b->pieceSize ); 212 b->pieceIndex = 0; 213 totalRemain = b->totalSize; 214 fd = tr_open_file_for_scanning( b->files[fileIndex].filename ); 215 if( fd < 0 ) 216 { 217 b->my_errno = errno; 218 tr_strlcpy( b->errfile, 219 b->files[fileIndex].filename, 220 sizeof( b->errfile ) ); 221 b->result = TR_MAKEMETA_IO_READ; 222 tr_free( buf ); 223 tr_free( ret ); 224 return NULL; 225 } 226 while( totalRemain ) 227 { 228 uint8_t * bufptr = buf; 229 const uint32_t thisPieceSize = (uint32_t) MIN( b->pieceSize, totalRemain ); 230 uint32_t leftInPiece = thisPieceSize; 231 232 assert( b->pieceIndex < b->pieceCount ); 233 234 while( leftInPiece ) 235 { 236 const size_t n_this_pass = (size_t) MIN( ( b->files[fileIndex].size - off ), leftInPiece ); 237 const ssize_t n_read = read( fd, bufptr, n_this_pass ); 238 bufptr += n_read; 239 off += n_read; 240 leftInPiece -= n_read; 241 if( off == b->files[fileIndex].size ) 242 { 243 off = 0; 244 tr_close_file( fd ); 245 fd = -1; 246 if( ++fileIndex < b->fileCount ) 247 { 248 fd = tr_open_file_for_scanning( b->files[fileIndex].filename ); 249 if( fd < 0 ) 250 { 251 b->my_errno = errno; 252 tr_strlcpy( b->errfile, 253 b->files[fileIndex].filename, 254 sizeof( b->errfile ) ); 255 b->result = TR_MAKEMETA_IO_READ; 256 tr_free( buf ); 257 tr_free( ret ); 258 return NULL; 259 } 260 } 261 } 262 } 263 264 assert( bufptr - buf == (int)thisPieceSize ); 265 assert( leftInPiece == 0 ); 266 tr_sha1( walk, buf, thisPieceSize, NULL ); 267 walk += SHA_DIGEST_LENGTH; 268 269 if( b->abortFlag ) 270 { 271 b->result = TR_MAKEMETA_CANCELLED; 272 break; 273 } 274 275 totalRemain -= thisPieceSize; 276 ++b->pieceIndex; 277 } 278 279 assert( b->abortFlag 280 || ( walk - ret == (int)( SHA_DIGEST_LENGTH * b->pieceCount ) ) ); 281 assert( b->abortFlag || !totalRemain ); 282 283 if( fd >= 0 ) 284 tr_close_file( fd ); 285 286 tr_free( buf ); 287 return ret; 288} 289 290static void 291getFileInfo( const char * topFile, 292 const tr_metainfo_builder_file * file, 293 tr_benc * uninitialized_length, 294 tr_benc * uninitialized_path ) 295{ 296 size_t offset; 297 298 /* get the file size */ 299 tr_bencInitInt( uninitialized_length, file->size ); 300 301 /* how much of file->filename to walk past */ 302 offset = strlen( topFile ); 303 if( offset>0 && topFile[offset-1]!=TR_PATH_DELIMITER ) 304 ++offset; /* +1 for the path delimiter */ 305 306 /* build the path list */ 307 tr_bencInitList( uninitialized_path, 0 ); 308 if( strlen(file->filename) > offset ) { 309 char * filename = tr_strdup( file->filename + offset ); 310 char * walk = filename; 311 const char * token; 312 while(( token = tr_strsep( &walk, TR_PATH_DELIMITER_STR ))) 313 tr_bencListAddStr( uninitialized_path, token ); 314 tr_free( filename ); 315 } 316} 317 318static void 319makeInfoDict( tr_benc * dict, 320 tr_metainfo_builder * builder ) 321{ 322 uint8_t * pch; 323 char * base; 324 325 tr_bencDictReserve( dict, 5 ); 326 327 if( builder->isSingleFile ) 328 { 329 tr_bencDictAddInt( dict, "length", builder->files[0].size ); 330 } 331 else /* root node is a directory */ 332 { 333 uint32_t i; 334 tr_benc * list = tr_bencDictAddList( dict, "files", 335 builder->fileCount ); 336 for( i = 0; i < builder->fileCount; ++i ) 337 { 338 tr_benc * d = tr_bencListAddDict( list, 2 ); 339 tr_benc * length = tr_bencDictAdd( d, "length" ); 340 tr_benc * pathVal = tr_bencDictAdd( d, "path" ); 341 getFileInfo( builder->top, &builder->files[i], length, pathVal ); 342 } 343 } 344 345 base = tr_basename( builder->top ); 346 tr_bencDictAddStr( dict, "name", base ); 347 tr_free( base ); 348 349 tr_bencDictAddInt( dict, "piece length", builder->pieceSize ); 350 351 if( ( pch = getHashInfo( builder ) ) ) 352 { 353 tr_bencDictAddRaw( dict, "pieces", pch, 354 SHA_DIGEST_LENGTH * builder->pieceCount ); 355 tr_free( pch ); 356 } 357 358 tr_bencDictAddInt( dict, "private", builder->isPrivate ? 1 : 0 ); 359} 360 361static void 362tr_realMakeMetaInfo( tr_metainfo_builder * builder ) 363{ 364 int i; 365 tr_benc top; 366 367 /* allow an empty set, but if URLs *are* listed, verify them. #814, #971 */ 368 for( i = 0; i < builder->trackerCount && !builder->result; ++i ) { 369 if( !tr_urlIsValidTracker( builder->trackers[i].announce ) ) { 370 tr_strlcpy( builder->errfile, builder->trackers[i].announce, 371 sizeof( builder->errfile ) ); 372 builder->result = TR_MAKEMETA_URL; 373 } 374 } 375 376 tr_bencInitDict( &top, 6 ); 377 378 if( !builder->fileCount || !builder->totalSize || 379 !builder->pieceSize || !builder->pieceCount ) 380 { 381 builder->errfile[0] = '\0'; 382 builder->my_errno = ENOENT; 383 builder->result = TR_MAKEMETA_IO_READ; 384 builder->isDone = true; 385 } 386 387 if( !builder->result && builder->trackerCount ) 388 { 389 int prevTier = -1; 390 tr_benc * tier = NULL; 391 392 if( builder->trackerCount > 1 ) 393 { 394 tr_benc * annList = tr_bencDictAddList( &top, "announce-list", 395 0 ); 396 for( i = 0; i < builder->trackerCount; ++i ) 397 { 398 if( prevTier != builder->trackers[i].tier ) 399 { 400 prevTier = builder->trackers[i].tier; 401 tier = tr_bencListAddList( annList, 0 ); 402 } 403 tr_bencListAddStr( tier, builder->trackers[i].announce ); 404 } 405 } 406 407 tr_bencDictAddStr( &top, "announce", builder->trackers[0].announce ); 408 } 409 410 if( !builder->result && !builder->abortFlag ) 411 { 412 if( builder->comment && *builder->comment ) 413 tr_bencDictAddStr( &top, "comment", builder->comment ); 414 tr_bencDictAddStr( &top, "created by", 415 TR_NAME "/" LONG_VERSION_STRING ); 416 tr_bencDictAddInt( &top, "creation date", time( NULL ) ); 417 tr_bencDictAddStr( &top, "encoding", "UTF-8" ); 418 makeInfoDict( tr_bencDictAddDict( &top, "info", 666 ), builder ); 419 } 420 421 /* save the file */ 422 if( !builder->result && !builder->abortFlag ) 423 { 424 if( tr_bencToFile( &top, TR_FMT_BENC, builder->outputFile ) ) 425 { 426 builder->my_errno = errno; 427 tr_strlcpy( builder->errfile, builder->outputFile, 428 sizeof( builder->errfile ) ); 429 builder->result = TR_MAKEMETA_IO_WRITE; 430 } 431 } 432 433 /* cleanup */ 434 tr_bencFree( &top ); 435 if( builder->abortFlag ) 436 builder->result = TR_MAKEMETA_CANCELLED; 437 builder->isDone = 1; 438} 439 440/*** 441**** 442**** A threaded builder queue 443**** 444***/ 445 446static tr_metainfo_builder * queue = NULL; 447 448static tr_thread * workerThread = NULL; 449 450static tr_lock* 451getQueueLock( void ) 452{ 453 static tr_lock * lock = NULL; 454 455 if( !lock ) 456 lock = tr_lockNew( ); 457 458 return lock; 459} 460 461static void 462makeMetaWorkerFunc( void * unused UNUSED ) 463{ 464 for( ;; ) 465 { 466 tr_metainfo_builder * builder = NULL; 467 468 /* find the next builder to process */ 469 tr_lock * lock = getQueueLock( ); 470 tr_lockLock( lock ); 471 if( queue ) 472 { 473 builder = queue; 474 queue = queue->nextBuilder; 475 } 476 tr_lockUnlock( lock ); 477 478 /* if no builders, this worker thread is done */ 479 if( builder == NULL ) 480 break; 481 482 tr_realMakeMetaInfo ( builder ); 483 } 484 485 workerThread = NULL; 486} 487 488void 489tr_makeMetaInfo( tr_metainfo_builder * builder, 490 const char * outputFile, 491 const tr_tracker_info * trackers, 492 int trackerCount, 493 const char * comment, 494 int isPrivate ) 495{ 496 int i; 497 tr_lock * lock; 498 499 /* free any variables from a previous run */ 500 for( i = 0; i < builder->trackerCount; ++i ) 501 tr_free( builder->trackers[i].announce ); 502 tr_free( builder->trackers ); 503 tr_free( builder->comment ); 504 tr_free( builder->outputFile ); 505 506 /* initialize the builder variables */ 507 builder->abortFlag = 0; 508 builder->result = 0; 509 builder->isDone = 0; 510 builder->pieceIndex = 0; 511 builder->trackerCount = trackerCount; 512 builder->trackers = tr_new0( tr_tracker_info, builder->trackerCount ); 513 for( i = 0; i < builder->trackerCount; ++i ) { 514 builder->trackers[i].tier = trackers[i].tier; 515 builder->trackers[i].announce = tr_strdup( trackers[i].announce ); 516 } 517 builder->comment = tr_strdup( comment ); 518 builder->isPrivate = isPrivate; 519 if( outputFile && *outputFile ) 520 builder->outputFile = tr_strdup( outputFile ); 521 else 522 builder->outputFile = tr_strdup_printf( "%s.torrent", builder->top ); 523 524 /* enqueue the builder */ 525 lock = getQueueLock ( ); 526 tr_lockLock( lock ); 527 builder->nextBuilder = queue; 528 queue = builder; 529 if( !workerThread ) 530 workerThread = tr_threadNew( makeMetaWorkerFunc, NULL ); 531 tr_lockUnlock( lock ); 532} 533 534