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 }