JSBigString.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #include "JSBigString.h"
  8. #include <glog/logging.h>
  9. #include <folly/Memory.h>
  10. #include <folly/ScopeGuard.h>
  11. #include <folly/portability/Fcntl.h>
  12. #include <folly/portability/SysMman.h>
  13. #include <folly/portability/SysStat.h>
  14. #include <folly/portability/Unistd.h>
  15. #include <memory>
  16. namespace facebook {
  17. namespace react {
  18. JSBigFileString::JSBigFileString(int fd, size_t size, off_t offset /*= 0*/)
  19. : m_fd{-1}, m_data{nullptr} {
  20. folly::checkUnixError(m_fd = dup(fd), "Could not duplicate file descriptor");
  21. // Offsets given to mmap must be page aligned. We abstract away that
  22. // restriction by sending a page aligned offset to mmap, and keeping track
  23. // of the offset within the page that we must alter the mmap pointer by to
  24. // get the final desired offset.
  25. if (offset != 0) {
  26. const static auto ps = sysconf(_SC_PAGESIZE);
  27. auto d = lldiv(offset, ps);
  28. m_mapOff = d.quot;
  29. m_pageOff = d.rem;
  30. m_size = size + m_pageOff;
  31. } else {
  32. m_mapOff = 0;
  33. m_pageOff = 0;
  34. m_size = size;
  35. }
  36. }
  37. JSBigFileString::~JSBigFileString() {
  38. if (m_data) {
  39. munmap((void *)m_data, m_size);
  40. }
  41. close(m_fd);
  42. }
  43. #ifdef WITH_FBREMAP
  44. // Read and advance the pointer.
  45. static uint16_t read16(char *&data) {
  46. uint16_t result;
  47. ::memcpy(&result, data, sizeof(result));
  48. data += sizeof(result);
  49. return result;
  50. }
  51. // If the given file has a remapping table header, remap its pages accordingly
  52. // and return the byte offset from the beginning to the unwrapped payload.
  53. static off_t maybeRemap(char *data, size_t size, int fd) {
  54. // A remapped file's size must be a multiple of its page size, so quickly
  55. // filter out files with incorrect size, without touching any pages.
  56. static const size_t kMinPageSize = 4096;
  57. if (size < kMinPageSize || size % kMinPageSize != 0) {
  58. return 0;
  59. }
  60. const auto begin = data;
  61. static const uint8_t kRemapMagic[] = {
  62. 0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f, 0xa1, 0xd0, 0xeb, 0x73};
  63. if (::memcmp(data, kRemapMagic, sizeof(kRemapMagic)) != 0) {
  64. return 0;
  65. }
  66. data += sizeof(kRemapMagic);
  67. const size_t filePS = static_cast<size_t>(1) << read16(data);
  68. if (size & (filePS - 1)) {
  69. return 0;
  70. }
  71. {
  72. // System page size must be at least as granular as the remapping.
  73. // TODO: Consider fallback that reads entire file into memory.
  74. const size_t systemPS = sysconf(_SC_PAGESIZE);
  75. CHECK(filePS >= systemPS)
  76. << "filePS: " << filePS << "systemPS: " << systemPS;
  77. }
  78. const off_t headerPages = read16(data);
  79. uint16_t numMappings = read16(data);
  80. size_t curFilePage = headerPages;
  81. while (numMappings--) {
  82. auto memPage = read16(data) + headerPages;
  83. auto numPages = read16(data);
  84. if (mmap(
  85. begin + memPage * filePS,
  86. numPages * filePS,
  87. PROT_READ,
  88. MAP_FILE | MAP_PRIVATE | MAP_FIXED,
  89. fd,
  90. curFilePage * filePS) == MAP_FAILED) {
  91. CHECK(false) << " memPage: " << memPage << " numPages: " << numPages
  92. << " curFilePage: " << curFilePage << " size: " << size
  93. << " error: " << std::strerror(errno);
  94. }
  95. curFilePage += numPages;
  96. }
  97. return headerPages * filePS;
  98. }
  99. #endif // WITH_FBREMAP
  100. const char *JSBigFileString::c_str() const {
  101. if (m_size == 0) {
  102. return "";
  103. }
  104. if (!m_data) {
  105. m_data =
  106. (const char *)mmap(0, m_size, PROT_READ, MAP_PRIVATE, m_fd, m_mapOff);
  107. CHECK(m_data != MAP_FAILED)
  108. << " fd: " << m_fd << " size: " << m_size << " offset: " << m_mapOff
  109. << " error: " << std::strerror(errno);
  110. #ifdef WITH_FBREMAP
  111. // Remapping is only attempted when the entire file was requested.
  112. if (m_mapOff == 0 && m_pageOff == 0) {
  113. m_pageOff = maybeRemap(const_cast<char *>(m_data), m_size, m_fd);
  114. }
  115. #endif // WITH_FBREMAP
  116. }
  117. return m_data + m_pageOff;
  118. }
  119. size_t JSBigFileString::size() const {
  120. // Ensure mapping has been initialized.
  121. c_str();
  122. return m_size - m_pageOff;
  123. }
  124. int JSBigFileString::fd() const {
  125. return m_fd;
  126. }
  127. std::unique_ptr<const JSBigFileString> JSBigFileString::fromPath(
  128. const std::string &sourceURL) {
  129. int fd = ::open(sourceURL.c_str(), O_RDONLY);
  130. folly::checkUnixError(fd, "Could not open file", sourceURL);
  131. SCOPE_EXIT {
  132. CHECK(::close(fd) == 0);
  133. };
  134. struct stat fileInfo;
  135. folly::checkUnixError(::fstat(fd, &fileInfo), "fstat on bundle failed.");
  136. return std::make_unique<const JSBigFileString>(fd, fileInfo.st_size);
  137. }
  138. } // namespace react
  139. } // namespace facebook