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