123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include <errno.h>
- #include <stdio.h>
- #include "nscore.h"
- #include "nsStringGlue.h"
- #include "private/pprio.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/FileUtils.h"
- #if defined(XP_UNIX)
- #include <fcntl.h>
- #include <unistd.h>
- #if defined(LINUX)
- #include <elf.h>
- #endif
- #include <sys/types.h>
- #include <sys/stat.h>
- #elif defined(XP_WIN)
- #include <windows.h>
- #endif
- // Functions that are not to be used in standalone glue must be implemented
- // within this #if block
- #if !defined(XPCOM_GLUE)
- bool
- mozilla::fallocate(PRFileDesc* aFD, int64_t aLength)
- {
- #if defined(HAVE_POSIX_FALLOCATE)
- return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
- #elif defined(XP_WIN)
- int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
- if (oldpos == -1) {
- return false;
- }
- if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
- return false;
- }
- bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
- PR_Seek64(aFD, oldpos, PR_SEEK_SET);
- return retval;
- #elif defined(XP_UNIX)
- // The following is copied from fcntlSizeHint in sqlite
- /* If the OS does not have posix_fallocate(), fake it. First use
- ** ftruncate() to set the file size, then write a single byte to
- ** the last byte in each block within the extended region. This
- ** is the same technique used by glibc to implement posix_fallocate()
- ** on systems that do not have a real fallocate() system call.
- */
- int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
- if (oldpos == -1) {
- return false;
- }
- struct stat buf;
- int fd = PR_FileDesc2NativeHandle(aFD);
- if (fstat(fd, &buf)) {
- return false;
- }
- if (buf.st_size >= aLength) {
- return false;
- }
- const int nBlk = buf.st_blksize;
- if (!nBlk) {
- return false;
- }
- if (ftruncate(fd, aLength)) {
- return false;
- }
- int nWrite; // Return value from write()
- int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - 1; // Next offset to write to
- while (iWrite < aLength) {
- nWrite = 0;
- if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
- nWrite = PR_Write(aFD, "", 1);
- }
- if (nWrite != 1) {
- break;
- }
- iWrite += nBlk;
- }
- PR_Seek64(aFD, oldpos, PR_SEEK_SET);
- return nWrite == 1;
- #endif
- return false;
- }
- #ifdef ReadSysFile_PRESENT
- bool
- mozilla::ReadSysFile(
- const char* aFilename,
- char* aBuf,
- size_t aBufSize)
- {
- int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_RDONLY));
- if (fd < 0) {
- return false;
- }
- ScopedClose autoClose(fd);
- if (aBufSize == 0) {
- return true;
- }
- ssize_t bytesRead;
- size_t offset = 0;
- do {
- bytesRead = MOZ_TEMP_FAILURE_RETRY(read(fd, aBuf + offset,
- aBufSize - offset));
- if (bytesRead == -1) {
- return false;
- }
- offset += bytesRead;
- } while (bytesRead > 0 && offset < aBufSize);
- MOZ_ASSERT(offset <= aBufSize);
- if (offset > 0 && aBuf[offset - 1] == '\n') {
- offset--;
- }
- if (offset == aBufSize) {
- MOZ_ASSERT(offset > 0);
- offset--;
- }
- aBuf[offset] = '\0';
- return true;
- }
- bool
- mozilla::ReadSysFile(
- const char* aFilename,
- int* aVal)
- {
- char valBuf[32];
- if (!ReadSysFile(aFilename, valBuf, sizeof(valBuf))) {
- return false;
- }
- return sscanf(valBuf, "%d", aVal) == 1;
- }
- bool
- mozilla::ReadSysFile(
- const char* aFilename,
- bool* aVal)
- {
- int v;
- if (!ReadSysFile(aFilename, &v)) {
- return false;
- }
- *aVal = (v != 0);
- return true;
- }
- #endif /* ReadSysFile_PRESENT */
- #ifdef WriteSysFile_PRESENT
- bool
- mozilla::WriteSysFile(
- const char* aFilename,
- const char* aBuf)
- {
- size_t aBufSize = strlen(aBuf);
- int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_WRONLY));
- if (fd < 0) {
- return false;
- }
- ScopedClose autoClose(fd);
- ssize_t bytesWritten;
- size_t offset = 0;
- do {
- bytesWritten = MOZ_TEMP_FAILURE_RETRY(write(fd, aBuf + offset,
- aBufSize - offset));
- if (bytesWritten == -1) {
- return false;
- }
- offset += bytesWritten;
- } while (bytesWritten > 0 && offset < aBufSize);
- MOZ_ASSERT(offset == aBufSize);
- return true;
- }
- #endif /* WriteSysFile_PRESENT */
- void
- mozilla::ReadAheadLib(nsIFile* aFile)
- {
- #if defined(XP_WIN)
- nsAutoString path;
- if (!aFile || NS_FAILED(aFile->GetPath(path))) {
- return;
- }
- ReadAheadLib(path.get());
- #elif defined(LINUX)
- nsAutoCString nativePath;
- if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
- return;
- }
- ReadAheadLib(nativePath.get());
- #endif
- }
- void
- mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
- const size_t aCount, mozilla::filedesc_t* aOutFd)
- {
- #if defined(XP_WIN)
- nsAutoString path;
- if (!aFile || NS_FAILED(aFile->GetPath(path))) {
- return;
- }
- ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
- #elif defined(LINUX)
- nsAutoCString nativePath;
- if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
- return;
- }
- ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
- #endif
- }
- #endif // !defined(XPCOM_GLUE)
- #if defined(LINUX)
- static const unsigned int bufsize = 4096;
- #ifdef __LP64__
- typedef Elf64_Ehdr Elf_Ehdr;
- typedef Elf64_Phdr Elf_Phdr;
- static const unsigned char ELFCLASS = ELFCLASS64;
- typedef Elf64_Off Elf_Off;
- #else
- typedef Elf32_Ehdr Elf_Ehdr;
- typedef Elf32_Phdr Elf_Phdr;
- static const unsigned char ELFCLASS = ELFCLASS32;
- typedef Elf32_Off Elf_Off;
- #endif
- #endif
- void
- mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
- const size_t aCount)
- {
- #if defined(XP_WIN)
- LARGE_INTEGER fpOriginal;
- LARGE_INTEGER fpOffset;
- #if defined(HAVE_LONG_LONG)
- fpOffset.QuadPart = 0;
- #else
- fpOffset.u.LowPart = 0;
- fpOffset.u.HighPart = 0;
- #endif
- // Get the current file pointer so that we can restore it. This isn't
- // really necessary other than to provide the same semantics regarding the
- // file pointer that other platforms do
- if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
- return;
- }
- if (aOffset) {
- #if defined(HAVE_LONG_LONG)
- fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
- #else
- fpOffset.u.LowPart = aOffset;
- fpOffset.u.HighPart = 0;
- #endif
- if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
- return;
- }
- }
- char buf[64 * 1024];
- size_t totalBytesRead = 0;
- DWORD dwBytesRead;
- // Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN.
- // Abort when underfilling because during testing the buffers are read fully
- // A buffer that's not keeping up would imply that readahead isn't working right
- while (totalBytesRead < aCount &&
- ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
- dwBytesRead == sizeof(buf)) {
- totalBytesRead += dwBytesRead;
- }
- // Restore the file pointer
- SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
- #elif defined(LINUX)
- readahead(aFd, aOffset, aCount);
- #endif
- }
- void
- mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath)
- {
- if (!aFilePath) {
- return;
- }
- #if defined(XP_WIN)
- ReadAheadFile(aFilePath);
- #elif defined(LINUX)
- int fd = open(aFilePath, O_RDONLY);
- if (fd < 0) {
- return;
- }
- union
- {
- char buf[bufsize];
- Elf_Ehdr ehdr;
- } elf;
- // Read ELF header (ehdr) and program header table (phdr).
- // We check that the ELF magic is found, that the ELF class matches
- // our own, and that the program header table as defined in the ELF
- // headers fits in the buffer we read.
- if ((read(fd, elf.buf, bufsize) <= 0) ||
- (memcmp(elf.buf, ELFMAG, 4)) ||
- (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
- // Upcast e_phentsize so the multiplication is done in the same precision
- // as the subsequent addition, to satisfy static analyzers and avoid
- // issues with abnormally large program header tables.
- (elf.ehdr.e_phoff + (static_cast<Elf_Off>(elf.ehdr.e_phentsize) *
- elf.ehdr.e_phnum) >= bufsize)) {
- close(fd);
- return;
- }
- // The program header table contains segment definitions. One such
- // segment type is PT_LOAD, which describes how the dynamic loader
- // is going to map the file in memory. We use that information to
- // find the biggest offset from the library that will be mapped in
- // memory.
- Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
- Elf_Off end = 0;
- for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
- if ((phdr->p_type == PT_LOAD) &&
- (end < phdr->p_offset + phdr->p_filesz)) {
- end = phdr->p_offset + phdr->p_filesz;
- }
- }
- // Let the kernel read ahead what the dynamic loader is going to
- // map in memory soon after.
- if (end > 0) {
- ReadAhead(fd, 0, end);
- }
- close(fd);
- #endif
- }
- void
- mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
- const size_t aCount, mozilla::filedesc_t* aOutFd)
- {
- #if defined(XP_WIN)
- if (!aFilePath) {
- if (aOutFd) {
- *aOutFd = INVALID_HANDLE_VALUE;
- }
- return;
- }
- HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
- OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
- if (aOutFd) {
- *aOutFd = fd;
- }
- if (fd == INVALID_HANDLE_VALUE) {
- return;
- }
- ReadAhead(fd, aOffset, aCount);
- if (!aOutFd) {
- CloseHandle(fd);
- }
- #elif defined(LINUX) || defined(XP_SOLARIS)
- if (!aFilePath) {
- if (aOutFd) {
- *aOutFd = -1;
- }
- return;
- }
- int fd = open(aFilePath, O_RDONLY);
- if (aOutFd) {
- *aOutFd = fd;
- }
- if (fd < 0) {
- return;
- }
- size_t count;
- if (aCount == SIZE_MAX) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- if (!aOutFd) {
- close(fd);
- }
- return;
- }
- count = st.st_size;
- } else {
- count = aCount;
- }
- ReadAhead(fd, aOffset, count);
- if (!aOutFd) {
- close(fd);
- }
- #endif
- }
|