xpcom/glue/FileUtils.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 }

mercurial