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/. */
6
7 #include <errno.h>
8 #include <stdio.h>
9
10 #include "nscore.h"
11 #include "nsStringGlue.h"
12 #include "private/pprio.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/FileUtils.h"
15
16 #if defined(XP_MACOSX)
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <mach/machine.h>
20 #include <mach-o/fat.h>
21 #include <mach-o/loader.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #include <limits.h>
25 #elif defined(XP_UNIX)
26 #include <fcntl.h>
27 #include <unistd.h>
28 #if defined(LINUX)
29 #include <elf.h>
30 #endif
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #elif defined(XP_WIN)
34 #include <windows.h>
35 #endif
36
37 // Functions that are not to be used in standalone glue must be implemented
38 // within this #if block
39 #if !defined(XPCOM_GLUE)
40
41 bool
fallocate(PRFileDesc * aFD,int64_t aLength)42 mozilla::fallocate(PRFileDesc* aFD, int64_t aLength)
43 {
44 #if defined(HAVE_POSIX_FALLOCATE)
45 return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
46 #elif defined(XP_WIN)
47 int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
48 if (oldpos == -1) {
49 return false;
50 }
51
52 if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
53 return false;
54 }
55
56 bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
57
58 PR_Seek64(aFD, oldpos, PR_SEEK_SET);
59 return retval;
60 #elif defined(XP_MACOSX)
61 int fd = PR_FileDesc2NativeHandle(aFD);
62 fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength};
63 // Try to get a continous chunk of disk space
64 int ret = fcntl(fd, F_PREALLOCATE, &store);
65 if (ret == -1) {
66 // OK, perhaps we are too fragmented, allocate non-continuous
67 store.fst_flags = F_ALLOCATEALL;
68 ret = fcntl(fd, F_PREALLOCATE, &store);
69 if (ret == -1) {
70 return false;
71 }
72 }
73 return ftruncate(fd, aLength) == 0;
74 #elif defined(XP_UNIX)
75 // The following is copied from fcntlSizeHint in sqlite
76 /* If the OS does not have posix_fallocate(), fake it. First use
77 ** ftruncate() to set the file size, then write a single byte to
78 ** the last byte in each block within the extended region. This
79 ** is the same technique used by glibc to implement posix_fallocate()
80 ** on systems that do not have a real fallocate() system call.
81 */
82 int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
83 if (oldpos == -1) {
84 return false;
85 }
86
87 struct stat buf;
88 int fd = PR_FileDesc2NativeHandle(aFD);
89 if (fstat(fd, &buf)) {
90 return false;
91 }
92
93 if (buf.st_size >= aLength) {
94 return false;
95 }
96
97 const int nBlk = buf.st_blksize;
98
99 if (!nBlk) {
100 return false;
101 }
102
103 if (ftruncate(fd, aLength)) {
104 return false;
105 }
106
107 int nWrite; // Return value from write()
108 int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - 1; // Next offset to write to
109 while (iWrite < aLength) {
110 nWrite = 0;
111 if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
112 nWrite = PR_Write(aFD, "", 1);
113 }
114 if (nWrite != 1) {
115 break;
116 }
117 iWrite += nBlk;
118 }
119
120 PR_Seek64(aFD, oldpos, PR_SEEK_SET);
121 return nWrite == 1;
122 #endif
123 return false;
124 }
125
126 #ifdef ReadSysFile_PRESENT
127
128 bool
ReadSysFile(const char * aFilename,char * aBuf,size_t aBufSize)129 mozilla::ReadSysFile(
130 const char* aFilename,
131 char* aBuf,
132 size_t aBufSize)
133 {
134 int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_RDONLY));
135 if (fd < 0) {
136 return false;
137 }
138 ScopedClose autoClose(fd);
139 if (aBufSize == 0) {
140 return true;
141 }
142 ssize_t bytesRead;
143 size_t offset = 0;
144 do {
145 bytesRead = MOZ_TEMP_FAILURE_RETRY(read(fd, aBuf + offset,
146 aBufSize - offset));
147 if (bytesRead == -1) {
148 return false;
149 }
150 offset += bytesRead;
151 } while (bytesRead > 0 && offset < aBufSize);
152 MOZ_ASSERT(offset <= aBufSize);
153 if (offset > 0 && aBuf[offset - 1] == '\n') {
154 offset--;
155 }
156 if (offset == aBufSize) {
157 MOZ_ASSERT(offset > 0);
158 offset--;
159 }
160 aBuf[offset] = '\0';
161 return true;
162 }
163
164 bool
ReadSysFile(const char * aFilename,int * aVal)165 mozilla::ReadSysFile(
166 const char* aFilename,
167 int* aVal)
168 {
169 char valBuf[32];
170 if (!ReadSysFile(aFilename, valBuf, sizeof(valBuf))) {
171 return false;
172 }
173 return sscanf(valBuf, "%d", aVal) == 1;
174 }
175
176 bool
ReadSysFile(const char * aFilename,bool * aVal)177 mozilla::ReadSysFile(
178 const char* aFilename,
179 bool* aVal)
180 {
181 int v;
182 if (!ReadSysFile(aFilename, &v)) {
183 return false;
184 }
185 *aVal = (v != 0);
186 return true;
187 }
188
189 #endif /* ReadSysFile_PRESENT */
190
191 #ifdef WriteSysFile_PRESENT
192
193 bool
WriteSysFile(const char * aFilename,const char * aBuf)194 mozilla::WriteSysFile(
195 const char* aFilename,
196 const char* aBuf)
197 {
198 size_t aBufSize = strlen(aBuf);
199 int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_WRONLY));
200 if (fd < 0) {
201 return false;
202 }
203 ScopedClose autoClose(fd);
204 ssize_t bytesWritten;
205 size_t offset = 0;
206 do {
207 bytesWritten = MOZ_TEMP_FAILURE_RETRY(write(fd, aBuf + offset,
208 aBufSize - offset));
209 if (bytesWritten == -1) {
210 return false;
211 }
212 offset += bytesWritten;
213 } while (bytesWritten > 0 && offset < aBufSize);
214 MOZ_ASSERT(offset == aBufSize);
215 return true;
216 }
217
218 #endif /* WriteSysFile_PRESENT */
219
220 void
ReadAheadLib(nsIFile * aFile)221 mozilla::ReadAheadLib(nsIFile* aFile)
222 {
223 #if defined(XP_WIN)
224 nsAutoString path;
225 if (!aFile || NS_FAILED(aFile->GetPath(path))) {
226 return;
227 }
228 ReadAheadLib(path.get());
229 #elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
230 nsAutoCString nativePath;
231 if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
232 return;
233 }
234 ReadAheadLib(nativePath.get());
235 #endif
236 }
237
238 void
ReadAheadFile(nsIFile * aFile,const size_t aOffset,const size_t aCount,mozilla::filedesc_t * aOutFd)239 mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
240 const size_t aCount, mozilla::filedesc_t* aOutFd)
241 {
242 #if defined(XP_WIN)
243 nsAutoString path;
244 if (!aFile || NS_FAILED(aFile->GetPath(path))) {
245 return;
246 }
247 ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
248 #elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
249 nsAutoCString nativePath;
250 if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
251 return;
252 }
253 ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
254 #endif
255 }
256
257 #endif // !defined(XPCOM_GLUE)
258
259 #if defined(LINUX) && !defined(ANDROID)
260
261 static const unsigned int bufsize = 4096;
262
263 #ifdef __LP64__
264 typedef Elf64_Ehdr Elf_Ehdr;
265 typedef Elf64_Phdr Elf_Phdr;
266 static const unsigned char ELFCLASS = ELFCLASS64;
267 typedef Elf64_Off Elf_Off;
268 #else
269 typedef Elf32_Ehdr Elf_Ehdr;
270 typedef Elf32_Phdr Elf_Phdr;
271 static const unsigned char ELFCLASS = ELFCLASS32;
272 typedef Elf32_Off Elf_Off;
273 #endif
274
275 #elif defined(XP_MACOSX)
276
277 #if defined(__i386__)
278 static const uint32_t CPU_TYPE = CPU_TYPE_X86;
279 #elif defined(__x86_64__)
280 static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
281 #elif defined(__ppc__)
282 static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
283 #elif defined(__ppc64__)
284 static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
285 #else
286 #error Unsupported CPU type
287 #endif
288
289 #ifdef __LP64__
290 #undef LC_SEGMENT
291 #define LC_SEGMENT LC_SEGMENT_64
292 #undef MH_MAGIC
293 #define MH_MAGIC MH_MAGIC_64
294 #define cpu_mach_header mach_header_64
295 #define segment_command segment_command_64
296 #else
297 #define cpu_mach_header mach_header
298 #endif
299
300 class ScopedMMap
301 {
302 public:
ScopedMMap(const char * aFilePath)303 explicit ScopedMMap(const char* aFilePath)
304 : buf(nullptr)
305 {
306 fd = open(aFilePath, O_RDONLY);
307 if (fd < 0) {
308 return;
309 }
310 struct stat st;
311 if (fstat(fd, &st) < 0) {
312 return;
313 }
314 size = st.st_size;
315 buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
316 }
~ScopedMMap()317 ~ScopedMMap()
318 {
319 if (buf) {
320 munmap(buf, size);
321 }
322 if (fd >= 0) {
323 close(fd);
324 }
325 }
operator char*()326 operator char*() { return buf; }
getFd()327 int getFd() { return fd; }
328 private:
329 int fd;
330 char* buf;
331 size_t size;
332 };
333 #endif
334
335 void
ReadAhead(mozilla::filedesc_t aFd,const size_t aOffset,const size_t aCount)336 mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
337 const size_t aCount)
338 {
339 #if defined(XP_WIN)
340
341 LARGE_INTEGER fpOriginal;
342 LARGE_INTEGER fpOffset;
343 #if defined(HAVE_LONG_LONG)
344 fpOffset.QuadPart = 0;
345 #else
346 fpOffset.u.LowPart = 0;
347 fpOffset.u.HighPart = 0;
348 #endif
349
350 // Get the current file pointer so that we can restore it. This isn't
351 // really necessary other than to provide the same semantics regarding the
352 // file pointer that other platforms do
353 if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
354 return;
355 }
356
357 if (aOffset) {
358 #if defined(HAVE_LONG_LONG)
359 fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
360 #else
361 fpOffset.u.LowPart = aOffset;
362 fpOffset.u.HighPart = 0;
363 #endif
364
365 if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
366 return;
367 }
368 }
369
370 char buf[64 * 1024];
371 size_t totalBytesRead = 0;
372 DWORD dwBytesRead;
373 // Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN.
374 // Abort when underfilling because during testing the buffers are read fully
375 // A buffer that's not keeping up would imply that readahead isn't working right
376 while (totalBytesRead < aCount &&
377 ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
378 dwBytesRead == sizeof(buf)) {
379 totalBytesRead += dwBytesRead;
380 }
381
382 // Restore the file pointer
383 SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
384
385 #elif defined(LINUX) && !defined(ANDROID)
386
387 readahead(aFd, aOffset, aCount);
388
389 #elif defined(XP_MACOSX)
390
391 struct radvisory ra;
392 ra.ra_offset = aOffset;
393 ra.ra_count = aCount;
394 // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
395 fcntl(aFd, F_RDADVISE, &ra);
396
397 #endif
398 }
399
400 void
ReadAheadLib(mozilla::pathstr_t aFilePath)401 mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath)
402 {
403 if (!aFilePath) {
404 return;
405 }
406 #if defined(XP_WIN)
407 ReadAheadFile(aFilePath);
408 #elif defined(LINUX) && !defined(ANDROID)
409 int fd = open(aFilePath, O_RDONLY);
410 if (fd < 0) {
411 return;
412 }
413
414 union
415 {
416 char buf[bufsize];
417 Elf_Ehdr ehdr;
418 } elf;
419 // Read ELF header (ehdr) and program header table (phdr).
420 // We check that the ELF magic is found, that the ELF class matches
421 // our own, and that the program header table as defined in the ELF
422 // headers fits in the buffer we read.
423 if ((read(fd, elf.buf, bufsize) <= 0) ||
424 (memcmp(elf.buf, ELFMAG, 4)) ||
425 (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
426 // Upcast e_phentsize so the multiplication is done in the same precision
427 // as the subsequent addition, to satisfy static analyzers and avoid
428 // issues with abnormally large program header tables.
429 (elf.ehdr.e_phoff + (static_cast<Elf_Off>(elf.ehdr.e_phentsize) *
430 elf.ehdr.e_phnum) >= bufsize)) {
431 close(fd);
432 return;
433 }
434 // The program header table contains segment definitions. One such
435 // segment type is PT_LOAD, which describes how the dynamic loader
436 // is going to map the file in memory. We use that information to
437 // find the biggest offset from the library that will be mapped in
438 // memory.
439 Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
440 Elf_Off end = 0;
441 for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
442 if ((phdr->p_type == PT_LOAD) &&
443 (end < phdr->p_offset + phdr->p_filesz)) {
444 end = phdr->p_offset + phdr->p_filesz;
445 }
446 }
447 // Let the kernel read ahead what the dynamic loader is going to
448 // map in memory soon after.
449 if (end > 0) {
450 ReadAhead(fd, 0, end);
451 }
452 close(fd);
453 #elif defined(XP_MACOSX)
454 ScopedMMap buf(aFilePath);
455 char* base = buf;
456 if (!base) {
457 return;
458 }
459
460 // An OSX binary might either be a fat (universal) binary or a
461 // Mach-O binary. A fat binary actually embeds several Mach-O
462 // binaries. If we have a fat binary, find the offset where the
463 // Mach-O binary for our CPU type can be found.
464 struct fat_header* fh = (struct fat_header*)base;
465
466 if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
467 uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
468 struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)];
469 for (; nfat_arch; arch++, nfat_arch--) {
470 if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
471 base += OSSwapBigToHostInt32(arch->offset);
472 break;
473 }
474 }
475 if (base == buf) {
476 return;
477 }
478 }
479
480 // Check Mach-O magic in the Mach header
481 struct cpu_mach_header* mh = (struct cpu_mach_header*)base;
482 if (mh->magic != MH_MAGIC) {
483 return;
484 }
485
486 // The Mach header is followed by a sequence of load commands.
487 // Each command has a header containing the command type and the
488 // command size. LD_SEGMENT commands describes how the dynamic
489 // loader is going to map the file in memory. We use that
490 // information to find the biggest offset from the library that
491 // will be mapped in memory.
492 char* cmd = &base[sizeof(struct cpu_mach_header)];
493 uint32_t end = 0;
494 for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
495 struct segment_command* sh = (struct segment_command*)cmd;
496 if (sh->cmd != LC_SEGMENT) {
497 continue;
498 }
499 if (end < sh->fileoff + sh->filesize) {
500 end = sh->fileoff + sh->filesize;
501 }
502 cmd += sh->cmdsize;
503 }
504 // Let the kernel read ahead what the dynamic loader is going to
505 // map in memory soon after.
506 if (end > 0) {
507 ReadAhead(buf.getFd(), base - buf, end);
508 }
509 #endif
510 }
511
512 void
ReadAheadFile(mozilla::pathstr_t aFilePath,const size_t aOffset,const size_t aCount,mozilla::filedesc_t * aOutFd)513 mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
514 const size_t aCount, mozilla::filedesc_t* aOutFd)
515 {
516 #if defined(XP_WIN)
517 if (!aFilePath) {
518 if (aOutFd) {
519 *aOutFd = INVALID_HANDLE_VALUE;
520 }
521 return;
522 }
523 HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
524 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
525 if (aOutFd) {
526 *aOutFd = fd;
527 }
528 if (fd == INVALID_HANDLE_VALUE) {
529 return;
530 }
531 ReadAhead(fd, aOffset, aCount);
532 if (!aOutFd) {
533 CloseHandle(fd);
534 }
535 #elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
536 if (!aFilePath) {
537 if (aOutFd) {
538 *aOutFd = -1;
539 }
540 return;
541 }
542 int fd = open(aFilePath, O_RDONLY);
543 if (aOutFd) {
544 *aOutFd = fd;
545 }
546 if (fd < 0) {
547 return;
548 }
549 size_t count;
550 if (aCount == SIZE_MAX) {
551 struct stat st;
552 if (fstat(fd, &st) < 0) {
553 if (!aOutFd) {
554 close(fd);
555 }
556 return;
557 }
558 count = st.st_size;
559 } else {
560 count = aCount;
561 }
562 ReadAhead(fd, aOffset, count);
563 if (!aOutFd) {
564 close(fd);
565 }
566 #endif
567 }
568
569