Bug 1822171 - Update NDK to r26c. r=geckoview-reviewers,m_kato
[gecko.git] / xpcom / glue / FileUtils.cpp
blob537c060bf70d307df6d2b7254d661e4cec4930f7
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/FileUtils.h"
9 #include "nscore.h"
10 #include "private/pprio.h"
11 #include "prmem.h"
12 #include "mozilla/BaseProfilerMarkers.h"
13 #include "mozilla/MemUtils.h"
15 #if defined(XP_MACOSX)
16 # include <fcntl.h>
17 # include <unistd.h>
18 # include <mach/machine.h>
19 # include <mach-o/fat.h>
20 # include <mach-o/loader.h>
21 # include <sys/mman.h>
22 # include <sys/stat.h>
23 # include <limits.h>
24 #elif defined(XP_UNIX)
25 # include <fcntl.h>
26 # include <unistd.h>
27 # if defined(LINUX)
28 # include <elf.h>
29 # endif
30 # include <sys/types.h>
31 # include <sys/stat.h>
32 #elif defined(XP_WIN)
33 # include <nsWindowsHelpers.h>
34 # include <mozilla/NativeNt.h>
35 # include <mozilla/ScopeExit.h>
36 #endif
38 // Functions that are not to be used in standalone glue must be implemented
39 // within this #if block
40 #if defined(MOZILLA_INTERNAL_API)
42 # include "nsString.h"
44 bool mozilla::fallocate(PRFileDesc* aFD, int64_t aLength) {
45 # if defined(HAVE_POSIX_FALLOCATE)
46 return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
47 # elif defined(XP_WIN)
48 int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
49 if (oldpos == -1) {
50 return false;
53 if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
54 return false;
57 bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
59 PR_Seek64(aFD, oldpos, PR_SEEK_SET);
60 return retval;
61 # elif defined(XP_MACOSX)
62 int fd = PR_FileDesc2NativeHandle(aFD);
63 fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength};
64 // Try to get a continous chunk of disk space
65 int ret = fcntl(fd, F_PREALLOCATE, &store);
66 if (ret == -1) {
67 // OK, perhaps we are too fragmented, allocate non-continuous
68 store.fst_flags = F_ALLOCATEALL;
69 ret = fcntl(fd, F_PREALLOCATE, &store);
70 if (ret == -1) {
71 return false;
74 return ftruncate(fd, aLength) == 0;
75 # elif defined(XP_UNIX)
76 // The following is copied from fcntlSizeHint in sqlite
77 /* If the OS does not have posix_fallocate(), fake it. First use
78 ** ftruncate() to set the file size, then write a single byte to
79 ** the last byte in each block within the extended region. This
80 ** is the same technique used by glibc to implement posix_fallocate()
81 ** on systems that do not have a real fallocate() system call.
83 int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
84 if (oldpos == -1) {
85 return false;
88 struct stat buf;
89 int fd = PR_FileDesc2NativeHandle(aFD);
90 if (fstat(fd, &buf)) {
91 return false;
94 if (buf.st_size >= aLength) {
95 return false;
98 const int nBlk = buf.st_blksize;
100 if (!nBlk) {
101 return false;
104 if (ftruncate(fd, aLength)) {
105 return false;
108 int nWrite; // Return value from write()
109 int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk -
110 1; // Next offset to write to
111 while (iWrite < aLength) {
112 nWrite = 0;
113 if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
114 nWrite = PR_Write(aFD, "", 1);
116 if (nWrite != 1) {
117 break;
119 iWrite += nBlk;
122 PR_Seek64(aFD, oldpos, PR_SEEK_SET);
123 return nWrite == 1;
124 # else
125 return false;
126 # endif
129 void mozilla::ReadAheadLib(nsIFile* aFile) {
130 # if defined(XP_WIN)
131 nsAutoString path;
132 if (!aFile || NS_FAILED(aFile->GetPath(path))) {
133 return;
135 ReadAheadLib(path.get());
136 # elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
137 nsAutoCString nativePath;
138 if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
139 return;
141 ReadAheadLib(nativePath.get());
142 # endif
145 void mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
146 const size_t aCount, mozilla::filedesc_t* aOutFd) {
147 # if defined(XP_WIN)
148 nsAutoString path;
149 if (!aFile || NS_FAILED(aFile->GetPath(path))) {
150 return;
152 ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
153 # elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
154 nsAutoCString nativePath;
155 if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
156 return;
158 ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
159 # endif
162 mozilla::PathString mozilla::GetLibraryName(mozilla::pathstr_t aDirectory,
163 const char* aLib) {
164 # ifdef XP_WIN
165 nsAutoString fullName;
166 if (aDirectory) {
167 fullName.Assign(aDirectory);
168 fullName.Append('\\');
170 AppendUTF8toUTF16(MakeStringSpan(aLib), fullName);
171 if (!strstr(aLib, ".dll")) {
172 fullName.AppendLiteral(".dll");
174 return std::move(fullName);
175 # else
176 char* temp = PR_GetLibraryName(aDirectory, aLib);
177 if (!temp) {
178 return ""_ns;
180 nsAutoCString libname(temp);
181 PR_FreeLibraryName(temp);
182 return std::move(libname);
183 # endif
186 mozilla::PathString mozilla::GetLibraryFilePathname(mozilla::pathstr_t aName,
187 PRFuncPtr aAddr) {
188 # ifdef XP_WIN
189 HMODULE handle = GetModuleHandleW(char16ptr_t(aName));
190 if (!handle) {
191 return u""_ns;
194 nsAutoString path;
195 path.SetLength(MAX_PATH);
196 DWORD len = GetModuleFileNameW(handle, char16ptr_t(path.BeginWriting()),
197 path.Length());
198 if (!len) {
199 return u""_ns;
202 path.SetLength(len);
203 return std::move(path);
204 # else
205 char* temp = PR_GetLibraryFilePathname(aName, aAddr);
206 if (!temp) {
207 return ""_ns;
209 nsAutoCString path(temp);
210 PR_Free(temp); // PR_GetLibraryFilePathname() uses PR_Malloc().
211 return std::move(path);
212 # endif
215 #endif // defined(MOZILLA_INTERNAL_API)
217 #if defined(LINUX) && !defined(ANDROID)
219 static const unsigned int bufsize = 4096;
221 # ifdef __LP64__
222 typedef Elf64_Ehdr Elf_Ehdr;
223 typedef Elf64_Phdr Elf_Phdr;
224 static const unsigned char ELFCLASS = ELFCLASS64;
225 typedef Elf64_Off Elf_Off;
226 # else
227 typedef Elf32_Ehdr Elf_Ehdr;
228 typedef Elf32_Phdr Elf_Phdr;
229 static const unsigned char ELFCLASS = ELFCLASS32;
230 typedef Elf32_Off Elf_Off;
231 # endif
233 #elif defined(XP_MACOSX)
235 # if defined(__i386__)
236 static const uint32_t CPU_TYPE = CPU_TYPE_X86;
237 # elif defined(__x86_64__)
238 static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
239 # elif defined(__ppc__)
240 static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
241 # elif defined(__ppc64__)
242 static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
243 # elif defined(__aarch64__)
244 static const uint32_t CPU_TYPE = CPU_TYPE_ARM64;
245 # else
246 # error Unsupported CPU type
247 # endif
249 # ifdef __LP64__
250 # undef LC_SEGMENT
251 # define LC_SEGMENT LC_SEGMENT_64
252 # undef MH_MAGIC
253 # define MH_MAGIC MH_MAGIC_64
254 # define cpu_mach_header mach_header_64
255 # define segment_command segment_command_64
256 # else
257 # define cpu_mach_header mach_header
258 # endif
260 class ScopedMMap {
261 public:
262 explicit ScopedMMap(const char* aFilePath) : buf(nullptr) {
263 fd = open(aFilePath, O_RDONLY);
264 if (fd < 0) {
265 return;
267 struct stat st;
268 if (fstat(fd, &st) < 0) {
269 return;
271 size = st.st_size;
272 buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
273 if (buf == MAP_FAILED) {
274 buf = nullptr;
277 ~ScopedMMap() {
278 if (buf) {
279 munmap(buf, size);
281 if (fd >= 0) {
282 close(fd);
285 operator char*() { return buf; }
286 int getFd() { return fd; }
288 private:
289 int fd;
290 char* buf;
291 size_t size;
293 #endif
295 void mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
296 const size_t aCount) {
297 #if defined(XP_WIN)
299 LARGE_INTEGER fpOriginal;
300 LARGE_INTEGER fpOffset;
301 # if defined(HAVE_LONG_LONG)
302 fpOffset.QuadPart = 0;
303 # else
304 fpOffset.u.LowPart = 0;
305 fpOffset.u.HighPart = 0;
306 # endif
308 // Get the current file pointer so that we can restore it. This isn't
309 // really necessary other than to provide the same semantics regarding the
310 // file pointer that other platforms do
311 if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
312 return;
315 if (aOffset) {
316 # if defined(HAVE_LONG_LONG)
317 fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
318 # else
319 fpOffset.u.LowPart = aOffset;
320 fpOffset.u.HighPart = 0;
321 # endif
323 if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
324 return;
328 char buf[64 * 1024];
329 size_t totalBytesRead = 0;
330 DWORD dwBytesRead;
331 // Do dummy reads to trigger kernel-side readhead via
332 // FILE_FLAG_SEQUENTIAL_SCAN. Abort when underfilling because during testing
333 // the buffers are read fully A buffer that's not keeping up would imply that
334 // readahead isn't working right
335 while (totalBytesRead < aCount &&
336 ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
337 dwBytesRead == sizeof(buf)) {
338 totalBytesRead += dwBytesRead;
341 // Restore the file pointer
342 SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
344 #elif defined(LINUX) && !defined(ANDROID)
346 readahead(aFd, aOffset, aCount);
348 #elif defined(XP_MACOSX)
350 struct radvisory ra;
351 ra.ra_offset = aOffset;
352 ra.ra_count = aCount;
353 // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
354 fcntl(aFd, F_RDADVISE, &ra);
356 #endif
359 void mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath) {
360 if (!aFilePath) {
361 return;
364 #ifdef XP_WIN
365 auto WideToUTF8 = [](const wchar_t* aStr) -> std::string {
366 std::string s;
367 // Determine the number of output bytes (including null terminator).
368 const int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, nullptr, 0,
369 nullptr, nullptr);
370 if (numConv == 0) {
371 return s;
373 s.resize(numConv);
374 const int numConvd = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, s.data(),
375 numConv, nullptr, nullptr);
376 if (numConvd != numConv) {
377 // Error during conversion, remove any temporary data.
378 s.clear();
380 return s;
382 #endif
384 AUTO_BASE_PROFILER_MARKER_TEXT("ReadAheadLib", OTHER, {},
385 #ifdef XP_WIN
386 WideToUTF8(aFilePath)
387 #else
388 aFilePath
389 #endif
392 #if defined(XP_WIN)
393 if (!CanPrefetchMemory()) {
394 ReadAheadFile(aFilePath);
395 return;
397 nsAutoHandle fd(CreateFileW(aFilePath, GENERIC_READ | GENERIC_EXECUTE,
398 FILE_SHARE_READ, nullptr, OPEN_EXISTING,
399 FILE_FLAG_SEQUENTIAL_SCAN, nullptr));
400 if (!fd) {
401 return;
404 nsAutoHandle mapping(CreateFileMapping(
405 fd, nullptr, SEC_IMAGE | PAGE_EXECUTE_READ, 0, 0, nullptr));
406 if (!mapping) {
407 return;
410 PVOID data = MapViewOfFile(
411 mapping, FILE_MAP_READ | FILE_MAP_EXECUTE | SEC_IMAGE, 0, 0, 0);
412 if (!data) {
413 return;
415 auto guard = MakeScopeExit([=]() { UnmapViewOfFile(data); });
416 mozilla::nt::PEHeaders headers(data);
417 Maybe<Span<const uint8_t>> bounds = headers.GetBounds();
418 if (!bounds) {
419 return;
422 PrefetchMemory((uint8_t*)data, bounds->Length());
424 #elif defined(LINUX) && !defined(ANDROID)
425 int fd = open(aFilePath, O_RDONLY);
426 if (fd < 0) {
427 return;
430 union {
431 char buf[bufsize];
432 Elf_Ehdr ehdr;
433 } elf;
434 // Read ELF header (ehdr) and program header table (phdr).
435 // We check that the ELF magic is found, that the ELF class matches
436 // our own, and that the program header table as defined in the ELF
437 // headers fits in the buffer we read.
438 if ((read(fd, elf.buf, bufsize) <= 0) || (memcmp(elf.buf, ELFMAG, 4)) ||
439 (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
440 // Upcast e_phentsize so the multiplication is done in the same precision
441 // as the subsequent addition, to satisfy static analyzers and avoid
442 // issues with abnormally large program header tables.
443 (elf.ehdr.e_phoff +
444 (static_cast<Elf_Off>(elf.ehdr.e_phentsize) * elf.ehdr.e_phnum) >=
445 bufsize)) {
446 close(fd);
447 return;
449 // The program header table contains segment definitions. One such
450 // segment type is PT_LOAD, which describes how the dynamic loader
451 // is going to map the file in memory. We use that information to
452 // find the biggest offset from the library that will be mapped in
453 // memory.
454 Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
455 Elf_Off end = 0;
456 for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
457 if ((phdr->p_type == PT_LOAD) && (end < phdr->p_offset + phdr->p_filesz)) {
458 end = phdr->p_offset + phdr->p_filesz;
461 // Let the kernel read ahead what the dynamic loader is going to
462 // map in memory soon after.
463 if (end > 0) {
464 ReadAhead(fd, 0, end);
466 close(fd);
467 #elif defined(XP_MACOSX)
468 ScopedMMap buf(aFilePath);
469 char* base = buf;
470 if (!base) {
471 return;
474 // An OSX binary might either be a fat (universal) binary or a
475 // Mach-O binary. A fat binary actually embeds several Mach-O
476 // binaries. If we have a fat binary, find the offset where the
477 // Mach-O binary for our CPU type can be found.
478 struct fat_header* fh = (struct fat_header*)base;
480 if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
481 uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
482 struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)];
483 for (; nfat_arch; arch++, nfat_arch--) {
484 if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
485 base += OSSwapBigToHostInt32(arch->offset);
486 break;
489 if (base == buf) {
490 return;
494 // Check Mach-O magic in the Mach header
495 struct cpu_mach_header* mh = (struct cpu_mach_header*)base;
496 if (mh->magic != MH_MAGIC) {
497 return;
500 // The Mach header is followed by a sequence of load commands.
501 // Each command has a header containing the command type and the
502 // command size. LD_SEGMENT commands describes how the dynamic
503 // loader is going to map the file in memory. We use that
504 // information to find the biggest offset from the library that
505 // will be mapped in memory.
506 char* cmd = &base[sizeof(struct cpu_mach_header)];
507 uint32_t end = 0;
508 for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
509 struct segment_command* sh = (struct segment_command*)cmd;
510 if (sh->cmd != LC_SEGMENT) {
511 continue;
513 if (end < sh->fileoff + sh->filesize) {
514 end = sh->fileoff + sh->filesize;
516 cmd += sh->cmdsize;
518 // Let the kernel read ahead what the dynamic loader is going to
519 // map in memory soon after.
520 if (end > 0) {
521 ReadAhead(buf.getFd(), base - buf, end);
523 #endif
526 void mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
527 const size_t aCount, mozilla::filedesc_t* aOutFd) {
528 #if defined(XP_WIN)
529 if (!aFilePath) {
530 if (aOutFd) {
531 *aOutFd = INVALID_HANDLE_VALUE;
533 return;
535 HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
536 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
537 if (aOutFd) {
538 *aOutFd = fd;
540 if (fd == INVALID_HANDLE_VALUE) {
541 return;
543 ReadAhead(fd, aOffset, aCount);
544 if (!aOutFd) {
545 CloseHandle(fd);
547 #elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
548 if (!aFilePath) {
549 if (aOutFd) {
550 *aOutFd = -1;
552 return;
554 int fd = open(aFilePath, O_RDONLY);
555 if (aOutFd) {
556 *aOutFd = fd;
558 if (fd < 0) {
559 return;
561 size_t count;
562 if (aCount == SIZE_MAX) {
563 struct stat st;
564 if (fstat(fd, &st) < 0) {
565 if (!aOutFd) {
566 close(fd);
568 return;
570 count = st.st_size;
571 } else {
572 count = aCount;
574 ReadAhead(fd, aOffset, count);
575 if (!aOutFd) {
576 close(fd);
578 #endif