1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/glue/FileUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,522 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <errno.h> 1.10 +#include <stdio.h> 1.11 + 1.12 +#include "nscore.h" 1.13 +#include "nsStringGlue.h" 1.14 +#include "private/pprio.h" 1.15 +#include "mozilla/Assertions.h" 1.16 +#include "mozilla/FileUtils.h" 1.17 + 1.18 +#if defined(XP_MACOSX) 1.19 +#include <fcntl.h> 1.20 +#include <unistd.h> 1.21 +#include <mach/machine.h> 1.22 +#include <mach-o/fat.h> 1.23 +#include <mach-o/loader.h> 1.24 +#include <sys/mman.h> 1.25 +#include <sys/stat.h> 1.26 +#include <limits.h> 1.27 +#elif defined(XP_UNIX) 1.28 +#include <fcntl.h> 1.29 +#include <unistd.h> 1.30 +#if defined(LINUX) 1.31 +#include <elf.h> 1.32 +#endif 1.33 +#include <sys/types.h> 1.34 +#include <sys/stat.h> 1.35 +#elif defined(XP_WIN) 1.36 +#include <windows.h> 1.37 +#endif 1.38 + 1.39 +// Functions that are not to be used in standalone glue must be implemented 1.40 +// within this #if block 1.41 +#if !defined(XPCOM_GLUE) 1.42 + 1.43 +bool 1.44 +mozilla::fallocate(PRFileDesc *aFD, int64_t aLength) 1.45 +{ 1.46 +#if defined(HAVE_POSIX_FALLOCATE) 1.47 + return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0; 1.48 +#elif defined(XP_WIN) 1.49 + int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR); 1.50 + if (oldpos == -1) 1.51 + return false; 1.52 + 1.53 + if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) 1.54 + return false; 1.55 + 1.56 + bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD))); 1.57 + 1.58 + PR_Seek64(aFD, oldpos, PR_SEEK_SET); 1.59 + return retval; 1.60 +#elif defined(XP_MACOSX) 1.61 + int fd = PR_FileDesc2NativeHandle(aFD); 1.62 + fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength}; 1.63 + // Try to get a continous chunk of disk space 1.64 + int ret = fcntl(fd, F_PREALLOCATE, &store); 1.65 + if (-1 == ret) { 1.66 + // OK, perhaps we are too fragmented, allocate non-continuous 1.67 + store.fst_flags = F_ALLOCATEALL; 1.68 + ret = fcntl(fd, F_PREALLOCATE, &store); 1.69 + if (-1 == ret) 1.70 + return false; 1.71 + } 1.72 + return 0 == ftruncate(fd, aLength); 1.73 +#elif defined(XP_UNIX) 1.74 + // The following is copied from fcntlSizeHint in sqlite 1.75 + /* If the OS does not have posix_fallocate(), fake it. First use 1.76 + ** ftruncate() to set the file size, then write a single byte to 1.77 + ** the last byte in each block within the extended region. This 1.78 + ** is the same technique used by glibc to implement posix_fallocate() 1.79 + ** on systems that do not have a real fallocate() system call. 1.80 + */ 1.81 + int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR); 1.82 + if (oldpos == -1) 1.83 + return false; 1.84 + 1.85 + struct stat buf; 1.86 + int fd = PR_FileDesc2NativeHandle(aFD); 1.87 + if (fstat(fd, &buf)) 1.88 + return false; 1.89 + 1.90 + if (buf.st_size >= aLength) 1.91 + return false; 1.92 + 1.93 + const int nBlk = buf.st_blksize; 1.94 + 1.95 + if (!nBlk) 1.96 + return false; 1.97 + 1.98 + if (ftruncate(fd, aLength)) 1.99 + return false; 1.100 + 1.101 + int nWrite; // Return value from write() 1.102 + int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - 1; // Next offset to write to 1.103 + while (iWrite < aLength) { 1.104 + nWrite = 0; 1.105 + if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) 1.106 + nWrite = PR_Write(aFD, "", 1); 1.107 + if (nWrite != 1) break; 1.108 + iWrite += nBlk; 1.109 + } 1.110 + 1.111 + PR_Seek64(aFD, oldpos, PR_SEEK_SET); 1.112 + return nWrite == 1; 1.113 +#endif 1.114 + return false; 1.115 +} 1.116 + 1.117 +#ifdef ReadSysFile_PRESENT 1.118 + 1.119 +bool 1.120 +mozilla::ReadSysFile( 1.121 + const char* aFilename, 1.122 + char* aBuf, 1.123 + size_t aBufSize) 1.124 +{ 1.125 + int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_RDONLY)); 1.126 + if (fd < 0) { 1.127 + return false; 1.128 + } 1.129 + ScopedClose autoClose(fd); 1.130 + if (aBufSize == 0) { 1.131 + return true; 1.132 + } 1.133 + ssize_t bytesRead; 1.134 + size_t offset = 0; 1.135 + do { 1.136 + bytesRead = MOZ_TEMP_FAILURE_RETRY( 1.137 + read(fd, aBuf + offset, aBufSize - offset)); 1.138 + if (bytesRead == -1) { 1.139 + return false; 1.140 + } 1.141 + offset += bytesRead; 1.142 + } while (bytesRead > 0 && offset < aBufSize); 1.143 + MOZ_ASSERT(offset <= aBufSize); 1.144 + if (offset > 0 && aBuf[offset - 1] == '\n') { 1.145 + offset--; 1.146 + } 1.147 + if (offset == aBufSize) { 1.148 + MOZ_ASSERT(offset > 0); 1.149 + offset--; 1.150 + } 1.151 + aBuf[offset] = '\0'; 1.152 + return true; 1.153 +} 1.154 + 1.155 +bool 1.156 +mozilla::ReadSysFile( 1.157 + const char* aFilename, 1.158 + int* aVal) 1.159 +{ 1.160 + char valBuf[32]; 1.161 + if (!ReadSysFile(aFilename, valBuf, sizeof(valBuf))) { 1.162 + return false; 1.163 + } 1.164 + return sscanf(valBuf, "%d", aVal) == 1; 1.165 +} 1.166 + 1.167 +bool 1.168 +mozilla::ReadSysFile( 1.169 + const char* aFilename, 1.170 + bool* aVal) 1.171 +{ 1.172 + int v; 1.173 + if (!ReadSysFile(aFilename, &v)) { 1.174 + return false; 1.175 + } 1.176 + *aVal = (v != 0); 1.177 + return true; 1.178 +} 1.179 + 1.180 +#endif /* ReadSysFile_PRESENT */ 1.181 + 1.182 +void 1.183 +mozilla::ReadAheadLib(nsIFile* aFile) 1.184 +{ 1.185 +#if defined(XP_WIN) 1.186 + nsAutoString path; 1.187 + if (!aFile || NS_FAILED(aFile->GetPath(path))) { 1.188 + return; 1.189 + } 1.190 + ReadAheadLib(path.get()); 1.191 +#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX) 1.192 + nsAutoCString nativePath; 1.193 + if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) { 1.194 + return; 1.195 + } 1.196 + ReadAheadLib(nativePath.get()); 1.197 +#endif 1.198 +} 1.199 + 1.200 +void 1.201 +mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset, 1.202 + const size_t aCount, mozilla::filedesc_t* aOutFd) 1.203 +{ 1.204 +#if defined(XP_WIN) 1.205 + nsAutoString path; 1.206 + if (!aFile || NS_FAILED(aFile->GetPath(path))) { 1.207 + return; 1.208 + } 1.209 + ReadAheadFile(path.get(), aOffset, aCount, aOutFd); 1.210 +#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX) 1.211 + nsAutoCString nativePath; 1.212 + if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) { 1.213 + return; 1.214 + } 1.215 + ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd); 1.216 +#endif 1.217 +} 1.218 + 1.219 +#endif // !defined(XPCOM_GLUE) 1.220 + 1.221 +#if defined(LINUX) && !defined(ANDROID) 1.222 + 1.223 +static const unsigned int bufsize = 4096; 1.224 + 1.225 +#ifdef __LP64__ 1.226 +typedef Elf64_Ehdr Elf_Ehdr; 1.227 +typedef Elf64_Phdr Elf_Phdr; 1.228 +static const unsigned char ELFCLASS = ELFCLASS64; 1.229 +typedef Elf64_Off Elf_Off; 1.230 +#else 1.231 +typedef Elf32_Ehdr Elf_Ehdr; 1.232 +typedef Elf32_Phdr Elf_Phdr; 1.233 +static const unsigned char ELFCLASS = ELFCLASS32; 1.234 +typedef Elf32_Off Elf_Off; 1.235 +#endif 1.236 + 1.237 +#elif defined(XP_MACOSX) 1.238 + 1.239 +#if defined(__i386__) 1.240 +static const uint32_t CPU_TYPE = CPU_TYPE_X86; 1.241 +#elif defined(__x86_64__) 1.242 +static const uint32_t CPU_TYPE = CPU_TYPE_X86_64; 1.243 +#elif defined(__ppc__) 1.244 +static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC; 1.245 +#elif defined(__ppc64__) 1.246 +static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64; 1.247 +#else 1.248 +#error Unsupported CPU type 1.249 +#endif 1.250 + 1.251 +#ifdef __LP64__ 1.252 +#undef LC_SEGMENT 1.253 +#define LC_SEGMENT LC_SEGMENT_64 1.254 +#undef MH_MAGIC 1.255 +#define MH_MAGIC MH_MAGIC_64 1.256 +#define cpu_mach_header mach_header_64 1.257 +#define segment_command segment_command_64 1.258 +#else 1.259 +#define cpu_mach_header mach_header 1.260 +#endif 1.261 + 1.262 +class ScopedMMap 1.263 +{ 1.264 +public: 1.265 + ScopedMMap(const char *aFilePath) 1.266 + : buf(nullptr) 1.267 + { 1.268 + fd = open(aFilePath, O_RDONLY); 1.269 + if (fd < 0) { 1.270 + return; 1.271 + } 1.272 + struct stat st; 1.273 + if (fstat(fd, &st) < 0) { 1.274 + return; 1.275 + } 1.276 + size = st.st_size; 1.277 + buf = (char *)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0); 1.278 + } 1.279 + ~ScopedMMap() 1.280 + { 1.281 + if (buf) { 1.282 + munmap(buf, size); 1.283 + } 1.284 + if (fd >= 0) { 1.285 + close(fd); 1.286 + } 1.287 + } 1.288 + operator char *() { return buf; } 1.289 + int getFd() { return fd; } 1.290 +private: 1.291 + int fd; 1.292 + char *buf; 1.293 + size_t size; 1.294 +}; 1.295 +#endif 1.296 + 1.297 +void 1.298 +mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset, 1.299 + const size_t aCount) 1.300 +{ 1.301 +#if defined(XP_WIN) 1.302 + 1.303 + LARGE_INTEGER fpOriginal; 1.304 + LARGE_INTEGER fpOffset; 1.305 +#if defined(HAVE_LONG_LONG) 1.306 + fpOffset.QuadPart = 0; 1.307 +#else 1.308 + fpOffset.u.LowPart = 0; 1.309 + fpOffset.u.HighPart = 0; 1.310 +#endif 1.311 + 1.312 + // Get the current file pointer so that we can restore it. This isn't 1.313 + // really necessary other than to provide the same semantics regarding the 1.314 + // file pointer that other platforms do 1.315 + if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) { 1.316 + return; 1.317 + } 1.318 + 1.319 + if (aOffset) { 1.320 +#if defined(HAVE_LONG_LONG) 1.321 + fpOffset.QuadPart = static_cast<LONGLONG>(aOffset); 1.322 +#else 1.323 + fpOffset.u.LowPart = aOffset; 1.324 + fpOffset.u.HighPart = 0; 1.325 +#endif 1.326 + 1.327 + if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) { 1.328 + return; 1.329 + } 1.330 + } 1.331 + 1.332 + char buf[64 * 1024]; 1.333 + size_t totalBytesRead = 0; 1.334 + DWORD dwBytesRead; 1.335 + // Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN. 1.336 + // Abort when underfilling because during testing the buffers are read fully 1.337 + // A buffer that's not keeping up would imply that readahead isn't working right 1.338 + while (totalBytesRead < aCount && 1.339 + ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) && 1.340 + dwBytesRead == sizeof(buf)) { 1.341 + totalBytesRead += dwBytesRead; 1.342 + } 1.343 + 1.344 + // Restore the file pointer 1.345 + SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN); 1.346 + 1.347 +#elif defined(LINUX) && !defined(ANDROID) 1.348 + 1.349 + readahead(aFd, aOffset, aCount); 1.350 + 1.351 +#elif defined(XP_MACOSX) 1.352 + 1.353 + struct radvisory ra; 1.354 + ra.ra_offset = aOffset; 1.355 + ra.ra_count = aCount; 1.356 + // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call. 1.357 + fcntl(aFd, F_RDADVISE, &ra); 1.358 + 1.359 +#endif 1.360 +} 1.361 + 1.362 +void 1.363 +mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath) 1.364 +{ 1.365 + if (!aFilePath) { 1.366 + return; 1.367 + } 1.368 +#if defined(XP_WIN) 1.369 + ReadAheadFile(aFilePath); 1.370 +#elif defined(LINUX) && !defined(ANDROID) 1.371 + int fd = open(aFilePath, O_RDONLY); 1.372 + if (fd < 0) { 1.373 + return; 1.374 + } 1.375 + 1.376 + union { 1.377 + char buf[bufsize]; 1.378 + Elf_Ehdr ehdr; 1.379 + } elf; 1.380 + // Read ELF header (ehdr) and program header table (phdr). 1.381 + // We check that the ELF magic is found, that the ELF class matches 1.382 + // our own, and that the program header table as defined in the ELF 1.383 + // headers fits in the buffer we read. 1.384 + if ((read(fd, elf.buf, bufsize) <= 0) || 1.385 + (memcmp(elf.buf, ELFMAG, 4)) || 1.386 + (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) || 1.387 + (elf.ehdr.e_phoff + elf.ehdr.e_phentsize * elf.ehdr.e_phnum >= bufsize)) { 1.388 + close(fd); 1.389 + return; 1.390 + } 1.391 + // The program header table contains segment definitions. One such 1.392 + // segment type is PT_LOAD, which describes how the dynamic loader 1.393 + // is going to map the file in memory. We use that information to 1.394 + // find the biggest offset from the library that will be mapped in 1.395 + // memory. 1.396 + Elf_Phdr *phdr = (Elf_Phdr *)&elf.buf[elf.ehdr.e_phoff]; 1.397 + Elf_Off end = 0; 1.398 + for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) { 1.399 + if ((phdr->p_type == PT_LOAD) && 1.400 + (end < phdr->p_offset + phdr->p_filesz)) { 1.401 + end = phdr->p_offset + phdr->p_filesz; 1.402 + } 1.403 + } 1.404 + // Let the kernel read ahead what the dynamic loader is going to 1.405 + // map in memory soon after. 1.406 + if (end > 0) { 1.407 + ReadAhead(fd, 0, end); 1.408 + } 1.409 + close(fd); 1.410 +#elif defined(XP_MACOSX) 1.411 + ScopedMMap buf(aFilePath); 1.412 + char *base = buf; 1.413 + if (!base) { 1.414 + return; 1.415 + } 1.416 + 1.417 + // An OSX binary might either be a fat (universal) binary or a 1.418 + // Mach-O binary. A fat binary actually embeds several Mach-O 1.419 + // binaries. If we have a fat binary, find the offset where the 1.420 + // Mach-O binary for our CPU type can be found. 1.421 + struct fat_header *fh = (struct fat_header *)base; 1.422 + 1.423 + if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) { 1.424 + uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch); 1.425 + struct fat_arch *arch = (struct fat_arch *)&buf[sizeof(struct fat_header)]; 1.426 + for (; nfat_arch; arch++, nfat_arch--) { 1.427 + if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) { 1.428 + base += OSSwapBigToHostInt32(arch->offset); 1.429 + break; 1.430 + } 1.431 + } 1.432 + if (base == buf) { 1.433 + return; 1.434 + } 1.435 + } 1.436 + 1.437 + // Check Mach-O magic in the Mach header 1.438 + struct cpu_mach_header *mh = (struct cpu_mach_header *)base; 1.439 + if (mh->magic != MH_MAGIC) { 1.440 + return; 1.441 + } 1.442 + 1.443 + // The Mach header is followed by a sequence of load commands. 1.444 + // Each command has a header containing the command type and the 1.445 + // command size. LD_SEGMENT commands describes how the dynamic 1.446 + // loader is going to map the file in memory. We use that 1.447 + // information to find the biggest offset from the library that 1.448 + // will be mapped in memory. 1.449 + char *cmd = &base[sizeof(struct cpu_mach_header)]; 1.450 + uint32_t end = 0; 1.451 + for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) { 1.452 + struct segment_command *sh = (struct segment_command *)cmd; 1.453 + if (sh->cmd != LC_SEGMENT) { 1.454 + continue; 1.455 + } 1.456 + if (end < sh->fileoff + sh->filesize) { 1.457 + end = sh->fileoff + sh->filesize; 1.458 + } 1.459 + cmd += sh->cmdsize; 1.460 + } 1.461 + // Let the kernel read ahead what the dynamic loader is going to 1.462 + // map in memory soon after. 1.463 + if (end > 0) { 1.464 + ReadAhead(buf.getFd(), base - buf, end); 1.465 + } 1.466 +#endif 1.467 +} 1.468 + 1.469 +void 1.470 +mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset, 1.471 + const size_t aCount, mozilla::filedesc_t* aOutFd) 1.472 +{ 1.473 +#if defined(XP_WIN) 1.474 + if (!aFilePath) { 1.475 + if (aOutFd) { 1.476 + *aOutFd = INVALID_HANDLE_VALUE; 1.477 + } 1.478 + return; 1.479 + } 1.480 + HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr, 1.481 + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); 1.482 + if (aOutFd) { 1.483 + *aOutFd = fd; 1.484 + } 1.485 + if (fd == INVALID_HANDLE_VALUE) { 1.486 + return; 1.487 + } 1.488 + ReadAhead(fd, aOffset, aCount); 1.489 + if (!aOutFd) { 1.490 + CloseHandle(fd); 1.491 + } 1.492 +#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX) 1.493 + if (!aFilePath) { 1.494 + if (aOutFd) { 1.495 + *aOutFd = -1; 1.496 + } 1.497 + return; 1.498 + } 1.499 + int fd = open(aFilePath, O_RDONLY); 1.500 + if (aOutFd) { 1.501 + *aOutFd = fd; 1.502 + } 1.503 + if (fd < 0) { 1.504 + return; 1.505 + } 1.506 + size_t count; 1.507 + if (aCount == SIZE_MAX) { 1.508 + struct stat st; 1.509 + if (fstat(fd, &st) < 0) { 1.510 + if (!aOutFd) { 1.511 + close(fd); 1.512 + } 1.513 + return; 1.514 + } 1.515 + count = st.st_size; 1.516 + } else { 1.517 + count = aCount; 1.518 + } 1.519 + ReadAhead(fd, aOffset, count); 1.520 + if (!aOutFd) { 1.521 + close(fd); 1.522 + } 1.523 +#endif 1.524 +} 1.525 +