Bug 1732219 - Add API for fetching the preview image. r=geckoview-reviewers,agi,mconley
[gecko.git] / xpcom / glue / FileUtils.cpp
blobb91ecb456d440c7bc447bc01980d750ec0d359aa
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 <errno.h>
10 #include <stdio.h>
11 #include <inttypes.h>
13 #include "nscore.h"
14 #include "private/pprio.h"
15 #include "prmem.h"
16 #include "mozilla/Assertions.h"
17 #include "mozilla/MemUtils.h"
18 #include "mozilla/BaseProfilerMarkers.h"
20 #if defined(XP_MACOSX)
21 # include <fcntl.h>
22 # include <unistd.h>
23 # include <mach/machine.h>
24 # include <mach-o/fat.h>
25 # include <mach-o/loader.h>
26 # include <sys/mman.h>
27 # include <sys/stat.h>
28 # include <limits.h>
29 #elif defined(XP_UNIX)
30 # include <fcntl.h>
31 # include <unistd.h>
32 # if defined(LINUX)
33 # include <elf.h>
34 # endif
35 # include <sys/types.h>
36 # include <sys/stat.h>
37 #elif defined(XP_WIN)
38 # include <nsWindowsHelpers.h>
39 # include <mozilla/NativeNt.h>
40 # include <mozilla/ScopeExit.h>
41 #endif
43 // Functions that are not to be used in standalone glue must be implemented
44 // within this #if block
45 #if defined(MOZILLA_INTERNAL_API)
47 # include "nsString.h"
49 bool mozilla::fallocate(PRFileDesc* aFD, int64_t aLength) {
50 # if defined(HAVE_POSIX_FALLOCATE)
51 return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
52 # elif defined(XP_WIN)
53 int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
54 if (oldpos == -1) {
55 return false;
58 if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
59 return false;
62 bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
64 PR_Seek64(aFD, oldpos, PR_SEEK_SET);
65 return retval;
66 # elif defined(XP_MACOSX)
67 int fd = PR_FileDesc2NativeHandle(aFD);
68 fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength};
69 // Try to get a continous chunk of disk space
70 int ret = fcntl(fd, F_PREALLOCATE, &store);
71 if (ret == -1) {
72 // OK, perhaps we are too fragmented, allocate non-continuous
73 store.fst_flags = F_ALLOCATEALL;
74 ret = fcntl(fd, F_PREALLOCATE, &store);
75 if (ret == -1) {
76 return false;
79 return ftruncate(fd, aLength) == 0;
80 # elif defined(XP_UNIX)
81 // The following is copied from fcntlSizeHint in sqlite
82 /* If the OS does not have posix_fallocate(), fake it. First use
83 ** ftruncate() to set the file size, then write a single byte to
84 ** the last byte in each block within the extended region. This
85 ** is the same technique used by glibc to implement posix_fallocate()
86 ** on systems that do not have a real fallocate() system call.
88 int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
89 if (oldpos == -1) {
90 return false;
93 struct stat buf;
94 int fd = PR_FileDesc2NativeHandle(aFD);
95 if (fstat(fd, &buf)) {
96 return false;
99 if (buf.st_size >= aLength) {
100 return false;
103 const int nBlk = buf.st_blksize;
105 if (!nBlk) {
106 return false;
109 if (ftruncate(fd, aLength)) {
110 return false;
113 int nWrite; // Return value from write()
114 int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk -
115 1; // Next offset to write to
116 while (iWrite < aLength) {
117 nWrite = 0;
118 if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
119 nWrite = PR_Write(aFD, "", 1);
121 if (nWrite != 1) {
122 break;
124 iWrite += nBlk;
127 PR_Seek64(aFD, oldpos, PR_SEEK_SET);
128 return nWrite == 1;
129 # else
130 return false;
131 # endif
134 void mozilla::ReadAheadLib(nsIFile* aFile) {
135 # if defined(XP_WIN)
136 nsAutoString path;
137 if (!aFile || NS_FAILED(aFile->GetPath(path))) {
138 return;
140 ReadAheadLib(path.get());
141 # elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
142 nsAutoCString nativePath;
143 if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
144 return;
146 ReadAheadLib(nativePath.get());
147 # endif
150 void mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
151 const size_t aCount, mozilla::filedesc_t* aOutFd) {
152 # if defined(XP_WIN)
153 nsAutoString path;
154 if (!aFile || NS_FAILED(aFile->GetPath(path))) {
155 return;
157 ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
158 # elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
159 nsAutoCString nativePath;
160 if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
161 return;
163 ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
164 # endif
167 mozilla::PathString mozilla::GetLibraryName(mozilla::pathstr_t aDirectory,
168 const char* aLib) {
169 # ifdef XP_WIN
170 nsAutoString fullName;
171 if (aDirectory) {
172 fullName.Assign(aDirectory);
173 fullName.Append('\\');
175 AppendUTF8toUTF16(MakeStringSpan(aLib), fullName);
176 if (!strstr(aLib, ".dll")) {
177 fullName.AppendLiteral(".dll");
179 return std::move(fullName);
180 # else
181 char* temp = PR_GetLibraryName(aDirectory, aLib);
182 if (!temp) {
183 return ""_ns;
185 nsAutoCString libname(temp);
186 PR_FreeLibraryName(temp);
187 return std::move(libname);
188 # endif
191 mozilla::PathString mozilla::GetLibraryFilePathname(mozilla::pathstr_t aName,
192 PRFuncPtr aAddr) {
193 # ifdef XP_WIN
194 HMODULE handle = GetModuleHandleW(char16ptr_t(aName));
195 if (!handle) {
196 return u""_ns;
199 nsAutoString path;
200 path.SetLength(MAX_PATH);
201 DWORD len = GetModuleFileNameW(handle, char16ptr_t(path.BeginWriting()),
202 path.Length());
203 if (!len) {
204 return u""_ns;
207 path.SetLength(len);
208 return std::move(path);
209 # else
210 char* temp = PR_GetLibraryFilePathname(aName, aAddr);
211 if (!temp) {
212 return ""_ns;
214 nsAutoCString path(temp);
215 PR_Free(temp); // PR_GetLibraryFilePathname() uses PR_Malloc().
216 return std::move(path);
217 # endif
220 #endif // defined(MOZILLA_INTERNAL_API)
222 #if defined(LINUX) && !defined(ANDROID)
224 static const unsigned int bufsize = 4096;
226 # ifdef __LP64__
227 typedef Elf64_Ehdr Elf_Ehdr;
228 typedef Elf64_Phdr Elf_Phdr;
229 static const unsigned char ELFCLASS = ELFCLASS64;
230 typedef Elf64_Off Elf_Off;
231 # else
232 typedef Elf32_Ehdr Elf_Ehdr;
233 typedef Elf32_Phdr Elf_Phdr;
234 static const unsigned char ELFCLASS = ELFCLASS32;
235 typedef Elf32_Off Elf_Off;
236 # endif
238 #elif defined(XP_MACOSX)
240 # if defined(__i386__)
241 static const uint32_t CPU_TYPE = CPU_TYPE_X86;
242 # elif defined(__x86_64__)
243 static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
244 # elif defined(__ppc__)
245 static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
246 # elif defined(__ppc64__)
247 static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
248 # elif defined(__aarch64__)
249 static const uint32_t CPU_TYPE = CPU_TYPE_ARM64;
250 # else
251 # error Unsupported CPU type
252 # endif
254 # ifdef __LP64__
255 # undef LC_SEGMENT
256 # define LC_SEGMENT LC_SEGMENT_64
257 # undef MH_MAGIC
258 # define MH_MAGIC MH_MAGIC_64
259 # define cpu_mach_header mach_header_64
260 # define segment_command segment_command_64
261 # else
262 # define cpu_mach_header mach_header
263 # endif
265 class ScopedMMap {
266 public:
267 explicit ScopedMMap(const char* aFilePath) : buf(nullptr) {
268 fd = open(aFilePath, O_RDONLY);
269 if (fd < 0) {
270 return;
272 struct stat st;
273 if (fstat(fd, &st) < 0) {
274 return;
276 size = st.st_size;
277 buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
278 if (buf == MAP_FAILED) {
279 buf = nullptr;
282 ~ScopedMMap() {
283 if (buf) {
284 munmap(buf, size);
286 if (fd >= 0) {
287 close(fd);
290 operator char*() { return buf; }
291 int getFd() { return fd; }
293 private:
294 int fd;
295 char* buf;
296 size_t size;
298 #endif
300 void mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
301 const size_t aCount) {
302 #if defined(XP_WIN)
304 LARGE_INTEGER fpOriginal;
305 LARGE_INTEGER fpOffset;
306 # if defined(HAVE_LONG_LONG)
307 fpOffset.QuadPart = 0;
308 # else
309 fpOffset.u.LowPart = 0;
310 fpOffset.u.HighPart = 0;
311 # endif
313 // Get the current file pointer so that we can restore it. This isn't
314 // really necessary other than to provide the same semantics regarding the
315 // file pointer that other platforms do
316 if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
317 return;
320 if (aOffset) {
321 # if defined(HAVE_LONG_LONG)
322 fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
323 # else
324 fpOffset.u.LowPart = aOffset;
325 fpOffset.u.HighPart = 0;
326 # endif
328 if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
329 return;
333 char buf[64 * 1024];
334 size_t totalBytesRead = 0;
335 DWORD dwBytesRead;
336 // Do dummy reads to trigger kernel-side readhead via
337 // FILE_FLAG_SEQUENTIAL_SCAN. Abort when underfilling because during testing
338 // the buffers are read fully A buffer that's not keeping up would imply that
339 // readahead isn't working right
340 while (totalBytesRead < aCount &&
341 ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
342 dwBytesRead == sizeof(buf)) {
343 totalBytesRead += dwBytesRead;
346 // Restore the file pointer
347 SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
349 #elif defined(LINUX) && !defined(ANDROID)
351 readahead(aFd, aOffset, aCount);
353 #elif defined(XP_MACOSX)
355 struct radvisory ra;
356 ra.ra_offset = aOffset;
357 ra.ra_count = aCount;
358 // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
359 fcntl(aFd, F_RDADVISE, &ra);
361 #endif
364 void mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath) {
365 if (!aFilePath) {
366 return;
369 #ifdef XP_WIN
370 auto WideToUTF8 = [](const wchar_t* aStr) -> std::string {
371 std::string s;
372 // Determine the number of output bytes (including null terminator).
373 const int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, nullptr, 0,
374 nullptr, nullptr);
375 if (numConv == 0) {
376 return s;
378 s.resize(numConv);
379 const int numConvd = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, s.data(),
380 numConv, nullptr, nullptr);
381 if (numConvd != numConv) {
382 // Error during conversion, remove any temporary data.
383 s.clear();
385 return s;
387 #endif
389 AUTO_BASE_PROFILER_MARKER_TEXT("ReadAheadLib", OTHER, {},
390 #ifdef XP_WIN
391 WideToUTF8(aFilePath)
392 #else
393 aFilePath
394 #endif
397 #if defined(XP_WIN)
398 if (!CanPrefetchMemory()) {
399 ReadAheadFile(aFilePath);
400 return;
402 nsAutoHandle fd(CreateFileW(aFilePath, GENERIC_READ | GENERIC_EXECUTE,
403 FILE_SHARE_READ, nullptr, OPEN_EXISTING,
404 FILE_FLAG_SEQUENTIAL_SCAN, nullptr));
405 if (!fd) {
406 return;
409 nsAutoHandle mapping(CreateFileMapping(
410 fd, nullptr, SEC_IMAGE | PAGE_EXECUTE_READ, 0, 0, nullptr));
411 if (!mapping) {
412 return;
415 PVOID data = MapViewOfFile(
416 mapping, FILE_MAP_READ | FILE_MAP_EXECUTE | SEC_IMAGE, 0, 0, 0);
417 if (!data) {
418 return;
420 auto guard = MakeScopeExit([=]() { UnmapViewOfFile(data); });
421 mozilla::nt::PEHeaders headers(data);
422 Maybe<Span<const uint8_t>> bounds = headers.GetBounds();
423 if (!bounds) {
424 return;
427 PrefetchMemory((uint8_t*)data, bounds->Length());
429 #elif defined(LINUX) && !defined(ANDROID)
430 int fd = open(aFilePath, O_RDONLY);
431 if (fd < 0) {
432 return;
435 union {
436 char buf[bufsize];
437 Elf_Ehdr ehdr;
438 } elf;
439 // Read ELF header (ehdr) and program header table (phdr).
440 // We check that the ELF magic is found, that the ELF class matches
441 // our own, and that the program header table as defined in the ELF
442 // headers fits in the buffer we read.
443 if ((read(fd, elf.buf, bufsize) <= 0) || (memcmp(elf.buf, ELFMAG, 4)) ||
444 (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
445 // Upcast e_phentsize so the multiplication is done in the same precision
446 // as the subsequent addition, to satisfy static analyzers and avoid
447 // issues with abnormally large program header tables.
448 (elf.ehdr.e_phoff +
449 (static_cast<Elf_Off>(elf.ehdr.e_phentsize) * elf.ehdr.e_phnum) >=
450 bufsize)) {
451 close(fd);
452 return;
454 // The program header table contains segment definitions. One such
455 // segment type is PT_LOAD, which describes how the dynamic loader
456 // is going to map the file in memory. We use that information to
457 // find the biggest offset from the library that will be mapped in
458 // memory.
459 Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
460 Elf_Off end = 0;
461 for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
462 if ((phdr->p_type == PT_LOAD) && (end < phdr->p_offset + phdr->p_filesz)) {
463 end = phdr->p_offset + phdr->p_filesz;
466 // Let the kernel read ahead what the dynamic loader is going to
467 // map in memory soon after.
468 if (end > 0) {
469 ReadAhead(fd, 0, end);
471 close(fd);
472 #elif defined(XP_MACOSX)
473 ScopedMMap buf(aFilePath);
474 char* base = buf;
475 if (!base) {
476 return;
479 // An OSX binary might either be a fat (universal) binary or a
480 // Mach-O binary. A fat binary actually embeds several Mach-O
481 // binaries. If we have a fat binary, find the offset where the
482 // Mach-O binary for our CPU type can be found.
483 struct fat_header* fh = (struct fat_header*)base;
485 if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
486 uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
487 struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)];
488 for (; nfat_arch; arch++, nfat_arch--) {
489 if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
490 base += OSSwapBigToHostInt32(arch->offset);
491 break;
494 if (base == buf) {
495 return;
499 // Check Mach-O magic in the Mach header
500 struct cpu_mach_header* mh = (struct cpu_mach_header*)base;
501 if (mh->magic != MH_MAGIC) {
502 return;
505 // The Mach header is followed by a sequence of load commands.
506 // Each command has a header containing the command type and the
507 // command size. LD_SEGMENT commands describes how the dynamic
508 // loader is going to map the file in memory. We use that
509 // information to find the biggest offset from the library that
510 // will be mapped in memory.
511 char* cmd = &base[sizeof(struct cpu_mach_header)];
512 uint32_t end = 0;
513 for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
514 struct segment_command* sh = (struct segment_command*)cmd;
515 if (sh->cmd != LC_SEGMENT) {
516 continue;
518 if (end < sh->fileoff + sh->filesize) {
519 end = sh->fileoff + sh->filesize;
521 cmd += sh->cmdsize;
523 // Let the kernel read ahead what the dynamic loader is going to
524 // map in memory soon after.
525 if (end > 0) {
526 ReadAhead(buf.getFd(), base - buf, end);
528 #endif
531 void mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
532 const size_t aCount, mozilla::filedesc_t* aOutFd) {
533 #if defined(XP_WIN)
534 if (!aFilePath) {
535 if (aOutFd) {
536 *aOutFd = INVALID_HANDLE_VALUE;
538 return;
540 HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
541 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
542 if (aOutFd) {
543 *aOutFd = fd;
545 if (fd == INVALID_HANDLE_VALUE) {
546 return;
548 ReadAhead(fd, aOffset, aCount);
549 if (!aOutFd) {
550 CloseHandle(fd);
552 #elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
553 if (!aFilePath) {
554 if (aOutFd) {
555 *aOutFd = -1;
557 return;
559 int fd = open(aFilePath, O_RDONLY);
560 if (aOutFd) {
561 *aOutFd = fd;
563 if (fd < 0) {
564 return;
566 size_t count;
567 if (aCount == SIZE_MAX) {
568 struct stat st;
569 if (fstat(fd, &st) < 0) {
570 if (!aOutFd) {
571 close(fd);
573 return;
575 count = st.st_size;
576 } else {
577 count = aCount;
579 ReadAhead(fd, aOffset, count);
580 if (!aOutFd) {
581 close(fd);
583 #endif