|
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/. */ |
|
5 |
|
6 #include <errno.h> |
|
7 #include <stdio.h> |
|
8 |
|
9 #include "nscore.h" |
|
10 #include "nsStringGlue.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(XPCOM_GLUE) |
|
39 |
|
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; |
|
49 |
|
50 if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) |
|
51 return false; |
|
52 |
|
53 bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD))); |
|
54 |
|
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; |
|
81 |
|
82 struct stat buf; |
|
83 int fd = PR_FileDesc2NativeHandle(aFD); |
|
84 if (fstat(fd, &buf)) |
|
85 return false; |
|
86 |
|
87 if (buf.st_size >= aLength) |
|
88 return false; |
|
89 |
|
90 const int nBlk = buf.st_blksize; |
|
91 |
|
92 if (!nBlk) |
|
93 return false; |
|
94 |
|
95 if (ftruncate(fd, aLength)) |
|
96 return false; |
|
97 |
|
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 } |
|
107 |
|
108 PR_Seek64(aFD, oldpos, PR_SEEK_SET); |
|
109 return nWrite == 1; |
|
110 #endif |
|
111 return false; |
|
112 } |
|
113 |
|
114 #ifdef ReadSysFile_PRESENT |
|
115 |
|
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 } |
|
151 |
|
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 } |
|
163 |
|
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 } |
|
176 |
|
177 #endif /* ReadSysFile_PRESENT */ |
|
178 |
|
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 } |
|
196 |
|
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 } |
|
215 |
|
216 #endif // !defined(XPCOM_GLUE) |
|
217 |
|
218 #if defined(LINUX) && !defined(ANDROID) |
|
219 |
|
220 static const unsigned int bufsize = 4096; |
|
221 |
|
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 |
|
233 |
|
234 #elif defined(XP_MACOSX) |
|
235 |
|
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 |
|
247 |
|
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 |
|
258 |
|
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 |
|
293 |
|
294 void |
|
295 mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset, |
|
296 const size_t aCount) |
|
297 { |
|
298 #if defined(XP_WIN) |
|
299 |
|
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 |
|
308 |
|
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 } |
|
315 |
|
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 |
|
323 |
|
324 if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) { |
|
325 return; |
|
326 } |
|
327 } |
|
328 |
|
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 } |
|
340 |
|
341 // Restore the file pointer |
|
342 SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN); |
|
343 |
|
344 #elif defined(LINUX) && !defined(ANDROID) |
|
345 |
|
346 readahead(aFd, aOffset, aCount); |
|
347 |
|
348 #elif defined(XP_MACOSX) |
|
349 |
|
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); |
|
355 |
|
356 #endif |
|
357 } |
|
358 |
|
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 } |
|
372 |
|
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 } |
|
413 |
|
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; |
|
419 |
|
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 } |
|
433 |
|
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 } |
|
439 |
|
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 } |
|
465 |
|
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 } |
|
522 |