KTorrent – BitTorrent client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1158 lines
27KB

  1. /***************************************************************************
  2. * Copyright (C) 2005 by *
  3. * Joris Guisson <joris.guisson@gmail.com> *
  4. * Ivan Vasic <ivasic@gmail.com> *
  5. * *
  6. * This program is free software; you can redistribute it and/or modify *
  7. * it under the terms of the GNU General Public License as published by *
  8. * the Free Software Foundation; either version 2 of the License, or *
  9. * (at your option) any later version. *
  10. * *
  11. * This program is distributed in the hope that it will be useful, *
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  14. * GNU General Public License for more details. *
  15. * *
  16. * You should have received a copy of the GNU General Public License *
  17. * along with this program; if not, write to the *
  18. * Free Software Foundation, Inc., *
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
  20. ***************************************************************************/
  21. #include <algorithm>
  22. #include <util/file.h>
  23. #include <util/array.h>
  24. #include <tqstringlist.h>
  25. #include "chunkmanager.h"
  26. #include "torrent.h"
  27. #include <util/error.h>
  28. #include <util/bitset.h>
  29. #include <util/fileops.h>
  30. #include "singlefilecache.h"
  31. #include "multifilecache.h"
  32. #include <util/log.h>
  33. #include <util/functions.h>
  34. #include "globals.h"
  35. #include <tdelocale.h>
  36. namespace bt
  37. {
  38. Uint32 ChunkManager::max_chunk_size_for_data_check = 0;
  39. ChunkManager::ChunkManager(
  40. Torrent & tor,
  41. const TQString & tmpdir,
  42. const TQString & datadir,
  43. bool custom_output_name)
  44. : tor(tor),chunks(tor.getNumChunks()),
  45. bitset(tor.getNumChunks()),excluded_chunks(tor.getNumChunks()),only_seed_chunks(tor.getNumChunks()),todo(tor.getNumChunks())
  46. {
  47. during_load = false;
  48. only_seed_chunks.setAll(false);
  49. todo.setAll(true);
  50. if (tor.isMultiFile())
  51. cache = new MultiFileCache(tor,tmpdir,datadir,custom_output_name);
  52. else
  53. cache = new SingleFileCache(tor,tmpdir,datadir);
  54. index_file = tmpdir + "index";
  55. file_info_file = tmpdir + "file_info";
  56. file_priority_file = tmpdir + "file_priority";
  57. Uint64 tsize = tor.getFileLength(); // total size
  58. Uint64 csize = tor.getChunkSize(); // chunk size
  59. Uint64 lsize = tsize - (csize * (tor.getNumChunks() - 1)); // size of last chunk
  60. for (Uint32 i = 0;i < tor.getNumChunks();i++)
  61. {
  62. if (i + 1 < tor.getNumChunks())
  63. chunks.insert(i,new Chunk(i,csize));
  64. else
  65. chunks.insert(i,new Chunk(i,lsize));
  66. }
  67. chunks.setAutoDelete(true);
  68. chunks_left = 0;
  69. recalc_chunks_left = true;
  70. corrupted_count = recheck_counter = 0;
  71. for (Uint32 i = 0;i < tor.getNumFiles();i++)
  72. {
  73. TorrentFile & tf = tor.getFile(i);
  74. connect(&tf,TQT_SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
  75. this,TQT_SLOT(downloadPriorityChanged(TorrentFile*, Priority, Priority )));
  76. if (tf.getPriority() != NORMAL_PRIORITY)
  77. {
  78. downloadPriorityChanged(&tf,tf.getPriority(),tf.getOldPriority());
  79. }
  80. }
  81. if(tor.isMultiFile())
  82. {
  83. for(Uint32 i=0; i<tor.getNumFiles(); ++i)
  84. {
  85. bt::TorrentFile & file = tor.getFile(i);
  86. if (!file.isMultimedia() || file.getPriority() == bt::ONLY_SEED_PRIORITY)
  87. continue;
  88. if (file.getFirstChunk() == file.getLastChunk())
  89. {
  90. // prioritise whole file
  91. prioritise(file.getFirstChunk(),file.getLastChunk(),PREVIEW_PRIORITY);
  92. }
  93. else
  94. {
  95. Uint32 chunkOffset;
  96. chunkOffset = ((file.getLastChunk() - file.getFirstChunk()) / 100) + 1;
  97. prioritise(file.getFirstChunk(), file.getFirstChunk()+chunkOffset, PREVIEW_PRIORITY);
  98. if (file.getLastChunk() - file.getFirstChunk() > chunkOffset)
  99. {
  100. prioritise(file.getLastChunk() - chunkOffset, file.getLastChunk(), PREVIEW_PRIORITY);
  101. }
  102. }
  103. }
  104. }
  105. else
  106. {
  107. if(tor.isMultimedia())
  108. {
  109. Uint32 chunkOffset;
  110. chunkOffset = (tor.getNumChunks() / 100) + 1;
  111. prioritise(0,chunkOffset,PREVIEW_PRIORITY);
  112. if (tor.getNumChunks() > chunkOffset)
  113. {
  114. prioritise(tor.getNumChunks() - chunkOffset, tor.getNumChunks() - 1,PREVIEW_PRIORITY);
  115. }
  116. }
  117. }
  118. }
  119. ChunkManager::~ChunkManager()
  120. {
  121. delete cache;
  122. }
  123. TQString ChunkManager::getDataDir() const
  124. {
  125. return cache->getDataDir();
  126. }
  127. void ChunkManager::changeDataDir(const TQString & data_dir)
  128. {
  129. cache->changeTmpDir(data_dir);
  130. index_file = data_dir + "index";
  131. file_info_file = data_dir + "file_info";
  132. file_priority_file = data_dir + "file_priority";
  133. }
  134. TDEIO::Job* ChunkManager::moveDataFiles(const TQString & ndir)
  135. {
  136. return cache->moveDataFiles(ndir);
  137. }
  138. void ChunkManager::moveDataFilesCompleted(TDEIO::Job* job)
  139. {
  140. cache->moveDataFilesCompleted(job);
  141. }
  142. void ChunkManager::changeOutputPath(const TQString & output_path)
  143. {
  144. cache->changeOutputPath(output_path);
  145. }
  146. void ChunkManager::loadIndexFile()
  147. {
  148. during_load = true;
  149. loadPriorityInfo();
  150. File fptr;
  151. if (!fptr.open(index_file,"rb"))
  152. {
  153. // no index file, so assume it's empty
  154. bt::Touch(index_file,true);
  155. Out(SYS_DIO|LOG_IMPORTANT) << "Can't open index file : " << fptr.errorString() << endl;
  156. during_load = false;
  157. return;
  158. }
  159. if (fptr.seek(File::END,0) != 0)
  160. {
  161. fptr.seek(File::BEGIN,0);
  162. while (!fptr.eof())
  163. {
  164. NewChunkHeader hdr;
  165. fptr.read(&hdr,sizeof(NewChunkHeader));
  166. Chunk* c = getChunk(hdr.index);
  167. if (c)
  168. {
  169. c->setStatus(Chunk::ON_DISK);
  170. bitset.set(hdr.index,true);
  171. todo.set(hdr.index,false);
  172. recalc_chunks_left = true;
  173. }
  174. }
  175. }
  176. tor.updateFilePercentage(bitset);
  177. during_load = false;
  178. }
  179. void ChunkManager::saveIndexFile()
  180. {
  181. File fptr;
  182. if (!fptr.open(index_file,"wb"))
  183. throw Error(i18n("Cannot open index file %1 : %2").arg(index_file).arg(fptr.errorString()));
  184. for (unsigned int i = 0;i < tor.getNumChunks();i++)
  185. {
  186. Chunk* c = getChunk(i);
  187. if (c->getStatus() != Chunk::NOT_DOWNLOADED)
  188. {
  189. NewChunkHeader hdr;
  190. hdr.index = i;
  191. fptr.write(&hdr,sizeof(NewChunkHeader));
  192. }
  193. }
  194. savePriorityInfo();
  195. }
  196. void ChunkManager::createFiles(bool check_priority)
  197. {
  198. if (!bt::Exists(index_file))
  199. {
  200. File fptr;
  201. fptr.open(index_file,"wb");
  202. }
  203. cache->create();
  204. if (check_priority)
  205. {
  206. for (Uint32 i = 0;i < tor.getNumFiles();i++)
  207. {
  208. TorrentFile & tf = tor.getFile(i);
  209. connect(&tf,TQT_SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
  210. this,TQT_SLOT(downloadPriorityChanged(TorrentFile*, Priority, Priority )));
  211. if (tf.getPriority() != NORMAL_PRIORITY)
  212. {
  213. downloadPriorityChanged(&tf,tf.getPriority(),tf.getOldPriority());
  214. }
  215. }
  216. }
  217. }
  218. bool ChunkManager::hasMissingFiles(TQStringList & sl)
  219. {
  220. return cache->hasMissingFiles(sl);
  221. }
  222. Chunk* ChunkManager::getChunk(unsigned int i)
  223. {
  224. if (i >= chunks.count())
  225. return 0;
  226. else
  227. return chunks[i];
  228. }
  229. void ChunkManager::start()
  230. {
  231. cache->open();
  232. }
  233. void ChunkManager::stop()
  234. {
  235. // unmmap all chunks which can
  236. for (Uint32 i = 0;i < bitset.getNumBits();i++)
  237. {
  238. Chunk* c = chunks[i];
  239. if (c->getStatus() == Chunk::MMAPPED)
  240. {
  241. cache->save(c);
  242. c->clear();
  243. c->setStatus(Chunk::ON_DISK);
  244. }
  245. else if (c->getStatus() == Chunk::BUFFERED)
  246. {
  247. c->clear();
  248. c->setStatus(Chunk::ON_DISK);
  249. }
  250. }
  251. cache->close();
  252. }
  253. Chunk* ChunkManager::grabChunk(unsigned int i)
  254. {
  255. if (i >= chunks.size())
  256. return 0;
  257. Chunk* c = chunks[i];
  258. if (c->getStatus() == Chunk::NOT_DOWNLOADED || c->isExcluded())
  259. {
  260. return 0;
  261. }
  262. else if (c->getStatus() == Chunk::ON_DISK)
  263. {
  264. // load the chunk if it is on disk
  265. cache->load(c);
  266. loaded.insert(i,bt::GetCurrentTime());
  267. bool check_allowed = (max_chunk_size_for_data_check == 0 || tor.getChunkSize() <= max_chunk_size_for_data_check);
  268. // when no corruptions have been found, only check once every 5 chunks
  269. if (check_allowed && recheck_counter < 5 && corrupted_count == 0)
  270. check_allowed = false;
  271. if (c->getData() && check_allowed)
  272. {
  273. recheck_counter = 0;
  274. if (!c->checkHash(tor.getHash(i)))
  275. {
  276. Out(SYS_DIO|LOG_IMPORTANT) << "Chunk " << i
  277. << " has been found invalid, redownloading" << endl;
  278. resetChunk(i);
  279. tor.updateFilePercentage(i,bitset);
  280. saveIndexFile();
  281. recalc_chunks_left = true;
  282. corrupted_count++;
  283. corrupted(i);
  284. return 0;
  285. }
  286. }
  287. else
  288. {
  289. recheck_counter++;
  290. }
  291. }
  292. loaded.insert(i,bt::GetCurrentTime());
  293. return c;
  294. }
  295. void ChunkManager::releaseChunk(unsigned int i)
  296. {
  297. if (i >= chunks.size())
  298. return;
  299. Chunk* c = chunks[i];
  300. if (!c->taken())
  301. {
  302. if (c->getStatus() == Chunk::MMAPPED)
  303. cache->save(c);
  304. c->clear();
  305. c->setStatus(Chunk::ON_DISK);
  306. loaded.remove(i);
  307. }
  308. }
  309. void ChunkManager::resetChunk(unsigned int i)
  310. {
  311. if (i >= chunks.size())
  312. return;
  313. Chunk* c = chunks[i];
  314. if (c->getStatus() == Chunk::MMAPPED)
  315. cache->save(c);
  316. c->clear();
  317. c->setStatus(Chunk::NOT_DOWNLOADED);
  318. bitset.set(i,false);
  319. todo.set(i,!excluded_chunks.get(i) && !only_seed_chunks.get(i));
  320. loaded.remove(i);
  321. tor.updateFilePercentage(i,bitset);
  322. }
  323. void ChunkManager::checkMemoryUsage()
  324. {
  325. Uint32 num_removed = 0;
  326. TQMap<Uint32,TimeStamp>::iterator i = loaded.begin();
  327. while (i != loaded.end())
  328. {
  329. Chunk* c = chunks[i.key()];
  330. // get rid of chunk if nobody asked for it in the last 5 seconds
  331. if (!c->taken() && bt::GetCurrentTime() - i.data() > 5000)
  332. {
  333. if (c->getStatus() == Chunk::MMAPPED)
  334. cache->save(c);
  335. c->clear();
  336. c->setStatus(Chunk::ON_DISK);
  337. TQMap<Uint32,TimeStamp>::iterator j = i;
  338. i++;
  339. loaded.erase(j);
  340. num_removed++;
  341. }
  342. else
  343. {
  344. i++;
  345. }
  346. }
  347. // Uint32 num_in_mem = loaded.count();
  348. // Out() << TQString("Cleaned %1 chunks, %2 still in memory").arg(num_removed).arg(num_in_mem) << endl;
  349. }
  350. void ChunkManager::saveChunk(unsigned int i,bool update_index)
  351. {
  352. if (i >= chunks.size())
  353. return;
  354. Chunk* c = chunks[i];
  355. if (!c->isExcluded())
  356. {
  357. cache->save(c);
  358. // update the index file
  359. if (update_index)
  360. {
  361. bitset.set(i,true);
  362. todo.set(i,false);
  363. recalc_chunks_left = true;
  364. writeIndexFileEntry(c);
  365. tor.updateFilePercentage(i,bitset);
  366. }
  367. }
  368. else
  369. {
  370. c->clear();
  371. c->setStatus(Chunk::NOT_DOWNLOADED);
  372. Out(SYS_DIO|LOG_IMPORTANT) << "Warning: attempted to save a chunk which was excluded" << endl;
  373. }
  374. }
  375. void ChunkManager::writeIndexFileEntry(Chunk* c)
  376. {
  377. File fptr;
  378. if (!fptr.open(index_file,"r+b"))
  379. {
  380. // no index file, so assume it's empty
  381. bt::Touch(index_file,true);
  382. Out(SYS_DIO|LOG_IMPORTANT) << "Can't open index file : " << fptr.errorString() << endl;
  383. // try again
  384. if (!fptr.open(index_file,"r+b"))
  385. // panick if it failes
  386. throw Error(i18n("Cannot open index file %1 : %2").arg(index_file).arg(fptr.errorString()));
  387. }
  388. fptr.seek(File::END,0);
  389. NewChunkHeader hdr;
  390. hdr.index = c->getIndex();
  391. fptr.write(&hdr,sizeof(NewChunkHeader));
  392. }
  393. Uint32 ChunkManager::onlySeedChunks() const
  394. {
  395. return only_seed_chunks.numOnBits();
  396. }
  397. bool ChunkManager::completed() const
  398. {
  399. return todo.numOnBits() == 0 && bitset.numOnBits() > 0;
  400. }
  401. Uint64 ChunkManager::bytesLeft() const
  402. {
  403. Uint32 num_left = bitset.getNumBits() - bitset.numOnBits();
  404. Uint32 last = chunks.size() - 1;
  405. if (last < chunks.size() && !bitset.get(last))
  406. {
  407. Chunk* c = chunks[last];
  408. if (c)
  409. return (num_left - 1)*tor.getChunkSize() + c->getSize();
  410. else
  411. return num_left*tor.getChunkSize();
  412. }
  413. else
  414. {
  415. return num_left*tor.getChunkSize();
  416. }
  417. }
  418. Uint64 ChunkManager::bytesLeftToDownload() const
  419. {
  420. Uint32 num_left = todo.numOnBits();
  421. Uint32 last = chunks.size() - 1;
  422. if (last < chunks.size() && todo.get(last))
  423. {
  424. Chunk* c = chunks[last];
  425. if (c)
  426. return (num_left - 1)*tor.getChunkSize() + c->getSize();
  427. else
  428. return num_left*tor.getChunkSize();
  429. }
  430. else
  431. {
  432. return num_left*tor.getChunkSize();
  433. }
  434. }
  435. Uint32 ChunkManager::chunksLeft() const
  436. {
  437. if (!recalc_chunks_left)
  438. return chunks_left;
  439. Uint32 num = 0;
  440. Uint32 tot = chunks.size();
  441. for (Uint32 i = 0;i < tot;i++)
  442. {
  443. const Chunk* c = chunks[i];
  444. if (!bitset.get(i) && !c->isExcluded())
  445. num++;
  446. }
  447. chunks_left = num;
  448. recalc_chunks_left = false;
  449. return num;
  450. }
  451. bool ChunkManager::haveAllChunks() const
  452. {
  453. return bitset.numOnBits() == bitset.getNumBits();
  454. }
  455. Uint64 ChunkManager::bytesExcluded() const
  456. {
  457. Uint64 excl = 0;
  458. if (excluded_chunks.get(tor.getNumChunks() - 1))
  459. {
  460. Chunk* c = chunks[tor.getNumChunks() - 1];
  461. Uint32 num = excluded_chunks.numOnBits() - 1;
  462. excl = tor.getChunkSize() * num + c->getSize();
  463. }
  464. else
  465. {
  466. excl = tor.getChunkSize() * excluded_chunks.numOnBits();
  467. }
  468. if (only_seed_chunks.get(tor.getNumChunks() - 1))
  469. {
  470. Chunk* c = chunks[tor.getNumChunks() - 1];
  471. Uint32 num = only_seed_chunks.numOnBits() - 1;
  472. excl += tor.getChunkSize() * num + c->getSize();
  473. }
  474. else
  475. {
  476. excl += tor.getChunkSize() * only_seed_chunks.numOnBits();
  477. }
  478. return excl;
  479. }
  480. Uint32 ChunkManager::chunksExcluded() const
  481. {
  482. return excluded_chunks.numOnBits() + only_seed_chunks.numOnBits();
  483. }
  484. Uint32 ChunkManager::chunksDownloaded() const
  485. {
  486. return bitset.numOnBits();
  487. }
  488. void ChunkManager::debugPrintMemUsage()
  489. {
  490. Out(SYS_DIO|LOG_DEBUG) << "Active Chunks : " << loaded.count()<< endl;
  491. }
  492. void ChunkManager::prioritise(Uint32 from,Uint32 to,Priority priority)
  493. {
  494. if (from > to)
  495. std::swap(from,to);
  496. Uint32 i = from;
  497. while (i <= to && i < chunks.count())
  498. {
  499. Chunk* c = chunks[i];
  500. c->setPriority(priority);
  501. if (priority == ONLY_SEED_PRIORITY)
  502. {
  503. only_seed_chunks.set(i,true);
  504. todo.set(i,false);
  505. }
  506. else if (priority == EXCLUDED)
  507. {
  508. only_seed_chunks.set(i,false);
  509. todo.set(i,false);
  510. }
  511. else
  512. {
  513. only_seed_chunks.set(i,false);
  514. todo.set(i,!bitset.get(i));
  515. }
  516. i++;
  517. }
  518. updateStats();
  519. }
  520. void ChunkManager::exclude(Uint32 from,Uint32 to)
  521. {
  522. if (from > to)
  523. std::swap(from,to);
  524. Uint32 i = from;
  525. while (i <= to && i < chunks.count())
  526. {
  527. Chunk* c = chunks[i];
  528. c->setExclude(true);
  529. excluded_chunks.set(i,true);
  530. only_seed_chunks.set(i,false);
  531. todo.set(i,false);
  532. bitset.set(i,false);
  533. i++;
  534. }
  535. recalc_chunks_left = true;
  536. excluded(from,to);
  537. updateStats();
  538. }
  539. void ChunkManager::include(Uint32 from,Uint32 to)
  540. {
  541. if (from > to)
  542. std::swap(from,to);
  543. Uint32 i = from;
  544. while (i <= to && i < chunks.count())
  545. {
  546. Chunk* c = chunks[i];
  547. c->setExclude(false);
  548. excluded_chunks.set(i,false);
  549. if (!bitset.get(i))
  550. todo.set(i,true);
  551. i++;
  552. }
  553. recalc_chunks_left = true;
  554. updateStats();
  555. included(from,to);
  556. }
  557. void ChunkManager::saveFileInfo()
  558. {
  559. // saves which TorrentFiles do not need to be downloaded
  560. File fptr;
  561. if (!fptr.open(file_info_file,"wb"))
  562. {
  563. Out(SYS_DIO|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl;
  564. return;
  565. }
  566. // first write the number of excluded ones
  567. // don't know this yet, so write 0 for the time being
  568. Uint32 tmp = 0;
  569. fptr.write(&tmp,sizeof(Uint32));
  570. Uint32 i = 0;
  571. Uint32 cnt = 0;
  572. while (i < tor.getNumFiles())
  573. {
  574. if (tor.getFile(i).doNotDownload())
  575. {
  576. fptr.write(&i,sizeof(Uint32));
  577. cnt++;
  578. }
  579. i++;
  580. }
  581. // go back to the beginning and write the number of files
  582. fptr.seek(File::BEGIN,0);
  583. fptr.write(&cnt,sizeof(Uint32));
  584. fptr.flush();
  585. }
  586. void ChunkManager::loadFileInfo()
  587. {
  588. if (during_load)
  589. return;
  590. File fptr;
  591. if (!fptr.open(file_info_file,"rb"))
  592. return;
  593. Uint32 num = 0,tmp = 0;
  594. // first read the number of dnd files
  595. if (fptr.read(&num,sizeof(Uint32)) != sizeof(Uint32))
  596. {
  597. Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
  598. return;
  599. }
  600. for (Uint32 i = 0;i < num;i++)
  601. {
  602. if (fptr.read(&tmp,sizeof(Uint32)) != sizeof(Uint32))
  603. {
  604. Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
  605. return;
  606. }
  607. bt::TorrentFile & tf = tor.getFile(tmp);
  608. if (!tf.isNull())
  609. {
  610. Out(SYS_DIO|LOG_DEBUG) << "Excluding : " << tf.getPath() << endl;
  611. tf.setDoNotDownload(true);
  612. }
  613. }
  614. }
  615. void ChunkManager::savePriorityInfo()
  616. {
  617. if (during_load)
  618. return;
  619. //save priority info and call saveFileInfo
  620. saveFileInfo();
  621. File fptr;
  622. if (!fptr.open(file_priority_file,"wb"))
  623. {
  624. Out(SYS_DIO|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl;
  625. return;
  626. }
  627. try
  628. {
  629. // first write the number of excluded ones
  630. // don't know this yet, so write 0 for the time being
  631. Uint32 tmp = 0;
  632. fptr.write(&tmp,sizeof(Uint32));
  633. Uint32 i = 0;
  634. Uint32 cnt = 0;
  635. while (i < tor.getNumFiles())
  636. {
  637. const TorrentFile & tf = tor.getFile(i);
  638. if (tf.getPriority() != NORMAL_PRIORITY)
  639. {
  640. tmp = tf.getPriority();
  641. fptr.write(&i,sizeof(Uint32));
  642. fptr.write(&tmp,sizeof(Uint32));
  643. cnt+=2;
  644. }
  645. i++;
  646. }
  647. // go back to the beginning and write the number of items
  648. fptr.seek(File::BEGIN,0);
  649. fptr.write(&cnt,sizeof(Uint32));
  650. fptr.flush();
  651. }
  652. catch (bt::Error & err)
  653. {
  654. Out(SYS_DIO|LOG_IMPORTANT) << "Failed to save priority file " << err.toString() << endl;
  655. bt::Delete(file_priority_file,true);
  656. }
  657. }
  658. void ChunkManager::loadPriorityInfo()
  659. {
  660. //load priority info and if that fails load file info
  661. File fptr;
  662. if (!fptr.open(file_priority_file,"rb"))
  663. {
  664. loadFileInfo();
  665. return;
  666. }
  667. Uint32 num = 0;
  668. // first read the number of lines
  669. if (fptr.read(&num,sizeof(Uint32)) != sizeof(Uint32) || num > 2*tor.getNumFiles())
  670. {
  671. Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
  672. loadFileInfo();
  673. return;
  674. }
  675. Array<Uint32> buf(num);
  676. if (fptr.read(buf,sizeof(Uint32)*num) != sizeof(Uint32)*num)
  677. {
  678. Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
  679. loadFileInfo();
  680. return;
  681. }
  682. fptr.close();
  683. for (Uint32 i = 0;i < num;i += 2)
  684. {
  685. Uint32 idx = buf[i];
  686. if (idx >= tor.getNumFiles())
  687. {
  688. Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
  689. loadFileInfo();
  690. return;
  691. }
  692. bt::TorrentFile & tf = tor.getFile(idx);
  693. if (!tf.isNull())
  694. {
  695. // numbers are to be compatible with old chunk info files
  696. switch(buf[i+1])
  697. {
  698. case FIRST_PRIORITY:
  699. case 3:
  700. tf.setPriority(FIRST_PRIORITY);
  701. break;
  702. case NORMAL_PRIORITY:
  703. case 2:
  704. tf.setPriority(NORMAL_PRIORITY);
  705. break;
  706. case EXCLUDED:
  707. case 0:
  708. //tf.setDoNotDownload(true);
  709. tf.setPriority(EXCLUDED);
  710. break;
  711. case ONLY_SEED_PRIORITY:
  712. case -1:
  713. tf.setPriority(ONLY_SEED_PRIORITY);
  714. break;
  715. default:
  716. tf.setPriority(LAST_PRIORITY);
  717. break;
  718. }
  719. }
  720. }
  721. }
  722. void ChunkManager::downloadStatusChanged(TorrentFile* tf,bool download)
  723. {
  724. Uint32 first = tf->getFirstChunk();
  725. Uint32 last = tf->getLastChunk();
  726. if (download)
  727. {
  728. // include the chunks
  729. include(first,last);
  730. // if it is a multimedia file, prioritise first and last chunks of file
  731. if (tf->isMultimedia())
  732. {
  733. Uint32 chunkOffset;
  734. chunkOffset = ((last - first) / 100) + 1;
  735. prioritise(first,first+chunkOffset,PREVIEW_PRIORITY);
  736. if (last - first > 2)
  737. {
  738. prioritise(last - chunkOffset, last, PREVIEW_PRIORITY);
  739. //prioritise(last -1,last, PREVIEW_PRIORITY);
  740. }
  741. }
  742. }
  743. else
  744. {
  745. // Out(SYS_DIO|LOG_DEBUG) << "Excluding chunks " << first << " to " << last << endl;
  746. // first and last chunk may be part of multiple files
  747. // so we can't just exclude them
  748. TQValueList<Uint32> files,last_files;
  749. // get list of files where first chunk lies in
  750. tor.calcChunkPos(first,files);
  751. tor.calcChunkPos(last,last_files);
  752. // check for exceptional case which causes very long loops
  753. if (first == last && files.count() > 1)
  754. {
  755. cache->downloadStatusChanged(tf,download);
  756. savePriorityInfo();
  757. return;
  758. }
  759. // go over all chunks from first to last and mark them as not downloaded
  760. // (first and last not included)
  761. for (Uint32 i = first + 1;i < last;i++)
  762. resetChunk(i);
  763. // if the first chunk only lies in one file, reset it
  764. if (files.count() == 1 && first != 0)
  765. {
  766. // Out(SYS_DIO|LOG_DEBUG) << "Resetting first " << first << endl;
  767. resetChunk(first);
  768. }
  769. // if the last chunk only lies in one file reset it
  770. if (last != first && last_files.count() == 1)
  771. {
  772. // Out(SYS_DIO|LOG_DEBUG) << "Resetting last " << last << endl;
  773. resetChunk(last);
  774. }
  775. Priority maxp = ONLY_SEED_PRIORITY;
  776. bool reprioritise_border_chunk = false;
  777. bool modified = false;
  778. // if one file in the list needs to be downloaded,increment first
  779. for (TQValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
  780. {
  781. if (*i == tf->getIndex())
  782. continue;
  783. const TorrentFile & other = tor.getFile(*i);
  784. if (!other.doNotDownload())
  785. {
  786. if (first != last && !modified)
  787. {
  788. first++;
  789. reprioritise_border_chunk = true;
  790. modified = true;
  791. }
  792. if (other.getPriority() > maxp)
  793. maxp = other.getPriority();
  794. }
  795. }
  796. // in case we have incremented first, we better reprioritise the border chunk
  797. if (reprioritise_border_chunk)
  798. prioritise(first-1,first-1,maxp);
  799. maxp = ONLY_SEED_PRIORITY;
  800. reprioritise_border_chunk = false;
  801. modified = false;
  802. // if one file in the list needs to be downloaded,decrement last
  803. for (TQValueList<Uint32>::iterator i = last_files.begin();i != last_files.end();i++)
  804. {
  805. if (*i == tf->getIndex())
  806. continue;
  807. const TorrentFile & other = tor.getFile(*i);
  808. if (!other.doNotDownload())
  809. {
  810. if (first != last && last > 0 && !modified)
  811. {
  812. last--;
  813. reprioritise_border_chunk = true;
  814. modified = true;
  815. }
  816. if (other.getPriority() > maxp)
  817. maxp = other.getPriority();
  818. }
  819. }
  820. if (reprioritise_border_chunk)
  821. prioritise(last+1,last+1,maxp);
  822. // last smaller then first is not normal, so just return
  823. if (last < first)
  824. {
  825. cache->downloadStatusChanged(tf,download);
  826. savePriorityInfo();
  827. return;
  828. }
  829. // Out(SYS_DIO|LOG_DEBUG) << "exclude " << first << " to " << last << endl;
  830. exclude(first,last);
  831. }
  832. // alert the cache but first put things in critical operation mode
  833. cache->downloadStatusChanged(tf,download);
  834. savePriorityInfo();
  835. }
  836. void ChunkManager::downloadPriorityChanged(TorrentFile* tf,Priority newpriority,Priority oldpriority)
  837. {
  838. if (newpriority == EXCLUDED)
  839. {
  840. downloadStatusChanged(tf, false);
  841. return;
  842. }
  843. if (oldpriority == EXCLUDED)
  844. {
  845. downloadStatusChanged(tf, true);
  846. return;
  847. }
  848. savePriorityInfo();
  849. Uint32 first = tf->getFirstChunk();
  850. Uint32 last = tf->getLastChunk();
  851. // first and last chunk may be part of multiple files
  852. // so we can't just exclude them
  853. TQValueList<Uint32> files;
  854. // get list of files where first chunk lies in
  855. tor.calcChunkPos(first,files);
  856. Chunk* c = chunks[first];
  857. // if one file in the list needs to be downloaded,increment first
  858. for (TQValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
  859. {
  860. Priority np = tor.getFile(*i).getPriority();
  861. if (np > newpriority && *i != tf->getIndex())
  862. {
  863. // make sure we don't go past last
  864. if (first == last)
  865. return;
  866. first++;
  867. break;
  868. }
  869. }
  870. files.clear();
  871. // get list of files where last chunk lies in
  872. tor.calcChunkPos(last,files);
  873. c = chunks[last];
  874. // if one file in the list needs to be downloaded,decrement last
  875. for (TQValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
  876. {
  877. Priority np = tor.getFile(*i).getPriority();
  878. if (np > newpriority && *i != tf->getIndex())
  879. {
  880. // make sure we don't wrap around
  881. if (last == 0 || last == first)
  882. return;
  883. last--;
  884. break;
  885. }
  886. }
  887. // last smaller then first is not normal, so just return
  888. if (last < first)
  889. {
  890. return;
  891. }
  892. prioritise(first,last,newpriority);
  893. if (newpriority == ONLY_SEED_PRIORITY)
  894. excluded(first,last);
  895. }
  896. bool ChunkManager::prepareChunk(Chunk* c,bool allways)
  897. {
  898. if (!allways && c->getStatus() != Chunk::NOT_DOWNLOADED)
  899. return false;
  900. return cache->prep(c);
  901. }
  902. TQString ChunkManager::getOutputPath() const
  903. {
  904. return cache->getOutputPath();
  905. }
  906. void ChunkManager::preallocateDiskSpace(PreallocationThread* prealloc)
  907. {
  908. cache->preallocateDiskSpace(prealloc);
  909. }
  910. void ChunkManager::dataChecked(const BitSet & ok_chunks)
  911. {
  912. // go over all chunks at check each of them
  913. for (Uint32 i = 0;i < chunks.count();i++)
  914. {
  915. Chunk* c = chunks[i];
  916. if (ok_chunks.get(i) && !bitset.get(i))
  917. {
  918. // We think we do not hae a chunk, but we do have it
  919. bitset.set(i,true);
  920. todo.set(i,false);
  921. // the chunk must be on disk
  922. c->setStatus(Chunk::ON_DISK);
  923. tor.updateFilePercentage(i,bitset);
  924. }
  925. else if (!ok_chunks.get(i) && bitset.get(i))
  926. {
  927. Out(SYS_DIO|LOG_IMPORTANT) << "Previously OK chunk " << i << " is corrupt !!!!!" << endl;
  928. // We think we have a chunk, but we don't
  929. bitset.set(i,false);
  930. todo.set(i,!only_seed_chunks.get(i) && !excluded_chunks.get(i));
  931. if (c->getStatus() == Chunk::ON_DISK)
  932. {
  933. c->setStatus(Chunk::NOT_DOWNLOADED);
  934. tor.updateFilePercentage(i,bitset);
  935. }
  936. else if (c->getStatus() == Chunk::MMAPPED || c->getStatus() == Chunk::BUFFERED)
  937. {
  938. resetChunk(i);
  939. }
  940. else
  941. {
  942. tor.updateFilePercentage(i,bitset);
  943. }
  944. }
  945. }
  946. recalc_chunks_left = true;
  947. try
  948. {
  949. saveIndexFile();
  950. }
  951. catch (bt::Error & err)
  952. {
  953. Out(SYS_DIO|LOG_DEBUG) << "Failed to save index file : " << err.toString() << endl;
  954. }
  955. catch (...)
  956. {
  957. Out(SYS_DIO|LOG_DEBUG) << "Failed to save index file : unkown exception" << endl;
  958. }
  959. chunksLeft();
  960. corrupted_count = 0;
  961. }
  962. bool ChunkManager::hasExistingFiles() const
  963. {
  964. return cache->hasExistingFiles();
  965. }
  966. void ChunkManager::recreateMissingFiles()
  967. {
  968. createFiles();
  969. if (tor.isMultiFile())
  970. {
  971. // loop over all files and mark all chunks of all missing files as
  972. // not downloaded
  973. for (Uint32 i = 0;i < tor.getNumFiles();i++)
  974. {
  975. TorrentFile & tf = tor.getFile(i);
  976. if (!tf.isMissing())
  977. continue;
  978. for (Uint32 j = tf.getFirstChunk(); j <= tf.getLastChunk();j++)
  979. resetChunk(j);
  980. tf.setMissing(false);
  981. }
  982. }
  983. else
  984. {
  985. // reset all chunks in case of single file torrent
  986. for (Uint32 j = 0; j < tor.getNumChunks();j++)
  987. resetChunk(j);
  988. }
  989. saveIndexFile();
  990. recalc_chunks_left = true;
  991. chunksLeft();
  992. }
  993. void ChunkManager::dndMissingFiles()
  994. {
  995. // createFiles(); // create them again
  996. // loop over all files and mark all chunks of all missing files as
  997. // not downloaded
  998. for (Uint32 i = 0;i < tor.getNumFiles();i++)
  999. {
  1000. TorrentFile & tf = tor.getFile(i);
  1001. if (!tf.isMissing())
  1002. continue;
  1003. for (Uint32 j = tf.getFirstChunk(); j <= tf.getLastChunk();j++)
  1004. resetChunk(j);
  1005. tf.setMissing(false);
  1006. tf.setDoNotDownload(true); // set do not download
  1007. }
  1008. savePriorityInfo();
  1009. saveIndexFile();
  1010. recalc_chunks_left = true;
  1011. chunksLeft();
  1012. }
  1013. void ChunkManager::deleteDataFiles()
  1014. {
  1015. cache->deleteDataFiles();
  1016. }
  1017. Uint64 ChunkManager::diskUsage()
  1018. {
  1019. return cache->diskUsage();
  1020. }
  1021. }
  1022. #include "chunkmanager.moc"