md5sum.cc 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // Md5sum implementation for Android. In gzip mode, takes in a list of files,
  5. // and outputs a list of Md5sums in the same order. Otherwise,
  6. #include <stddef.h>
  7. #include <dirent.h>
  8. #include <fstream>
  9. #include <iostream>
  10. #include <memory>
  11. #include <set>
  12. #include <string>
  13. #include "base/base64.h"
  14. #include "base/hash/md5.h"
  15. #include "third_party/zlib/google/compression_utils_portable.h"
  16. namespace {
  17. // Only used in the gzip mode.
  18. const char kFilePathDelimiter = ';';
  19. const int kMD5HashLength = 16;
  20. // Returns whether |path|'s MD5 was successfully written to |digest_string|.
  21. bool MD5Sum(const std::string& path, std::string* digest_string) {
  22. FILE* fd = fopen(path.c_str(), "rb");
  23. if (!fd) {
  24. std::cerr << "Could not open file " << path << std::endl;
  25. return false;
  26. }
  27. base::MD5Context ctx;
  28. base::MD5Init(&ctx);
  29. const size_t kBufferSize = 1 << 16;
  30. std::unique_ptr<char[]> buf(new char[kBufferSize]);
  31. size_t len;
  32. while ((len = fread(buf.get(), 1, kBufferSize, fd)) > 0)
  33. base::MD5Update(&ctx, base::StringPiece(buf.get(), len));
  34. if (ferror(fd)) {
  35. std::cerr << "Error reading file " << path << std::endl;
  36. return false;
  37. }
  38. fclose(fd);
  39. base::MD5Digest digest;
  40. base::MD5Final(&digest, &ctx);
  41. *digest_string = base::MD5DigestToBase16(digest);
  42. return true;
  43. }
  44. void MakeFileSetHelper(const std::string& path,
  45. std::set<std::string>& file_set) {
  46. DIR* dir = opendir(path.c_str());
  47. if (!dir) {
  48. file_set.insert(path);
  49. return;
  50. }
  51. dirent* entry;
  52. while ((entry = readdir(dir))) {
  53. if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
  54. continue;
  55. MakeFileSetHelper(path + '/' + entry->d_name, file_set);
  56. }
  57. closedir(dir);
  58. }
  59. // Returns the set of all files contained in |files|. This handles directories
  60. // by walking them recursively. Excludes, .svn directories and file under them.
  61. std::vector<std::string> MakeFileSet(const char** files) {
  62. std::set<std::string> file_set;
  63. for (const char** file = files; *file; ++file) {
  64. MakeFileSetHelper(*file, file_set);
  65. }
  66. return std::vector<std::string>(file_set.begin(), file_set.end());
  67. }
  68. std::vector<std::string> StringSplit(const std::string& str, char delim) {
  69. std::vector<std::string> ret;
  70. size_t found_idx = str.find(delim);
  71. size_t start_idx = 0;
  72. while (found_idx != std::string::npos) {
  73. ret.push_back(str.substr(start_idx, found_idx - start_idx));
  74. start_idx = found_idx + 1;
  75. found_idx = str.find(delim, start_idx);
  76. }
  77. ret.push_back(str.substr(start_idx, std::string::npos));
  78. return ret;
  79. }
  80. std::vector<std::string> MakeFileListFromCompressedList(const char* data) {
  81. std::string gzipdata;
  82. // Expected compressed input is using Base64 encoding, we got convert it
  83. // to a regular string before passing it to zlib.
  84. base::Base64Decode(base::StringPiece(data), &gzipdata);
  85. size_t compressed_size = gzipdata.size();
  86. unsigned long decompressed_size = zlib_internal::GetGzipUncompressedSize(
  87. reinterpret_cast<const Bytef*>(gzipdata.c_str()), compressed_size);
  88. std::string decompressed(decompressed_size, '#');
  89. // We can skip an extraneous copy by relying on a C++11 std::string guarantee
  90. // of contiguous memory access to a string.
  91. zlib_internal::UncompressHelper(
  92. zlib_internal::WrapperType::GZIP,
  93. reinterpret_cast<unsigned char*>(&decompressed[0]), &decompressed_size,
  94. reinterpret_cast<const unsigned char*>(gzipdata.c_str()),
  95. compressed_size);
  96. return StringSplit(decompressed, kFilePathDelimiter);
  97. }
  98. } // namespace
  99. int main(int argc, const char* argv[]) {
  100. bool gzip_mode = argc >= 2 && strcmp("-gz", argv[1]) == 0;
  101. if (argc < 2 || (gzip_mode && argc < 3)) {
  102. std::cerr << "Usage: md5sum <path/to/file_or_dir>... or md5sum "
  103. << "-gz base64-gzipped-'" << kFilePathDelimiter
  104. << "'-separated-files" << std::endl;
  105. return 1;
  106. }
  107. std::vector<std::string> files;
  108. if (gzip_mode) {
  109. files = MakeFileListFromCompressedList(argv[2]);
  110. } else {
  111. files = MakeFileSet(argv + 1);
  112. }
  113. bool failed = false;
  114. std::string digest;
  115. for (const auto& file : files) {
  116. if (!MD5Sum(file, &digest)) {
  117. failed = true;
  118. continue;
  119. }
  120. if (gzip_mode) {
  121. std::cout << digest.substr(0, kMD5HashLength) << std::endl;
  122. } else {
  123. std::cout << digest << " " << file << std::endl;
  124. }
  125. }
  126. return failed;
  127. }