tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CompleteFile.cpp (4274B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "util/CompleteFile.h"
      8 
      9 #include <cstring>     // std::strcmp
     10 #include <stdio.h>     // FILE, fileno, fopen, getc, getc_unlocked, _getc_nolock
     11 #include <sys/stat.h>  // stat, fstat
     12 
     13 #ifdef __wasi__
     14 #  include "js/Vector.h"
     15 #endif  // __wasi__
     16 
     17 #include "js/CharacterEncoding.h"     // EncodeUtf8ToWide, EncodeUtf8ToNarrow
     18 #include "js/ErrorReport.h"           // JS_ReportErrorNumberUTF8
     19 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_CANT_OPEN
     20 
     21 bool js::ReadCompleteFile(JSContext* cx, FILE* fp, FileContents& buffer) {
     22  /* Get the complete length of the file, if possible. */
     23  struct stat st;
     24  int ok = fstat(fileno(fp), &st);
     25  if (ok != 0) {
     26    // Use the Latin1 variant here (and below), because the encoding of
     27    // strerror() is platform-dependent.
     28    JS_ReportErrorLatin1(cx, "error reading file: %s", strerror(errno));
     29    errno = 0;
     30    return false;
     31  }
     32  if ((st.st_mode & S_IFDIR) != 0) {
     33    JS_ReportErrorLatin1(cx, "error reading file: %s", strerror(EISDIR));
     34    return false;
     35  }
     36 
     37  if (st.st_size > 0) {
     38    if (!buffer.reserve(st.st_size)) {
     39      return false;
     40    }
     41  }
     42 
     43  /* Use the fastest available getc. */
     44  auto fast_getc =
     45 #if defined(HAVE_GETC_UNLOCKED)
     46      getc_unlocked
     47 #elif defined(HAVE__GETC_NOLOCK)
     48      _getc_nolock
     49 #else
     50      getc
     51 #endif
     52      ;
     53 
     54  // Read in the whole file. Note that we can't assume the data's length
     55  // is actually st.st_size, because 1) some files lie about their size
     56  // (/dev/zero and /dev/random), and 2) reading files in text mode on
     57  // Windows collapses "\r\n" pairs to single \n characters.
     58  for (;;) {
     59    int c = fast_getc(fp);
     60    if (c == EOF) {
     61      break;
     62    }
     63    if (!buffer.append(c)) {
     64      return false;
     65    }
     66  }
     67 
     68  if (ferror(fp)) {
     69    // getc failed
     70    JS_ReportErrorLatin1(cx, "error reading file: %s", strerror(errno));
     71    errno = 0;
     72    return false;
     73  }
     74 
     75  return true;
     76 }
     77 
     78 #ifdef __wasi__
     79 static bool NormalizeWASIPath(const char* filename,
     80                              js::Vector<char>* normalized, JSContext* cx) {
     81  // On WASI, we need to collapse ".." path components for the capabilities
     82  // that we pass to our unit tests to be reasonable; otherwise we need to
     83  // grant "tests/script1.js/../lib.js" and "tests/script2.js/../lib.js"
     84  // separately (because the check appears to be a prefix only).
     85  for (const char* cur = filename; *cur; ++cur) {
     86    if (std::strncmp(cur, "/../", 4) == 0) {
     87      do {
     88        if (normalized->empty()) {
     89          JS_ReportErrorASCII(cx, "Path processing error");
     90          return false;
     91        }
     92      } while (normalized->popCopy() != '/');
     93      cur += 2;
     94      continue;
     95    }
     96    if (!normalized->append(*cur)) {
     97      return false;
     98    }
     99  }
    100  if (!normalized->append('\0')) {
    101    return false;
    102  }
    103  return true;
    104 }
    105 #endif
    106 
    107 static FILE* OpenFile(JSContext* cx, const char* filename) {
    108 #ifdef XP_WIN
    109  JS::UniqueWideChars wideFilename = JS::EncodeUtf8ToWide(cx, filename);
    110  if (!wideFilename) {
    111    return nullptr;
    112  }
    113  return _wfopen(wideFilename.get(), L"r");
    114 #else
    115  JS::UniqueChars narrowFilename = JS::EncodeUtf8ToNarrow(cx, filename);
    116  if (!narrowFilename) {
    117    return nullptr;
    118  }
    119  return fopen(narrowFilename.get(), "r");
    120 #endif
    121 }
    122 
    123 /*
    124 * Open a source file for reading. Supports "-" and nullptr to mean stdin. The
    125 * return value must be fclosed unless it is stdin.
    126 */
    127 bool js::AutoFile::open(JSContext* cx, const char* filename) {
    128  if (!filename || std::strcmp(filename, "-") == 0) {
    129    fp_ = stdin;
    130  } else {
    131 #ifdef __wasi__
    132    js::Vector<char> normalized(cx);
    133    if (!NormalizeWASIPath(filename, &normalized, cx)) {
    134      return false;
    135    }
    136    fp_ = OpenFile(cx, normalized.begin());
    137 #else
    138    fp_ = OpenFile(cx, filename);
    139 #endif
    140    if (!fp_) {
    141      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_CANT_OPEN,
    142                               filename, "No such file or directory");
    143      return false;
    144    }
    145  }
    146  return true;
    147 }