FileUtils.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include <errno.h>
  6. #include <stdio.h>
  7. #include "nscore.h"
  8. #include "nsStringGlue.h"
  9. #include "private/pprio.h"
  10. #include "mozilla/Assertions.h"
  11. #include "mozilla/FileUtils.h"
  12. #if defined(XP_UNIX)
  13. #include <fcntl.h>
  14. #include <unistd.h>
  15. #if defined(LINUX)
  16. #include <elf.h>
  17. #endif
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #elif defined(XP_WIN)
  21. #include <windows.h>
  22. #endif
  23. // Functions that are not to be used in standalone glue must be implemented
  24. // within this #if block
  25. #if !defined(XPCOM_GLUE)
  26. bool
  27. mozilla::fallocate(PRFileDesc* aFD, int64_t aLength)
  28. {
  29. #if defined(HAVE_POSIX_FALLOCATE)
  30. return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
  31. #elif defined(XP_WIN)
  32. int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
  33. if (oldpos == -1) {
  34. return false;
  35. }
  36. if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
  37. return false;
  38. }
  39. bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
  40. PR_Seek64(aFD, oldpos, PR_SEEK_SET);
  41. return retval;
  42. #elif defined(XP_UNIX)
  43. // The following is copied from fcntlSizeHint in sqlite
  44. /* If the OS does not have posix_fallocate(), fake it. First use
  45. ** ftruncate() to set the file size, then write a single byte to
  46. ** the last byte in each block within the extended region. This
  47. ** is the same technique used by glibc to implement posix_fallocate()
  48. ** on systems that do not have a real fallocate() system call.
  49. */
  50. int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
  51. if (oldpos == -1) {
  52. return false;
  53. }
  54. struct stat buf;
  55. int fd = PR_FileDesc2NativeHandle(aFD);
  56. if (fstat(fd, &buf)) {
  57. return false;
  58. }
  59. if (buf.st_size >= aLength) {
  60. return false;
  61. }
  62. const int nBlk = buf.st_blksize;
  63. if (!nBlk) {
  64. return false;
  65. }
  66. if (ftruncate(fd, aLength)) {
  67. return false;
  68. }
  69. int nWrite; // Return value from write()
  70. int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - 1; // Next offset to write to
  71. while (iWrite < aLength) {
  72. nWrite = 0;
  73. if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
  74. nWrite = PR_Write(aFD, "", 1);
  75. }
  76. if (nWrite != 1) {
  77. break;
  78. }
  79. iWrite += nBlk;
  80. }
  81. PR_Seek64(aFD, oldpos, PR_SEEK_SET);
  82. return nWrite == 1;
  83. #endif
  84. return false;
  85. }
  86. #ifdef ReadSysFile_PRESENT
  87. bool
  88. mozilla::ReadSysFile(
  89. const char* aFilename,
  90. char* aBuf,
  91. size_t aBufSize)
  92. {
  93. int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_RDONLY));
  94. if (fd < 0) {
  95. return false;
  96. }
  97. ScopedClose autoClose(fd);
  98. if (aBufSize == 0) {
  99. return true;
  100. }
  101. ssize_t bytesRead;
  102. size_t offset = 0;
  103. do {
  104. bytesRead = MOZ_TEMP_FAILURE_RETRY(read(fd, aBuf + offset,
  105. aBufSize - offset));
  106. if (bytesRead == -1) {
  107. return false;
  108. }
  109. offset += bytesRead;
  110. } while (bytesRead > 0 && offset < aBufSize);
  111. MOZ_ASSERT(offset <= aBufSize);
  112. if (offset > 0 && aBuf[offset - 1] == '\n') {
  113. offset--;
  114. }
  115. if (offset == aBufSize) {
  116. MOZ_ASSERT(offset > 0);
  117. offset--;
  118. }
  119. aBuf[offset] = '\0';
  120. return true;
  121. }
  122. bool
  123. mozilla::ReadSysFile(
  124. const char* aFilename,
  125. int* aVal)
  126. {
  127. char valBuf[32];
  128. if (!ReadSysFile(aFilename, valBuf, sizeof(valBuf))) {
  129. return false;
  130. }
  131. return sscanf(valBuf, "%d", aVal) == 1;
  132. }
  133. bool
  134. mozilla::ReadSysFile(
  135. const char* aFilename,
  136. bool* aVal)
  137. {
  138. int v;
  139. if (!ReadSysFile(aFilename, &v)) {
  140. return false;
  141. }
  142. *aVal = (v != 0);
  143. return true;
  144. }
  145. #endif /* ReadSysFile_PRESENT */
  146. #ifdef WriteSysFile_PRESENT
  147. bool
  148. mozilla::WriteSysFile(
  149. const char* aFilename,
  150. const char* aBuf)
  151. {
  152. size_t aBufSize = strlen(aBuf);
  153. int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_WRONLY));
  154. if (fd < 0) {
  155. return false;
  156. }
  157. ScopedClose autoClose(fd);
  158. ssize_t bytesWritten;
  159. size_t offset = 0;
  160. do {
  161. bytesWritten = MOZ_TEMP_FAILURE_RETRY(write(fd, aBuf + offset,
  162. aBufSize - offset));
  163. if (bytesWritten == -1) {
  164. return false;
  165. }
  166. offset += bytesWritten;
  167. } while (bytesWritten > 0 && offset < aBufSize);
  168. MOZ_ASSERT(offset == aBufSize);
  169. return true;
  170. }
  171. #endif /* WriteSysFile_PRESENT */
  172. void
  173. mozilla::ReadAheadLib(nsIFile* aFile)
  174. {
  175. #if defined(XP_WIN)
  176. nsAutoString path;
  177. if (!aFile || NS_FAILED(aFile->GetPath(path))) {
  178. return;
  179. }
  180. ReadAheadLib(path.get());
  181. #elif defined(LINUX)
  182. nsAutoCString nativePath;
  183. if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
  184. return;
  185. }
  186. ReadAheadLib(nativePath.get());
  187. #endif
  188. }
  189. void
  190. mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
  191. const size_t aCount, mozilla::filedesc_t* aOutFd)
  192. {
  193. #if defined(XP_WIN)
  194. nsAutoString path;
  195. if (!aFile || NS_FAILED(aFile->GetPath(path))) {
  196. return;
  197. }
  198. ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
  199. #elif defined(LINUX)
  200. nsAutoCString nativePath;
  201. if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
  202. return;
  203. }
  204. ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
  205. #endif
  206. }
  207. #endif // !defined(XPCOM_GLUE)
  208. #if defined(LINUX)
  209. static const unsigned int bufsize = 4096;
  210. #ifdef __LP64__
  211. typedef Elf64_Ehdr Elf_Ehdr;
  212. typedef Elf64_Phdr Elf_Phdr;
  213. static const unsigned char ELFCLASS = ELFCLASS64;
  214. typedef Elf64_Off Elf_Off;
  215. #else
  216. typedef Elf32_Ehdr Elf_Ehdr;
  217. typedef Elf32_Phdr Elf_Phdr;
  218. static const unsigned char ELFCLASS = ELFCLASS32;
  219. typedef Elf32_Off Elf_Off;
  220. #endif
  221. #endif
  222. void
  223. mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
  224. const size_t aCount)
  225. {
  226. #if defined(XP_WIN)
  227. LARGE_INTEGER fpOriginal;
  228. LARGE_INTEGER fpOffset;
  229. #if defined(HAVE_LONG_LONG)
  230. fpOffset.QuadPart = 0;
  231. #else
  232. fpOffset.u.LowPart = 0;
  233. fpOffset.u.HighPart = 0;
  234. #endif
  235. // Get the current file pointer so that we can restore it. This isn't
  236. // really necessary other than to provide the same semantics regarding the
  237. // file pointer that other platforms do
  238. if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
  239. return;
  240. }
  241. if (aOffset) {
  242. #if defined(HAVE_LONG_LONG)
  243. fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
  244. #else
  245. fpOffset.u.LowPart = aOffset;
  246. fpOffset.u.HighPart = 0;
  247. #endif
  248. if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
  249. return;
  250. }
  251. }
  252. char buf[64 * 1024];
  253. size_t totalBytesRead = 0;
  254. DWORD dwBytesRead;
  255. // Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN.
  256. // Abort when underfilling because during testing the buffers are read fully
  257. // A buffer that's not keeping up would imply that readahead isn't working right
  258. while (totalBytesRead < aCount &&
  259. ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
  260. dwBytesRead == sizeof(buf)) {
  261. totalBytesRead += dwBytesRead;
  262. }
  263. // Restore the file pointer
  264. SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
  265. #elif defined(LINUX)
  266. readahead(aFd, aOffset, aCount);
  267. #endif
  268. }
  269. void
  270. mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath)
  271. {
  272. if (!aFilePath) {
  273. return;
  274. }
  275. #if defined(XP_WIN)
  276. ReadAheadFile(aFilePath);
  277. #elif defined(LINUX)
  278. int fd = open(aFilePath, O_RDONLY);
  279. if (fd < 0) {
  280. return;
  281. }
  282. union
  283. {
  284. char buf[bufsize];
  285. Elf_Ehdr ehdr;
  286. } elf;
  287. // Read ELF header (ehdr) and program header table (phdr).
  288. // We check that the ELF magic is found, that the ELF class matches
  289. // our own, and that the program header table as defined in the ELF
  290. // headers fits in the buffer we read.
  291. if ((read(fd, elf.buf, bufsize) <= 0) ||
  292. (memcmp(elf.buf, ELFMAG, 4)) ||
  293. (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
  294. // Upcast e_phentsize so the multiplication is done in the same precision
  295. // as the subsequent addition, to satisfy static analyzers and avoid
  296. // issues with abnormally large program header tables.
  297. (elf.ehdr.e_phoff + (static_cast<Elf_Off>(elf.ehdr.e_phentsize) *
  298. elf.ehdr.e_phnum) >= bufsize)) {
  299. close(fd);
  300. return;
  301. }
  302. // The program header table contains segment definitions. One such
  303. // segment type is PT_LOAD, which describes how the dynamic loader
  304. // is going to map the file in memory. We use that information to
  305. // find the biggest offset from the library that will be mapped in
  306. // memory.
  307. Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
  308. Elf_Off end = 0;
  309. for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
  310. if ((phdr->p_type == PT_LOAD) &&
  311. (end < phdr->p_offset + phdr->p_filesz)) {
  312. end = phdr->p_offset + phdr->p_filesz;
  313. }
  314. }
  315. // Let the kernel read ahead what the dynamic loader is going to
  316. // map in memory soon after.
  317. if (end > 0) {
  318. ReadAhead(fd, 0, end);
  319. }
  320. close(fd);
  321. #endif
  322. }
  323. void
  324. mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
  325. const size_t aCount, mozilla::filedesc_t* aOutFd)
  326. {
  327. #if defined(XP_WIN)
  328. if (!aFilePath) {
  329. if (aOutFd) {
  330. *aOutFd = INVALID_HANDLE_VALUE;
  331. }
  332. return;
  333. }
  334. HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
  335. OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
  336. if (aOutFd) {
  337. *aOutFd = fd;
  338. }
  339. if (fd == INVALID_HANDLE_VALUE) {
  340. return;
  341. }
  342. ReadAhead(fd, aOffset, aCount);
  343. if (!aOutFd) {
  344. CloseHandle(fd);
  345. }
  346. #elif defined(LINUX) || defined(XP_SOLARIS)
  347. if (!aFilePath) {
  348. if (aOutFd) {
  349. *aOutFd = -1;
  350. }
  351. return;
  352. }
  353. int fd = open(aFilePath, O_RDONLY);
  354. if (aOutFd) {
  355. *aOutFd = fd;
  356. }
  357. if (fd < 0) {
  358. return;
  359. }
  360. size_t count;
  361. if (aCount == SIZE_MAX) {
  362. struct stat st;
  363. if (fstat(fd, &st) < 0) {
  364. if (!aOutFd) {
  365. close(fd);
  366. }
  367. return;
  368. }
  369. count = st.st_size;
  370. } else {
  371. count = aCount;
  372. }
  373. ReadAhead(fd, aOffset, count);
  374. if (!aOutFd) {
  375. close(fd);
  376. }
  377. #endif
  378. }