AbstractDiskWriter.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  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 "AbstractDiskWriter.h"
  36. #include <unistd.h>
  37. #ifdef HAVE_MMAP
  38. # include <sys/mman.h>
  39. #endif // HAVE_MMAP
  40. #include <cerrno>
  41. #include <cstring>
  42. #include <cassert>
  43. #include "File.h"
  44. #include "util.h"
  45. #include "message.h"
  46. #include "DlAbortEx.h"
  47. #include "a2io.h"
  48. #include "fmt.h"
  49. #include "DownloadFailureException.h"
  50. #include "error_code.h"
  51. #include "LogFactory.h"
  52. namespace aria2 {
  53. AbstractDiskWriter::AbstractDiskWriter(const std::string& filename)
  54. : filename_(filename),
  55. fd_(A2_BAD_FD),
  56. #ifdef __MINGW32__
  57. mapView_(0),
  58. #else // !__MINGW32__
  59. #endif // !__MINGW32__
  60. readOnly_(false),
  61. enableMmap_(false),
  62. mapaddr_(0),
  63. maplen_(0)
  64. {}
  65. AbstractDiskWriter::~AbstractDiskWriter()
  66. {
  67. closeFile();
  68. }
  69. namespace {
  70. // Returns error code depending on the platform. For MinGW32, return
  71. // the value of GetLastError(). Otherwise, return errno.
  72. int fileError()
  73. {
  74. #ifdef __MINGW32__
  75. return GetLastError();
  76. #else // !__MINGW32__
  77. return errno;
  78. #endif // !__MINGW32__
  79. }
  80. } // namespace
  81. namespace {
  82. // Formats error message for error code errNum. For MinGW32, errNum is
  83. // assumed to be the return value of GetLastError(). Otherwise, it is
  84. // errno.
  85. std::string fileStrerror(int errNum)
  86. {
  87. #ifdef __MINGW32__
  88. static char buf[256];
  89. if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  90. 0,
  91. errNum,
  92. // Default language
  93. MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  94. (LPTSTR) &buf,
  95. sizeof(buf),
  96. 0) == 0) {
  97. snprintf(buf, sizeof(buf), "File I/O error %x", errNum);
  98. }
  99. return buf;
  100. #else // !__MINGW32__
  101. return util::safeStrerror(errNum);
  102. #endif // !__MINGW32__
  103. }
  104. } // namespace
  105. void AbstractDiskWriter::openFile(int64_t totalLength)
  106. {
  107. try {
  108. openExistingFile(totalLength);
  109. } catch(RecoverableException& e) {
  110. if(
  111. #ifdef __MINGW32__
  112. e.getErrNum() == ERROR_FILE_NOT_FOUND ||
  113. e.getErrNum() == ERROR_PATH_NOT_FOUND
  114. #else // !__MINGW32__
  115. e.getErrNum() == ENOENT
  116. #endif // !__MINGW32__
  117. ) {
  118. initAndOpenFile(totalLength);
  119. } else {
  120. throw;
  121. }
  122. }
  123. }
  124. void AbstractDiskWriter::closeFile()
  125. {
  126. #if defined HAVE_MMAP || defined __MINGW32__
  127. if(mapaddr_) {
  128. int errNum = 0;
  129. #ifdef __MINGW32__
  130. if(!UnmapViewOfFile(mapaddr_)) {
  131. errNum = GetLastError();
  132. }
  133. CloseHandle(mapView_);
  134. mapView_ = INVALID_HANDLE_VALUE;
  135. #else // !__MINGW32__
  136. if(munmap(mapaddr_, maplen_) == -1) {
  137. errNum = errno;
  138. }
  139. #endif // !__MINGW32__
  140. if(errNum != 0) {
  141. int errNum = fileError();
  142. A2_LOG_ERROR(fmt("Unmapping file %s failed: %s",
  143. filename_.c_str(), fileStrerror(errNum).c_str()));
  144. } else {
  145. A2_LOG_INFO(fmt("Unmapping file %s succeeded", filename_.c_str()));
  146. }
  147. mapaddr_ = 0;
  148. maplen_ = 0;
  149. }
  150. #endif // HAVE_MMAP || defined __MINGW32__
  151. if(fd_ != A2_BAD_FD) {
  152. #ifdef __MINGW32__
  153. CloseHandle(fd_);
  154. #else // !__MINGW32__
  155. close(fd_);
  156. #endif // !__MINGW32__
  157. fd_ = A2_BAD_FD;
  158. }
  159. }
  160. namespace {
  161. #ifdef __MINGW32__
  162. HANDLE openFileWithFlags(const std::string& filename, int flags,
  163. error_code::Value errCode)
  164. {
  165. HANDLE hn;
  166. DWORD desiredAccess = 0;
  167. DWORD sharedMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  168. DWORD creationDisp = 0;
  169. if(flags & O_RDWR) {
  170. desiredAccess = GENERIC_READ | GENERIC_WRITE;
  171. } else if(flags & O_WRONLY) {
  172. desiredAccess = GENERIC_WRITE;
  173. } else {
  174. desiredAccess = GENERIC_READ;
  175. }
  176. if(flags & O_CREAT) {
  177. if(flags & O_TRUNC) {
  178. creationDisp |= CREATE_ALWAYS;
  179. } else {
  180. creationDisp |= CREATE_NEW;
  181. }
  182. } else {
  183. creationDisp |= OPEN_EXISTING;
  184. }
  185. hn = CreateFileW(utf8ToWChar(filename).c_str(), desiredAccess, sharedMode,
  186. /* lpSecurityAttributes */ 0, creationDisp,
  187. FILE_ATTRIBUTE_NORMAL, /* hTemplateFile */ 0);
  188. if(hn == INVALID_HANDLE_VALUE) {
  189. int errNum = GetLastError();
  190. throw DL_ABORT_EX3(errNum, fmt(EX_FILE_OPEN,
  191. filename.c_str(),
  192. fileStrerror(errNum).c_str()),
  193. errCode);
  194. }
  195. return hn;
  196. }
  197. #else // !__MINGW32__
  198. int openFileWithFlags(const std::string& filename, int flags,
  199. error_code::Value errCode)
  200. {
  201. int fd;
  202. while((fd = a2open(utf8ToWChar(filename).c_str(), flags, OPEN_MODE)) == -1
  203. && errno == EINTR);
  204. if(fd < 0) {
  205. int errNum = errno;
  206. throw DL_ABORT_EX3(errNum, fmt(EX_FILE_OPEN, filename.c_str(),
  207. util::safeStrerror(errNum).c_str()),
  208. errCode);
  209. }
  210. // This may reduce memory consumption on Mac OS X. Not tested.
  211. #if defined(__APPLE__) && defined(__MACH__)
  212. fcntl(fd, F_GLOBAL_NOCACHE, 1);
  213. #endif // __APPLE__ && __MACH__
  214. return fd;
  215. }
  216. #endif // !__MINGW32__
  217. } // namespace
  218. #ifdef __MINGW32__
  219. namespace {
  220. HANDLE getWin32Handle(int fd)
  221. {
  222. return reinterpret_cast<HANDLE>(_get_osfhandle(fd));
  223. }
  224. } // namespace
  225. #endif // __MINGW32__
  226. void AbstractDiskWriter::openExistingFile(int64_t totalLength)
  227. {
  228. int flags = O_BINARY;
  229. if(readOnly_) {
  230. flags |= O_RDONLY;
  231. } else {
  232. flags |= O_RDWR;
  233. }
  234. fd_ = openFileWithFlags(filename_, flags, error_code::FILE_OPEN_ERROR);
  235. }
  236. void AbstractDiskWriter::createFile(int addFlags)
  237. {
  238. assert(!filename_.empty());
  239. util::mkdirs(File(filename_).getDirname());
  240. fd_ = openFileWithFlags(filename_, O_CREAT|O_RDWR|O_TRUNC|O_BINARY|addFlags,
  241. error_code::FILE_CREATE_ERROR);
  242. }
  243. ssize_t AbstractDiskWriter::writeDataInternal(const unsigned char* data,
  244. size_t len, int64_t offset)
  245. {
  246. if(mapaddr_) {
  247. memcpy(mapaddr_ + offset, data, len);
  248. return len;
  249. } else {
  250. ssize_t writtenLength = 0;
  251. seek(offset);
  252. while((size_t)writtenLength < len) {
  253. #ifdef __MINGW32__
  254. DWORD nwrite;
  255. if(WriteFile(fd_, data+writtenLength, len-writtenLength, &nwrite, 0)) {
  256. writtenLength += nwrite;
  257. } else {
  258. return -1;
  259. }
  260. #else // !__MINGW32__
  261. ssize_t ret = 0;
  262. while((ret = write(fd_, data+writtenLength, len-writtenLength)) == -1 &&
  263. errno == EINTR);
  264. if(ret == -1) {
  265. return -1;
  266. }
  267. writtenLength += ret;
  268. #endif // !__MINGW32__
  269. }
  270. return writtenLength;
  271. }
  272. }
  273. ssize_t AbstractDiskWriter::readDataInternal(unsigned char* data, size_t len,
  274. int64_t offset)
  275. {
  276. if(mapaddr_) {
  277. ssize_t readlen;
  278. if(offset > maplen_) {
  279. readlen = 0;
  280. } else {
  281. readlen = std::min(static_cast<size_t>(maplen_ - offset), len);
  282. }
  283. memcpy(data, mapaddr_ + offset, readlen);
  284. return readlen;
  285. } else {
  286. seek(offset);
  287. #ifdef __MINGW32__
  288. DWORD nread;
  289. if(ReadFile(fd_, data, len, &nread, 0)) {
  290. return nread;
  291. } else {
  292. return -1;
  293. }
  294. #else // !__MINGW32__
  295. ssize_t ret = 0;
  296. while((ret = read(fd_, data, len)) == -1 && errno == EINTR);
  297. return ret;
  298. #endif // !__MINGW32__
  299. }
  300. }
  301. void AbstractDiskWriter::seek(int64_t offset)
  302. {
  303. #ifdef __MINGW32__
  304. LARGE_INTEGER fileLength;
  305. fileLength.QuadPart = offset;
  306. if(SetFilePointerEx(fd_, fileLength, 0, FILE_BEGIN) == 0)
  307. #else // !__MINGW32__
  308. if(a2lseek(fd_, offset, SEEK_SET) == (a2_off_t)-1)
  309. #endif // !__MINGW32__
  310. {
  311. int errNum = fileError();
  312. throw DL_ABORT_EX2(fmt(EX_FILE_SEEK, filename_.c_str(),
  313. fileStrerror(errNum).c_str()),
  314. error_code::FILE_IO_ERROR);
  315. }
  316. }
  317. void AbstractDiskWriter::ensureMmapWrite(size_t len, int64_t offset)
  318. {
  319. #if defined HAVE_MMAP || defined __MINGW32__
  320. if(enableMmap_) {
  321. if(mapaddr_) {
  322. if(static_cast<int64_t>(len + offset) > maplen_) {
  323. int errNum = 0;
  324. #ifdef __MINGW32__
  325. if(!UnmapViewOfFile(mapaddr_)) {
  326. errNum = GetLastError();
  327. }
  328. CloseHandle(mapView_);
  329. mapView_ = INVALID_HANDLE_VALUE;
  330. #else // !__MINGW32__
  331. if(munmap(mapaddr_, maplen_) == -1) {
  332. errNum = errno;
  333. }
  334. #endif // !__MINGW32__
  335. if(errNum != 0) {
  336. A2_LOG_ERROR(fmt("Unmapping file %s failed: %s",
  337. filename_.c_str(), fileStrerror(errNum).c_str()));
  338. }
  339. mapaddr_ = 0;
  340. maplen_ = 0;
  341. enableMmap_ = false;
  342. }
  343. } else {
  344. int64_t filesize = size();
  345. int errNum = 0;
  346. if(static_cast<int64_t>(len + offset) <= filesize) {
  347. #ifdef __MINGW32__
  348. mapView_ = CreateFileMapping(fd_, 0, PAGE_READWRITE,
  349. filesize >> 32, filesize & 0xffffffffu,
  350. 0);
  351. if(mapView_) {
  352. mapaddr_ = reinterpret_cast<unsigned char*>
  353. (MapViewOfFile(mapView_, FILE_MAP_WRITE, 0, 0, 0));
  354. if(!mapaddr_) {
  355. errNum = GetLastError();
  356. CloseHandle(mapView_);
  357. mapView_ = INVALID_HANDLE_VALUE;
  358. }
  359. } else {
  360. errNum = GetLastError();
  361. }
  362. #else // !__MINGW32__
  363. mapaddr_ = reinterpret_cast<unsigned char*>
  364. (mmap(0, size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0));
  365. if(!mapaddr_) {
  366. errNum = errno;
  367. }
  368. #endif // !__MINGW32__
  369. if(mapaddr_) {
  370. A2_LOG_DEBUG(fmt("Mapping file %s succeeded, length=%" PRId64 "",
  371. filename_.c_str(),
  372. static_cast<uint64_t>(filesize)));
  373. maplen_ = filesize;
  374. } else {
  375. A2_LOG_WARN(fmt("Mapping file %s failed: %s",
  376. filename_.c_str(), fileStrerror(errNum).c_str()));
  377. enableMmap_ = false;
  378. }
  379. }
  380. }
  381. }
  382. #endif // HAVE_MMAP || __MINGW32__
  383. }
  384. void AbstractDiskWriter::writeData(const unsigned char* data, size_t len, int64_t offset)
  385. {
  386. ensureMmapWrite(len, offset);
  387. if(writeDataInternal(data, len, offset) < 0) {
  388. int errNum = fileError();
  389. if(
  390. // If the error indicates disk full situation, throw
  391. // DownloadFailureException and abort download instantly.
  392. #ifdef __MINGW32__
  393. errNum == ERROR_DISK_FULL || errNum == ERROR_HANDLE_DISK_FULL
  394. #else // !__MINGW32__
  395. errNum == ENOSPC
  396. #endif // !__MINGW32__
  397. ) {
  398. throw DOWNLOAD_FAILURE_EXCEPTION3
  399. (errNum, fmt(EX_FILE_WRITE, filename_.c_str(),
  400. fileStrerror(errNum).c_str()),
  401. error_code::NOT_ENOUGH_DISK_SPACE);
  402. } else {
  403. throw DL_ABORT_EX3
  404. (errNum, fmt(EX_FILE_WRITE, filename_.c_str(),
  405. fileStrerror(errNum).c_str()),
  406. error_code::FILE_IO_ERROR);
  407. }
  408. }
  409. }
  410. ssize_t AbstractDiskWriter::readData(unsigned char* data, size_t len, int64_t offset)
  411. {
  412. ssize_t ret;
  413. if((ret = readDataInternal(data, len, offset)) < 0) {
  414. int errNum = fileError();
  415. throw DL_ABORT_EX3
  416. (errNum, fmt(EX_FILE_READ, filename_.c_str(),
  417. fileStrerror(errNum).c_str()),
  418. error_code::FILE_IO_ERROR);
  419. }
  420. return ret;
  421. }
  422. void AbstractDiskWriter::truncate(int64_t length)
  423. {
  424. if(fd_ == A2_BAD_FD) {
  425. throw DL_ABORT_EX("File not yet opened.");
  426. }
  427. #ifdef __MINGW32__
  428. // Since mingw32's ftruncate cannot handle over 2GB files, we use
  429. // SetEndOfFile instead.
  430. seek(length);
  431. if(SetEndOfFile(fd_) == 0)
  432. #else // !__MINGW32__
  433. if(a2ftruncate(fd_, length) == -1)
  434. #endif // !__MINGW32__
  435. {
  436. int errNum = fileError();
  437. throw DL_ABORT_EX2(fmt("File truncation failed. cause: %s",
  438. fileStrerror(errNum).c_str()),
  439. error_code::FILE_IO_ERROR);
  440. }
  441. }
  442. void AbstractDiskWriter::allocate(int64_t offset, int64_t length, bool sparse)
  443. {
  444. if(fd_ == A2_BAD_FD) {
  445. throw DL_ABORT_EX("File not yet opened.");
  446. }
  447. if(sparse) {
  448. #ifdef __MINGW32__
  449. DWORD bytesReturned;
  450. if(!DeviceIoControl(fd_, FSCTL_SET_SPARSE, 0, 0, 0, 0,
  451. &bytesReturned, 0)) {
  452. A2_LOG_WARN(fmt("Making file sparse failed or pending: %s",
  453. fileStrerror(GetLastError()).c_str()));
  454. }
  455. #endif // __MINGW32__
  456. truncate(offset+length);
  457. return;
  458. }
  459. #ifdef HAVE_SOME_FALLOCATE
  460. # ifdef __MINGW32__
  461. truncate(offset+length);
  462. # elif HAVE_FALLOCATE
  463. // For linux, we use fallocate to detect file system supports
  464. // fallocate or not.
  465. int r;
  466. while((r = fallocate(fd_, 0, offset, length)) == -1 && errno == EINTR);
  467. int errNum = errno;
  468. if(r == -1) {
  469. throw DL_ABORT_EX3(errNum,
  470. fmt("fallocate failed. cause: %s",
  471. util::safeStrerror(errNum).c_str()),
  472. error_code::FILE_IO_ERROR);
  473. }
  474. # elif HAVE_POSIX_FALLOCATE
  475. int r = posix_fallocate(fd_, offset, length);
  476. if(r != 0) {
  477. throw DL_ABORT_EX3(r,
  478. fmt("posix_fallocate failed. cause: %s",
  479. util::safeStrerror(r).c_str()),
  480. error_code::FILE_IO_ERROR);
  481. }
  482. # else
  483. # error "no *_fallocate function available."
  484. # endif
  485. #endif // HAVE_SOME_FALLOCATE
  486. }
  487. int64_t AbstractDiskWriter::size()
  488. {
  489. return File(filename_).size();
  490. }
  491. void AbstractDiskWriter::enableReadOnly()
  492. {
  493. readOnly_ = true;
  494. }
  495. void AbstractDiskWriter::disableReadOnly()
  496. {
  497. readOnly_ = false;
  498. }
  499. void AbstractDiskWriter::enableMmap()
  500. {
  501. enableMmap_ = true;
  502. }
  503. } // namespace aria2