00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <sys/types.h>
00034 #include <openssl/sha.h>
00035 #include <openssl/md2.h>
00036 #include <openssl/evp.h>
00037 #include <openssl/md5.h>
00038
00039 #include "HashUtil.h"
00040 #include "hash_string.h"
00041 #include "Util.h"
00042 #include "FileUtil.h"
00043 #include "mace-macros.h"
00044
00045 using namespace std;
00046
00047 const sha1& HashUtil::NULL_HASH = HashUtil::nullHash();
00048 const size_t HashUtil::BLOCK_SIZE = 8192;
00049 const size_t HashUtil::MAX_RETRY_COUNT = 7;
00050
00051 sha1& HashUtil::nullHash() {
00052 static sha1 nullHashVal;
00053 char c = 0;
00054 computeSha1(&c, 0, nullHashVal);
00055
00056 return nullHashVal;
00057 }
00058
00059 void HashUtil::computeSha1(const string& buf, sha1& r) {
00060 computeSha1((void*)buf.data(), buf.size(), r);
00061 }
00062
00063 void HashUtil::computeSha1(int buf, sha1& r) {
00064 computeSha1(&buf, sizeof(buf), r);
00065 }
00066
00067 void HashUtil::computeSha1(const void* buf, size_t size, sha1& r) {
00068 char sha[SHA_DIGEST_LENGTH];
00069 SHA1((const unsigned char*)buf, size, (unsigned char*)sha);
00070 r.clear();
00071 r.append(sha, SHA_DIGEST_LENGTH);
00072 }
00073
00074 void HashUtil::computeMD5(const void* buf, size_t size, md5& r) {
00075 char md[MD5_DIGEST_LENGTH];
00076 MD5((const unsigned char*)buf, size, (unsigned char*)md);
00077 r.clear();
00078 r.append(md, MD5_DIGEST_LENGTH);
00079 }
00080
00081 void HashUtil::computeFileSha1(const std::string& filePath, sha1& h, struct stat& sbuf,
00082 bool dostat, bool followlink) throw (FileException) {
00083 computeFileHash(filePath, h, sbuf, dostat, followlink, EVP_sha1());
00084 }
00085
00086 void HashUtil::computeFileMD5(const std::string& filePath, md5& h, struct stat& sbuf,
00087 bool dostat, bool followlink) throw (FileException) {
00088 computeFileHash(filePath, h, sbuf, dostat, followlink, EVP_md5());
00089 }
00090
00091 EVP_MD_CTX* HashUtil::initCTX(const EVP_MD* md) {
00092 EVP_MD_CTX* mdctx = new EVP_MD_CTX();
00093 EVP_MD_CTX_init(mdctx);
00094 EVP_DigestInit_ex(mdctx, md, NULL);
00095 return mdctx;
00096 }
00097
00098 void HashUtil::finalizeCTX(EVP_MD_CTX* mdctx, std::string& r) {
00099 unsigned char rbuf[EVP_MAX_MD_SIZE];
00100 unsigned int rlen;
00101 EVP_DigestFinal_ex(mdctx, rbuf, &rlen);
00102 freeCTX(mdctx);
00103
00104 r.clear();
00105 r.append((const char*)rbuf, rlen);
00106 }
00107
00108 void HashUtil::freeCTX(EVP_MD_CTX* mdctx) {
00109 EVP_MD_CTX_cleanup(mdctx);
00110 delete mdctx;
00111 }
00112
00113 void HashUtil::computeHash(const void* buf, size_t size, std::string& r,
00114 const EVP_MD* md) {
00115 EVP_MD_CTX* c = initCTX(md);
00116 EVP_DigestUpdate(c, buf, size);
00117 finalizeCTX(c, r);
00118 }
00119
00120 HashUtil::Context::Context(const EVP_MD* m) : md(m), ctx(0), finalized(false) {
00121 ctx = initCTX(md);
00122 }
00123
00124 HashUtil::Context::~Context() {
00125 if (ctx) {
00126 freeCTX(ctx);
00127 }
00128 }
00129
00130 const mace::string& HashUtil::Context::getHash() {
00131 ASSERT(finalized);
00132 return hash;
00133 }
00134
00135 void HashUtil::Context::update(const std::string& buf) {
00136 EVP_DigestUpdate(ctx, buf.data(), buf.size());
00137 }
00138
00139 void HashUtil::Context::finalize() {
00140 finalizeCTX(ctx, hash);
00141 ctx = 0;
00142 finalized = true;
00143 }
00144
00145 HashUtil::MD5Context::MD5Context() : Context(EVP_md5()) {
00146 }
00147
00148 HashUtil::Sha1Context::Sha1Context() : Context(EVP_sha1()) {
00149 }
00150
00151 void HashUtil::computeFileHash(const std::string& path, std::string& h,
00152 struct stat& sbuf, bool dostat, bool followlink,
00153 const EVP_MD* md) throw(FileException) {
00154 ADD_SELECTORS("HashUtil::computeFileHash");
00155 assert(md);
00156
00157 if (dostat) {
00158 FileUtil::lstatWithErr(path, sbuf);
00159 }
00160
00161 if (S_ISDIR(sbuf.st_mode)) {
00162 throw BadFileTypeException("cannot compute hash on directory " + path);
00163 }
00164
00165 #ifdef S_ISLNK
00166 if (S_ISLNK(sbuf.st_mode)) {
00167 if (followlink) {
00168 if (dostat) {
00169 FileUtil::statWithErr(path, sbuf);
00170 }
00171 }
00172 else {
00173 std::string s = FileUtil::readlink(path);
00174 computeHash(s.data(), s.size(), h, md);
00175 return;
00176 }
00177 }
00178 #endif
00179
00180 bool retry = false;
00181 size_t count = 0;
00182 int fd = 0;
00183 char buf[BLOCK_SIZE];
00184 size_t len = 0;
00185 EVP_MD_CTX* mdctx = 0;
00186 do {
00187 count++;
00188 if (count > MAX_RETRY_COUNT) {
00189 throw FileException("could not hash file " + path + ": too many errors");
00190 }
00191 try {
00192 fd = FileUtil::open(path);
00193 }
00194 catch (const FileException& e) {
00195 Log::err() << e << Log::endl;
00196 retry = true;
00197 FileUtil::lstatWithErr(path, sbuf);
00198 continue;
00199 }
00200
00201 mdctx = initCTX(md);
00202
00203 int r = FileUtil::read(fd, buf, BLOCK_SIZE);
00204 while (r != 0) {
00205 if (r < 0) {
00206 Log::err() << "HashUtil::computeFileHash: error reading block from "
00207 << path << ": " << Util::getErrorString(errno) << Log::endl;
00208 close(fd);
00209 freeCTX(mdctx);
00210 retry = true;
00211 }
00212 len += r;
00213 EVP_DigestUpdate(mdctx, buf, r);
00214 r = FileUtil::read(fd, buf, BLOCK_SIZE);
00215 }
00216 retry = false;
00217 } while (retry);
00218
00219 close(fd);
00220 finalizeCTX(mdctx, h);
00221 }