MultiDiskAdaptor.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /* <!-- copyright */
  2. /*
  3. * aria2 - The high speed download utility
  4. *
  5. * Copyright (C) 2006 Tatsuhiro Tsujikawa
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * In addition, as a special exception, the copyright holders give
  22. * permission to link the code of portions of this program with the
  23. * OpenSSL library under certain conditions as described in each
  24. * individual source file, and distribute linked combinations
  25. * including the two.
  26. * You must obey the GNU General Public License in all respects
  27. * for all of the code used other than OpenSSL. If you modify
  28. * file(s) with this exception, you may extend this exception to your
  29. * version of the file(s), but you are not obligated to do so. If you
  30. * do not wish to do so, delete this exception statement from your
  31. * version. If you delete this exception statement from all source
  32. * files in the program, then also delete it here.
  33. */
  34. /* copyright --> */
  35. #include "MultiDiskAdaptor.h"
  36. #include <cassert>
  37. #include <algorithm>
  38. #include <map>
  39. #include "DefaultDiskWriter.h"
  40. #include "message.h"
  41. #include "util.h"
  42. #include "FileEntry.h"
  43. #include "MultiFileAllocationIterator.h"
  44. #include "DefaultDiskWriterFactory.h"
  45. #include "DlAbortEx.h"
  46. #include "File.h"
  47. #include "fmt.h"
  48. #include "Logger.h"
  49. #include "LogFactory.h"
  50. #include "SimpleRandomizer.h"
  51. namespace aria2 {
  52. DiskWriterEntry::DiskWriterEntry(const SharedHandle<FileEntry>& fileEntry)
  53. : fileEntry_(fileEntry),
  54. open_(false),
  55. needsFileAllocation_(false)
  56. {}
  57. const std::string& DiskWriterEntry::getFilePath() const
  58. {
  59. return fileEntry_->getPath();
  60. }
  61. void DiskWriterEntry::initAndOpenFile()
  62. {
  63. if(diskWriter_) {
  64. diskWriter_->initAndOpenFile(fileEntry_->getLength());
  65. open_ = true;
  66. }
  67. }
  68. void DiskWriterEntry::openFile()
  69. {
  70. if(diskWriter_) {
  71. diskWriter_->openFile(fileEntry_->getLength());
  72. open_ = true;
  73. }
  74. }
  75. void DiskWriterEntry::openExistingFile()
  76. {
  77. if(diskWriter_) {
  78. diskWriter_->openExistingFile(fileEntry_->getLength());
  79. open_ = true;
  80. }
  81. }
  82. void DiskWriterEntry::closeFile()
  83. {
  84. if(open_) {
  85. diskWriter_->closeFile();
  86. open_ = false;
  87. }
  88. }
  89. bool DiskWriterEntry::fileExists()
  90. {
  91. return fileEntry_->exists();
  92. }
  93. int64_t DiskWriterEntry::size() const
  94. {
  95. return File(getFilePath()).size();
  96. }
  97. void DiskWriterEntry::setDiskWriter(const SharedHandle<DiskWriter>& diskWriter)
  98. {
  99. diskWriter_ = diskWriter;
  100. }
  101. bool DiskWriterEntry::operator<(const DiskWriterEntry& entry) const
  102. {
  103. return *fileEntry_ < *entry.fileEntry_;
  104. }
  105. MultiDiskAdaptor::MultiDiskAdaptor()
  106. : pieceLength_(0),
  107. maxOpenFiles_(DEFAULT_MAX_OPEN_FILES),
  108. readOnly_(false),
  109. enableMmap_(false)
  110. {}
  111. MultiDiskAdaptor::~MultiDiskAdaptor() {}
  112. namespace {
  113. SharedHandle<DiskWriterEntry> createDiskWriterEntry
  114. (const SharedHandle<FileEntry>& fileEntry,
  115. bool needsFileAllocation)
  116. {
  117. SharedHandle<DiskWriterEntry> entry(new DiskWriterEntry(fileEntry));
  118. entry->needsFileAllocation(needsFileAllocation);
  119. return entry;
  120. }
  121. } // namespace
  122. void MultiDiskAdaptor::resetDiskWriterEntries()
  123. {
  124. diskWriterEntries_.clear();
  125. if(getFileEntries().empty()) {
  126. return;
  127. }
  128. for(std::vector<SharedHandle<FileEntry> >::const_iterator i =
  129. getFileEntries().begin(), eoi = getFileEntries().end(); i != eoi; ++i) {
  130. diskWriterEntries_.push_back
  131. (createDiskWriterEntry(*i, (*i)->isRequested()));
  132. }
  133. std::map<std::string, bool> dwreq;
  134. // TODO Currently, pieceLength_ == 0 is used for unit testing only.
  135. if(pieceLength_ > 0) {
  136. std::vector<SharedHandle<DiskWriterEntry> >::const_iterator done =
  137. diskWriterEntries_.begin();
  138. for(std::vector<SharedHandle<DiskWriterEntry> >::const_iterator itr =
  139. diskWriterEntries_.begin(), eoi = diskWriterEntries_.end();
  140. itr != eoi;) {
  141. const SharedHandle<FileEntry>& fileEntry = (*itr)->getFileEntry();
  142. if(!fileEntry->isRequested()) {
  143. ++itr;
  144. continue;
  145. }
  146. int64_t pieceStartOffset =
  147. (fileEntry->getOffset()/pieceLength_)*pieceLength_;
  148. if(itr != diskWriterEntries_.begin()) {
  149. for(std::vector<SharedHandle<DiskWriterEntry> >::const_iterator i =
  150. itr-1; true; --i) {
  151. const SharedHandle<FileEntry>& fileEntry = (*i)->getFileEntry();
  152. if(pieceStartOffset <= fileEntry->getOffset() ||
  153. pieceStartOffset < fileEntry->getLastOffset()) {
  154. (*i)->needsFileAllocation(true);
  155. } else {
  156. break;
  157. }
  158. if(i == done) {
  159. break;
  160. }
  161. }
  162. }
  163. if(fileEntry->getLength() > 0) {
  164. int64_t lastPieceStartOffset =
  165. (fileEntry->getOffset()+fileEntry->getLength()-1)/
  166. pieceLength_*pieceLength_;
  167. A2_LOG_DEBUG(fmt("Checking adjacent backward file to %s"
  168. " whose lastPieceStartOffset+pieceLength_=%" PRId64 "",
  169. fileEntry->getPath().c_str(),
  170. static_cast<int64_t>
  171. (lastPieceStartOffset+pieceLength_)));
  172. ++itr;
  173. // adjacent backward files are not needed to be allocated. They
  174. // just requre DiskWriter
  175. for(; itr != eoi &&
  176. (!(*itr)->getFileEntry()->isRequested() ||
  177. (*itr)->getFileEntry()->getLength() == 0); ++itr) {
  178. A2_LOG_DEBUG
  179. (fmt("file=%s, offset=%" PRId64 "",
  180. (*itr)->getFileEntry()->getPath().c_str(),
  181. (*itr)->getFileEntry()->getOffset()));
  182. if((*itr)->getFileEntry()->getOffset() <
  183. lastPieceStartOffset+pieceLength_) {
  184. A2_LOG_DEBUG
  185. (fmt("%s needs diskwriter",
  186. (*itr)->getFileEntry()->getPath().c_str()));
  187. dwreq[(*itr)->getFileEntry()->getPath()] = true;
  188. } else {
  189. break;
  190. }
  191. }
  192. done = itr-1;
  193. } else {
  194. done = itr;
  195. ++itr;
  196. }
  197. }
  198. }
  199. DefaultDiskWriterFactory dwFactory;
  200. for(std::vector<SharedHandle<DiskWriterEntry> >::const_iterator i =
  201. diskWriterEntries_.begin(), eoi = diskWriterEntries_.end();
  202. i != eoi; ++i) {
  203. if((*i)->needsFileAllocation() ||
  204. dwreq.find((*i)->getFileEntry()->getPath()) != dwreq.end() ||
  205. (*i)->fileExists()) {
  206. A2_LOG_DEBUG(fmt("Creating DiskWriter for filename=%s",
  207. (*i)->getFilePath().c_str()));
  208. (*i)->setDiskWriter(dwFactory.newDiskWriter((*i)->getFilePath()));
  209. if(readOnly_) {
  210. (*i)->getDiskWriter()->enableReadOnly();
  211. }
  212. if(enableMmap_) {
  213. (*i)->getDiskWriter()->enableMmap();
  214. }
  215. }
  216. }
  217. }
  218. void MultiDiskAdaptor::openIfNot
  219. (const SharedHandle<DiskWriterEntry>& entry, void (DiskWriterEntry::*open)())
  220. {
  221. if(!entry->isOpen()) {
  222. // A2_LOG_DEBUG(fmt("DiskWriterEntry: Cache MISS. offset=%s",
  223. // util::itos(entry->getFileEntry()->getOffset()).c_str()));
  224. int numOpened = openedDiskWriterEntries_.size();
  225. (entry.get()->*open)();
  226. if(numOpened >= maxOpenFiles_) {
  227. // Cache is full.
  228. // Choose one DiskWriterEntry randomly and close it.
  229. size_t index =
  230. SimpleRandomizer::getInstance()->getRandomNumber(numOpened);
  231. std::vector<SharedHandle<DiskWriterEntry> >::iterator i =
  232. openedDiskWriterEntries_.begin();
  233. std::advance(i, index);
  234. (*i)->closeFile();
  235. (*i) = entry;
  236. } else {
  237. openedDiskWriterEntries_.push_back(entry);
  238. }
  239. } else {
  240. // A2_LOG_DEBUG(fmt("DiskWriterEntry: Cache HIT. offset=%s",
  241. // util::itos(entry->getFileEntry()->getOffset()).c_str()));
  242. }
  243. }
  244. void MultiDiskAdaptor::openFile()
  245. {
  246. resetDiskWriterEntries();
  247. // util::mkdir() is called in AbstractDiskWriter::createFile(), so
  248. // we don't need to call it here.
  249. // Call DiskWriterEntry::openFile to make sure that zero-length files are
  250. // created.
  251. for(DiskWriterEntries::const_iterator itr = diskWriterEntries_.begin(),
  252. eoi = diskWriterEntries_.end(); itr != eoi; ++itr) {
  253. openIfNot(*itr, &DiskWriterEntry::openFile);
  254. }
  255. }
  256. void MultiDiskAdaptor::initAndOpenFile()
  257. {
  258. resetDiskWriterEntries();
  259. // util::mkdir() is called in AbstractDiskWriter::createFile(), so
  260. // we don't need to call it here.
  261. // Call DiskWriterEntry::initAndOpenFile to make files truncated.
  262. for(DiskWriterEntries::const_iterator itr = diskWriterEntries_.begin(),
  263. eoi = diskWriterEntries_.end(); itr != eoi; ++itr) {
  264. openIfNot(*itr, &DiskWriterEntry::initAndOpenFile);
  265. }
  266. }
  267. void MultiDiskAdaptor::openExistingFile()
  268. {
  269. resetDiskWriterEntries();
  270. // Not need to call openIfNot here.
  271. }
  272. void MultiDiskAdaptor::closeFile()
  273. {
  274. std::for_each(diskWriterEntries_.begin(), diskWriterEntries_.end(),
  275. mem_fun_sh(&DiskWriterEntry::closeFile));
  276. }
  277. namespace {
  278. bool isInRange(const SharedHandle<DiskWriterEntry> entry, int64_t offset)
  279. {
  280. return entry->getFileEntry()->getOffset() <= offset &&
  281. offset < entry->getFileEntry()->getLastOffset();
  282. }
  283. } // namespace
  284. namespace {
  285. ssize_t calculateLength(const SharedHandle<DiskWriterEntry> entry,
  286. int64_t fileOffset, ssize_t rem)
  287. {
  288. if(entry->getFileEntry()->getLength() < fileOffset+rem) {
  289. return entry->getFileEntry()->getLength()-fileOffset;
  290. } else {
  291. return rem;
  292. }
  293. }
  294. } // namespace
  295. namespace {
  296. class OffsetCompare {
  297. public:
  298. bool operator()(int64_t offset, const SharedHandle<DiskWriterEntry>& dwe)
  299. {
  300. return offset < dwe->getFileEntry()->getOffset();
  301. }
  302. };
  303. } // namespace
  304. namespace {
  305. DiskWriterEntries::const_iterator findFirstDiskWriterEntry
  306. (const DiskWriterEntries& diskWriterEntries, int64_t offset)
  307. {
  308. DiskWriterEntries::const_iterator first =
  309. std::upper_bound(diskWriterEntries.begin(), diskWriterEntries.end(),
  310. offset, OffsetCompare());
  311. --first;
  312. // In case when offset is out-of-range
  313. if(!isInRange(*first, offset)) {
  314. throw DL_ABORT_EX
  315. (fmt(EX_FILE_OFFSET_OUT_OF_RANGE, static_cast<int64_t>(offset)));
  316. }
  317. return first;
  318. }
  319. } // namespace
  320. namespace {
  321. void throwOnDiskWriterNotOpened(const SharedHandle<DiskWriterEntry>& e,
  322. int64_t offset)
  323. {
  324. throw DL_ABORT_EX
  325. (fmt("DiskWriter for offset=%" PRId64 ", filename=%s is not opened.",
  326. static_cast<int64_t>(offset),
  327. e->getFilePath().c_str()));
  328. }
  329. } // namespace
  330. void MultiDiskAdaptor::writeData(const unsigned char* data, size_t len,
  331. int64_t offset)
  332. {
  333. DiskWriterEntries::const_iterator first =
  334. findFirstDiskWriterEntry(diskWriterEntries_, offset);
  335. ssize_t rem = len;
  336. int64_t fileOffset = offset-(*first)->getFileEntry()->getOffset();
  337. for(DiskWriterEntries::const_iterator i = first,
  338. eoi = diskWriterEntries_.end(); i != eoi; ++i) {
  339. ssize_t writeLength = calculateLength(*i, fileOffset, rem);
  340. openIfNot(*i, &DiskWriterEntry::openFile);
  341. if(!(*i)->isOpen()) {
  342. throwOnDiskWriterNotOpened(*i, offset+(len-rem));
  343. }
  344. (*i)->getDiskWriter()->writeData(data+(len-rem), writeLength, fileOffset);
  345. rem -= writeLength;
  346. fileOffset = 0;
  347. if(rem == 0) {
  348. break;
  349. }
  350. }
  351. }
  352. ssize_t MultiDiskAdaptor::readData
  353. (unsigned char* data, size_t len, int64_t offset)
  354. {
  355. DiskWriterEntries::const_iterator first =
  356. findFirstDiskWriterEntry(diskWriterEntries_, offset);
  357. ssize_t rem = len;
  358. ssize_t totalReadLength = 0;
  359. int64_t fileOffset = offset-(*first)->getFileEntry()->getOffset();
  360. for(DiskWriterEntries::const_iterator i = first,
  361. eoi = diskWriterEntries_.end(); i != eoi; ++i) {
  362. ssize_t readLength = calculateLength(*i, fileOffset, rem);
  363. openIfNot(*i, &DiskWriterEntry::openFile);
  364. if(!(*i)->isOpen()) {
  365. throwOnDiskWriterNotOpened(*i, offset+(len-rem));
  366. }
  367. totalReadLength +=
  368. (*i)->getDiskWriter()->readData(data+(len-rem), readLength, fileOffset);
  369. rem -= readLength;
  370. fileOffset = 0;
  371. if(rem == 0) {
  372. break;
  373. }
  374. }
  375. return totalReadLength;
  376. }
  377. bool MultiDiskAdaptor::fileExists()
  378. {
  379. return std::find_if(getFileEntries().begin(), getFileEntries().end(),
  380. mem_fun_sh(&FileEntry::exists)) !=
  381. getFileEntries().end();
  382. }
  383. int64_t MultiDiskAdaptor::size()
  384. {
  385. int64_t size = 0;
  386. for(std::vector<SharedHandle<FileEntry> >::const_iterator i =
  387. getFileEntries().begin(), eoi = getFileEntries().end(); i != eoi; ++i) {
  388. size += File((*i)->getPath()).size();
  389. }
  390. return size;
  391. }
  392. SharedHandle<FileAllocationIterator> MultiDiskAdaptor::fileAllocationIterator()
  393. {
  394. return SharedHandle<FileAllocationIterator>
  395. (new MultiFileAllocationIterator(this));
  396. }
  397. void MultiDiskAdaptor::enableReadOnly()
  398. {
  399. readOnly_ = true;
  400. }
  401. void MultiDiskAdaptor::disableReadOnly()
  402. {
  403. readOnly_ = false;
  404. }
  405. void MultiDiskAdaptor::enableMmap()
  406. {
  407. enableMmap_ = true;
  408. }
  409. void MultiDiskAdaptor::cutTrailingGarbage()
  410. {
  411. for(std::vector<SharedHandle<DiskWriterEntry> >::const_iterator i =
  412. diskWriterEntries_.begin(), eoi = diskWriterEntries_.end();
  413. i != eoi; ++i) {
  414. int64_t length = (*i)->getFileEntry()->getLength();
  415. if(File((*i)->getFilePath()).size() > length) {
  416. // We need open file before calling DiskWriter::truncate(int64_t)
  417. openIfNot(*i, &DiskWriterEntry::openFile);
  418. (*i)->getDiskWriter()->truncate(length);
  419. }
  420. }
  421. }
  422. void MultiDiskAdaptor::setMaxOpenFiles(int maxOpenFiles)
  423. {
  424. maxOpenFiles_ = maxOpenFiles;
  425. }
  426. size_t MultiDiskAdaptor::utime(const Time& actime, const Time& modtime)
  427. {
  428. size_t numOK = 0;
  429. for(std::vector<SharedHandle<FileEntry> >::const_iterator i =
  430. getFileEntries().begin(), eoi = getFileEntries().end(); i != eoi; ++i) {
  431. if((*i)->isRequested()) {
  432. File f((*i)->getPath());
  433. if(f.isFile() && f.utime(actime, modtime)) {
  434. ++numOK;
  435. }
  436. }
  437. }
  438. return numOK;
  439. }
  440. } // namespace aria2