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