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