importer.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. // Author: kenton@google.com (Kenton Varda)
  31. // Based on original Protocol Buffers design by
  32. // Sanjay Ghemawat, Jeff Dean, and others.
  33. #ifdef _MSC_VER
  34. #include <direct.h>
  35. #else
  36. #include <unistd.h>
  37. #endif
  38. #include <sys/types.h>
  39. #include <sys/stat.h>
  40. #include <fcntl.h>
  41. #include <errno.h>
  42. #include <algorithm>
  43. #include <memory>
  44. #include <google/protobuf/compiler/importer.h>
  45. #include <google/protobuf/compiler/parser.h>
  46. #include <google/protobuf/io/tokenizer.h>
  47. #include <google/protobuf/io/zero_copy_stream_impl.h>
  48. #include <google/protobuf/stubs/strutil.h>
  49. #include <google/protobuf/stubs/io_win32.h>
  50. #ifdef _WIN32
  51. #include <ctype.h>
  52. #endif
  53. namespace google {
  54. namespace protobuf {
  55. namespace compiler {
  56. #ifdef _WIN32
  57. // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
  58. // them like we do below.
  59. using google::protobuf::internal::win32::access;
  60. using google::protobuf::internal::win32::open;
  61. #endif
  62. // Returns true if the text looks like a Windows-style absolute path, starting
  63. // with a drive letter. Example: "C:\foo". TODO(kenton): Share this with
  64. // copy in command_line_interface.cc?
  65. static bool IsWindowsAbsolutePath(const string& text) {
  66. #if defined(_WIN32) || defined(__CYGWIN__)
  67. return text.size() >= 3 && text[1] == ':' &&
  68. isalpha(text[0]) &&
  69. (text[2] == '/' || text[2] == '\\') &&
  70. text.find_last_of(':') == 1;
  71. #else
  72. return false;
  73. #endif
  74. }
  75. MultiFileErrorCollector::~MultiFileErrorCollector() {}
  76. // This class serves two purposes:
  77. // - It implements the ErrorCollector interface (used by Tokenizer and Parser)
  78. // in terms of MultiFileErrorCollector, using a particular filename.
  79. // - It lets us check if any errors have occurred.
  80. class SourceTreeDescriptorDatabase::SingleFileErrorCollector
  81. : public io::ErrorCollector {
  82. public:
  83. SingleFileErrorCollector(const string& filename,
  84. MultiFileErrorCollector* multi_file_error_collector)
  85. : filename_(filename),
  86. multi_file_error_collector_(multi_file_error_collector),
  87. had_errors_(false) {}
  88. ~SingleFileErrorCollector() {}
  89. bool had_errors() { return had_errors_; }
  90. // implements ErrorCollector ---------------------------------------
  91. void AddError(int line, int column, const string& message) {
  92. if (multi_file_error_collector_ != NULL) {
  93. multi_file_error_collector_->AddError(filename_, line, column, message);
  94. }
  95. had_errors_ = true;
  96. }
  97. private:
  98. string filename_;
  99. MultiFileErrorCollector* multi_file_error_collector_;
  100. bool had_errors_;
  101. };
  102. // ===================================================================
  103. SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase(
  104. SourceTree* source_tree)
  105. : source_tree_(source_tree),
  106. error_collector_(NULL),
  107. using_validation_error_collector_(false),
  108. validation_error_collector_(this) {}
  109. SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {}
  110. bool SourceTreeDescriptorDatabase::FindFileByName(
  111. const string& filename, FileDescriptorProto* output) {
  112. std::unique_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
  113. if (input == NULL) {
  114. if (error_collector_ != NULL) {
  115. error_collector_->AddError(filename, -1, 0,
  116. source_tree_->GetLastErrorMessage());
  117. }
  118. return false;
  119. }
  120. // Set up the tokenizer and parser.
  121. SingleFileErrorCollector file_error_collector(filename, error_collector_);
  122. io::Tokenizer tokenizer(input.get(), &file_error_collector);
  123. Parser parser;
  124. if (error_collector_ != NULL) {
  125. parser.RecordErrorsTo(&file_error_collector);
  126. }
  127. if (using_validation_error_collector_) {
  128. parser.RecordSourceLocationsTo(&source_locations_);
  129. }
  130. // Parse it.
  131. output->set_name(filename);
  132. return parser.Parse(&tokenizer, output) &&
  133. !file_error_collector.had_errors();
  134. }
  135. bool SourceTreeDescriptorDatabase::FindFileContainingSymbol(
  136. const string& symbol_name, FileDescriptorProto* output) {
  137. return false;
  138. }
  139. bool SourceTreeDescriptorDatabase::FindFileContainingExtension(
  140. const string& containing_type, int field_number,
  141. FileDescriptorProto* output) {
  142. return false;
  143. }
  144. // -------------------------------------------------------------------
  145. SourceTreeDescriptorDatabase::ValidationErrorCollector::
  146. ValidationErrorCollector(SourceTreeDescriptorDatabase* owner)
  147. : owner_(owner) {}
  148. SourceTreeDescriptorDatabase::ValidationErrorCollector::
  149. ~ValidationErrorCollector() {}
  150. void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError(
  151. const string& filename,
  152. const string& element_name,
  153. const Message* descriptor,
  154. ErrorLocation location,
  155. const string& message) {
  156. if (owner_->error_collector_ == NULL) return;
  157. int line, column;
  158. owner_->source_locations_.Find(descriptor, location, &line, &column);
  159. owner_->error_collector_->AddError(filename, line, column, message);
  160. }
  161. void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddWarning(
  162. const string& filename,
  163. const string& element_name,
  164. const Message* descriptor,
  165. ErrorLocation location,
  166. const string& message) {
  167. if (owner_->error_collector_ == NULL) return;
  168. int line, column;
  169. owner_->source_locations_.Find(descriptor, location, &line, &column);
  170. owner_->error_collector_->AddWarning(filename, line, column, message);
  171. }
  172. // ===================================================================
  173. Importer::Importer(SourceTree* source_tree,
  174. MultiFileErrorCollector* error_collector)
  175. : database_(source_tree),
  176. pool_(&database_, database_.GetValidationErrorCollector()) {
  177. pool_.EnforceWeakDependencies(true);
  178. database_.RecordErrorsTo(error_collector);
  179. }
  180. Importer::~Importer() {}
  181. const FileDescriptor* Importer::Import(const string& filename) {
  182. return pool_.FindFileByName(filename);
  183. }
  184. void Importer::AddUnusedImportTrackFile(const string& file_name) {
  185. pool_.AddUnusedImportTrackFile(file_name);
  186. }
  187. void Importer::ClearUnusedImportTrackFiles() {
  188. pool_.ClearUnusedImportTrackFiles();
  189. }
  190. // ===================================================================
  191. SourceTree::~SourceTree() {}
  192. string SourceTree::GetLastErrorMessage() {
  193. return "File not found.";
  194. }
  195. DiskSourceTree::DiskSourceTree() {}
  196. DiskSourceTree::~DiskSourceTree() {}
  197. static inline char LastChar(const string& str) {
  198. return str[str.size() - 1];
  199. }
  200. // Given a path, returns an equivalent path with these changes:
  201. // - On Windows, any backslashes are replaced with forward slashes.
  202. // - Any instances of the directory "." are removed.
  203. // - Any consecutive '/'s are collapsed into a single slash.
  204. // Note that the resulting string may be empty.
  205. //
  206. // TODO(kenton): It would be nice to handle "..", e.g. so that we can figure
  207. // out that "foo/bar.proto" is inside "baz/../foo". However, if baz is a
  208. // symlink or doesn't exist, then things get complicated, and we can't
  209. // actually determine this without investigating the filesystem, probably
  210. // in non-portable ways. So, we punt.
  211. //
  212. // TODO(kenton): It would be nice to use realpath() here except that it
  213. // resolves symbolic links. This could cause problems if people place
  214. // symbolic links in their source tree. For example, if you executed:
  215. // protoc --proto_path=foo foo/bar/baz.proto
  216. // then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize
  217. // to a path which does not appear to be under foo, and thus the compiler
  218. // will complain that baz.proto is not inside the --proto_path.
  219. static string CanonicalizePath(string path) {
  220. #ifdef _WIN32
  221. // The Win32 API accepts forward slashes as a path delimiter even though
  222. // backslashes are standard. Let's avoid confusion and use only forward
  223. // slashes.
  224. if (HasPrefixString(path, "\\\\")) {
  225. // Avoid converting two leading backslashes.
  226. path = "\\\\" + StringReplace(path.substr(2), "\\", "/", true);
  227. } else {
  228. path = StringReplace(path, "\\", "/", true);
  229. }
  230. #endif
  231. std::vector<string> canonical_parts;
  232. std::vector<string> parts = Split(
  233. path, "/", true); // Note: Removes empty parts.
  234. for (int i = 0; i < parts.size(); i++) {
  235. if (parts[i] == ".") {
  236. // Ignore.
  237. } else {
  238. canonical_parts.push_back(parts[i]);
  239. }
  240. }
  241. string result = Join(canonical_parts, "/");
  242. if (!path.empty() && path[0] == '/') {
  243. // Restore leading slash.
  244. result = '/' + result;
  245. }
  246. if (!path.empty() && LastChar(path) == '/' &&
  247. !result.empty() && LastChar(result) != '/') {
  248. // Restore trailing slash.
  249. result += '/';
  250. }
  251. return result;
  252. }
  253. static inline bool ContainsParentReference(const string& path) {
  254. return path == ".." || HasPrefixString(path, "../") ||
  255. HasSuffixString(path, "/..") || path.find("/../") != string::npos;
  256. }
  257. // Maps a file from an old location to a new one. Typically, old_prefix is
  258. // a virtual path and new_prefix is its corresponding disk path. Returns
  259. // false if the filename did not start with old_prefix, otherwise replaces
  260. // old_prefix with new_prefix and stores the result in *result. Examples:
  261. // string result;
  262. // assert(ApplyMapping("foo/bar", "", "baz", &result));
  263. // assert(result == "baz/foo/bar");
  264. //
  265. // assert(ApplyMapping("foo/bar", "foo", "baz", &result));
  266. // assert(result == "baz/bar");
  267. //
  268. // assert(ApplyMapping("foo", "foo", "bar", &result));
  269. // assert(result == "bar");
  270. //
  271. // assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
  272. // assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
  273. // assert(!ApplyMapping("foobar", "foo", "baz", &result));
  274. static bool ApplyMapping(const string& filename,
  275. const string& old_prefix,
  276. const string& new_prefix,
  277. string* result) {
  278. if (old_prefix.empty()) {
  279. // old_prefix matches any relative path.
  280. if (ContainsParentReference(filename)) {
  281. // We do not allow the file name to use "..".
  282. return false;
  283. }
  284. if (HasPrefixString(filename, "/") || IsWindowsAbsolutePath(filename)) {
  285. // This is an absolute path, so it isn't matched by the empty string.
  286. return false;
  287. }
  288. result->assign(new_prefix);
  289. if (!result->empty()) result->push_back('/');
  290. result->append(filename);
  291. return true;
  292. } else if (HasPrefixString(filename, old_prefix)) {
  293. // old_prefix is a prefix of the filename. Is it the whole filename?
  294. if (filename.size() == old_prefix.size()) {
  295. // Yep, it's an exact match.
  296. *result = new_prefix;
  297. return true;
  298. } else {
  299. // Not an exact match. Is the next character a '/'? Otherwise,
  300. // this isn't actually a match at all. E.g. the prefix "foo/bar"
  301. // does not match the filename "foo/barbaz".
  302. int after_prefix_start = -1;
  303. if (filename[old_prefix.size()] == '/') {
  304. after_prefix_start = old_prefix.size() + 1;
  305. } else if (filename[old_prefix.size() - 1] == '/') {
  306. // old_prefix is never empty, and canonicalized paths never have
  307. // consecutive '/' characters.
  308. after_prefix_start = old_prefix.size();
  309. }
  310. if (after_prefix_start != -1) {
  311. // Yep. So the prefixes are directories and the filename is a file
  312. // inside them.
  313. string after_prefix = filename.substr(after_prefix_start);
  314. if (ContainsParentReference(after_prefix)) {
  315. // We do not allow the file name to use "..".
  316. return false;
  317. }
  318. result->assign(new_prefix);
  319. if (!result->empty()) result->push_back('/');
  320. result->append(after_prefix);
  321. return true;
  322. }
  323. }
  324. }
  325. return false;
  326. }
  327. void DiskSourceTree::MapPath(const string& virtual_path,
  328. const string& disk_path) {
  329. mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path)));
  330. }
  331. DiskSourceTree::DiskFileToVirtualFileResult
  332. DiskSourceTree::DiskFileToVirtualFile(
  333. const string& disk_file,
  334. string* virtual_file,
  335. string* shadowing_disk_file) {
  336. int mapping_index = -1;
  337. string canonical_disk_file = CanonicalizePath(disk_file);
  338. for (int i = 0; i < mappings_.size(); i++) {
  339. // Apply the mapping in reverse.
  340. if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path,
  341. mappings_[i].virtual_path, virtual_file)) {
  342. // Success.
  343. mapping_index = i;
  344. break;
  345. }
  346. }
  347. if (mapping_index == -1) {
  348. return NO_MAPPING;
  349. }
  350. // Iterate through all mappings with higher precedence and verify that none
  351. // of them map this file to some other existing file.
  352. for (int i = 0; i < mapping_index; i++) {
  353. if (ApplyMapping(*virtual_file, mappings_[i].virtual_path,
  354. mappings_[i].disk_path, shadowing_disk_file)) {
  355. if (access(shadowing_disk_file->c_str(), F_OK) >= 0) {
  356. // File exists.
  357. return SHADOWED;
  358. }
  359. }
  360. }
  361. shadowing_disk_file->clear();
  362. // Verify that we can open the file. Note that this also has the side-effect
  363. // of verifying that we are not canonicalizing away any non-existent
  364. // directories.
  365. std::unique_ptr<io::ZeroCopyInputStream> stream(OpenDiskFile(disk_file));
  366. if (stream == NULL) {
  367. return CANNOT_OPEN;
  368. }
  369. return SUCCESS;
  370. }
  371. bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file,
  372. string* disk_file) {
  373. std::unique_ptr<io::ZeroCopyInputStream> stream(
  374. OpenVirtualFile(virtual_file, disk_file));
  375. return stream != NULL;
  376. }
  377. io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) {
  378. return OpenVirtualFile(filename, NULL);
  379. }
  380. string DiskSourceTree::GetLastErrorMessage() {
  381. return last_error_message_;
  382. }
  383. io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
  384. const string& virtual_file,
  385. string* disk_file) {
  386. if (virtual_file != CanonicalizePath(virtual_file) ||
  387. ContainsParentReference(virtual_file)) {
  388. // We do not allow importing of paths containing things like ".." or
  389. // consecutive slashes since the compiler expects files to be uniquely
  390. // identified by file name.
  391. last_error_message_ = "Backslashes, consecutive slashes, \".\", or \"..\" "
  392. "are not allowed in the virtual path";
  393. return NULL;
  394. }
  395. for (int i = 0; i < mappings_.size(); i++) {
  396. string temp_disk_file;
  397. if (ApplyMapping(virtual_file, mappings_[i].virtual_path,
  398. mappings_[i].disk_path, &temp_disk_file)) {
  399. io::ZeroCopyInputStream* stream = OpenDiskFile(temp_disk_file);
  400. if (stream != NULL) {
  401. if (disk_file != NULL) {
  402. *disk_file = temp_disk_file;
  403. }
  404. return stream;
  405. }
  406. if (errno == EACCES) {
  407. // The file exists but is not readable.
  408. last_error_message_ = "Read access is denied for file: " +
  409. temp_disk_file;
  410. return NULL;
  411. }
  412. }
  413. }
  414. last_error_message_ = "File not found.";
  415. return NULL;
  416. }
  417. io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile(
  418. const string& filename) {
  419. int file_descriptor;
  420. do {
  421. file_descriptor = open(filename.c_str(), O_RDONLY);
  422. } while (file_descriptor < 0 && errno == EINTR);
  423. if (file_descriptor >= 0) {
  424. io::FileInputStream* result = new io::FileInputStream(file_descriptor);
  425. result->SetCloseOnDelete(true);
  426. return result;
  427. } else {
  428. return NULL;
  429. }
  430. }
  431. } // namespace compiler
  432. } // namespace protobuf
  433. } // namespace google