* Refactoring: hash class.

This commit is contained in:
Eelco Dolstra 2003-06-15 13:41:32 +00:00
parent f66055fa1e
commit 21fe717ce2
8 changed files with 266 additions and 184 deletions

View file

@ -1,13 +1,16 @@
bin_PROGRAMS = nix fix
noinst_PROGRAMS = test
AM_CXXFLAGS = -DSYSTEM=\"@host@\" -Wall
nix_SOURCES = nix.cc db.cc util.cc md5.c
nix_SOURCES = nix.cc db.cc util.cc hash.cc md5.c
nix_LDADD = -ldb_cxx-4 -lATerm
fix_SOURCES = fix.cc util.cc md5.c
fix_SOURCES = fix.cc util.cc hash.cc md5.c
fix_LDADD = -lATerm
test_SOURCES = test.cc util.cc hash.cc md5.c
install-data-local:
$(INSTALL) -d $(localstatedir)/nix
$(INSTALL) -d $(localstatedir)/nix/descriptors

View file

@ -10,6 +10,7 @@ extern "C" {
}
#include "util.hh"
#include "hash.hh"
static string nixDescriptorDir;
@ -20,7 +21,7 @@ static bool verbose = false;
/* Mapping of Fix file names to the hashes of the resulting Nix
descriptors. */
typedef map<string, string> DescriptorMap;
typedef map<string, Hash> DescriptorMap;
void registerFile(string filename)
@ -32,12 +33,13 @@ void registerFile(string filename)
}
void registerURL(string hash, string url)
void registerURL(Hash hash, string url)
{
int res = system(("nix regurl " + hash + " " + url).c_str());
int res = system(("nix regurl " + (string) hash + " " + url).c_str());
/* !!! escape */
if (WEXITSTATUS(res) != 0)
throw Error("cannot register " + hash + " -> " + url + " with Nix");
throw Error("cannot register " +
(string) hash + " -> " + url + " with Nix");
}
@ -61,7 +63,7 @@ struct EvalContext
ATerm evaluate(ATerm e, EvalContext ctx);
string instantiateDescriptor(string filename, EvalContext ctx);
Hash instantiateDescriptor(string filename, EvalContext ctx);
string evaluateStr(ATerm e, EvalContext ctx)
@ -101,7 +103,7 @@ ATerm evaluate(ATerm e, EvalContext ctx)
ATmatch(e, "Pkg(<str>)", &s) ||
ATmatch(e, "File(<str>)", &s))
{
checkHash(s);
parseHash(s);
return e;
}
@ -131,7 +133,7 @@ ATerm evaluate(ATerm e, EvalContext ctx)
else if (ATmatch(e, "Fix(<term>)", &e2)) {
string filename = absPath(evaluateStr(e2, ctx), ctx.dir); /* !!! */
return ATmake("Pkg(<str>)",
instantiateDescriptor(filename, ctx).c_str());
((string) instantiateDescriptor(filename, ctx)).c_str());
}
#if 0
@ -160,19 +162,18 @@ ATerm evaluate(ATerm e, EvalContext ctx)
hash. */
else if (ATmatch(e, "Local(<term>)", &e2)) {
string filename = absPath(evaluateStr(e2, ctx), ctx.dir); /* !!! */
string hash = hashFile(filename);
Hash hash = hashFile(filename);
registerFile(filename); /* !!! */
return ATmake("File(<str>)", hash.c_str());
return ATmake("File(<str>)", ((string) hash).c_str());
}
/* `Url' registers a mapping from a hash to an url with Nix, and
returns the hash. */
else if (ATmatch(e, "Url(<term>, <term>)", &e2, &e3)) {
string hash = evaluateStr(e2, ctx);
checkHash(hash);
Hash hash = parseHash(evaluateStr(e2, ctx));
string url = evaluateStr(e3, ctx);
registerURL(hash, url);
return ATmake("File(<str>)", hash.c_str());
return ATmake("File(<str>)", ((string) hash).c_str());
}
/* `If' provides conditional evaluation. */
@ -199,7 +200,7 @@ string getStringFromMap(BindingsMap & bindingsMap,
/* Instantiate a Fix descriptors into a Nix descriptor, recursively
instantiating referenced descriptors as well. */
string instantiateDescriptor(string filename, EvalContext ctx)
Hash instantiateDescriptor(string filename, EvalContext ctx)
{
/* Already done? */
DescriptorMap::iterator isInMap = ctx.done->find(filename);
@ -256,8 +257,9 @@ string instantiateDescriptor(string filename, EvalContext ctx)
if (!ATwriteToNamedTextFile(outTerm, tmpFilename.c_str()))
throw Error("cannot write aterm to " + tmpFilename);
string outHash = hashFile(tmpFilename);
string outFilename = nixDescriptorDir + "/" + id + "-" + outHash + ".nix";
Hash outHash = hashFile(tmpFilename);
string outFilename = nixDescriptorDir + "/" +
id + "-" + (string) outHash + ".nix";
if (rename(tmpFilename.c_str(), outFilename.c_str()))
throw Error("cannot rename " + tmpFilename + " to " + outFilename);
@ -265,7 +267,8 @@ string instantiateDescriptor(string filename, EvalContext ctx)
registerFile(outFilename);
if (verbose)
cerr << "instantiated " << outHash << " from " << filename << endl;
cerr << "instantiated " << (string) outHash
<< " from " << filename << endl;
(*ctx.done)[filename] = outHash;
return outHash;
@ -284,7 +287,7 @@ void instantiateDescriptors(Strings filenames)
it != filenames.end(); it++)
{
string filename = absPath(*it);
cout << instantiateDescriptor(filename, ctx) << endl;
cout << (string) instantiateDescriptor(filename, ctx) << endl;
}
}
@ -293,7 +296,7 @@ void instantiateDescriptors(Strings filenames)
void printUsage()
{
cerr <<
"Usage: fix ...
"Usage: fix ...\n\
";
}

87
src/hash.cc Normal file
View file

@ -0,0 +1,87 @@
extern "C" {
#include "md5.h"
}
#include "hash.hh"
#include <iostream>
/* Create a zeroed hash object. */
Hash::Hash()
{
memset(hash, 0, sizeof(hash));
}
/* Check whether two hash are equal. */
bool Hash::operator == (Hash & h2)
{
for (unsigned int i = 0; i < hashSize; i++)
if (hash[i] != h2.hash[i]) return false;
return true;
}
/* Check whether two hash are not equal. */
bool Hash::operator != (Hash & h2)
{
return !(*this == h2);
}
/* Convert a hash code into a hexadecimal representation. */
Hash::operator string() const
{
ostringstream str;
for (unsigned int i = 0; i < hashSize; i++) {
str.fill('0');
str.width(2);
str << hex << (int) hash[i];
}
return str.str();
}
/* Parse a hexadecimal representation of a hash code. */
Hash parseHash(const string & s)
{
Hash hash;
for (unsigned int i = 0; i < Hash::hashSize; i++) {
string s2(s, i * 2, 2);
if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
throw BadRefError("invalid hash: " + s);
istringstream str(s2);
int n;
str >> hex >> n;
hash.hash[i] = n;
}
return hash;
}
/* Verify that a reference is valid (that is, is a MD5 hash code). */
bool isHash(const string & s)
{
if (s.length() != 32) return false;
for (int i = 0; i < 32; i++) {
char c = s[i];
if (!((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f')))
return false;
}
return true;
}
/* Compute the MD5 hash of a file. */
Hash hashFile(const string & fileName)
{
Hash hash;
FILE * file = fopen(fileName.c_str(), "rb");
if (!file)
throw Error("file `" + fileName + "' does not exist");
int err = md5_stream(file, hash.hash);
fclose(file);
if (err) throw Error("cannot hash file");
return hash;
}

34
src/hash.hh Normal file
View file

@ -0,0 +1,34 @@
#ifndef __HASH_H
#define __HASH_H
#include <string>
#include "util.hh"
using namespace std;
struct Hash
{
static const unsigned int hashSize = 16;
unsigned char hash[hashSize];
Hash();
bool operator == (Hash & h2);
bool operator != (Hash & h2);
operator string() const;
};
class BadRefError : public Error
{
public:
BadRefError(string _err) : Error(_err) { };
};
Hash parseHash(const string & s);
bool isHash(const string & s);
Hash hashFile(const string & fileName);
#endif /* !__HASH_H */

View file

@ -16,6 +16,7 @@ extern "C" {
}
#include "util.hh"
#include "hash.hh"
#include "db.hh"
using namespace std;
@ -58,7 +59,7 @@ string fetchURL(string url)
database), we use that. Otherwise, we attempt to fetch it from the
network (using dbNetSources). We verify that the file has the
right hash. */
string getFile(string hash)
string getFile(Hash hash)
{
bool checkedNet = false;
@ -77,10 +78,10 @@ string getFile(string hash)
if (checkedNet)
throw Error("consistency problem: file fetched from " + url +
" should have hash " + hash + ", but it doesn't");
" should have hash " + (string) hash + ", but it doesn't");
if (!queryDB(nixDB, dbNetSources, hash, url))
throw Error("a file with hash " + hash + " is requested, "
throw Error("a file with hash " + (string) hash + " is requested, "
"but it is not known to exist locally or on the network");
checkedNet = true;
@ -95,7 +96,7 @@ string getFile(string hash)
typedef map<string, string> Params;
void readPkgDescr(const string & hash,
void readPkgDescr(Hash hash,
Params & pkgImports, Params & fileImports, Params & arguments)
{
string pkgfile;
@ -112,15 +113,15 @@ void readPkgDescr(const string & hash,
char * cname;
ATerm value;
while (ATmatch(bindings, "[Bind(<str>, <term>), <list>]",
&cname, &value, &bindings))
&cname, &value, &bindings))
{
string name(cname);
char * arg;
if (ATmatch(value, "Pkg(<str>)", &arg)) {
checkHash(arg);
parseHash(arg);
pkgImports[name] = arg;
} else if (ATmatch(value, "File(<str>)", &arg)) {
checkHash(arg);
parseHash(arg);
fileImports[name] = arg;
} else if (ATmatch(value, "Str(<str>)", &arg))
arguments[name] = arg;
@ -136,13 +137,13 @@ void readPkgDescr(const string & hash,
}
string getPkg(string hash);
string getPkg(Hash hash);
typedef map<string, string> Environment;
void fetchDeps(string hash, Environment & env)
void fetchDeps(Hash hash, Environment & env)
{
/* Read the package description file. */
Params pkgImports, fileImports, arguments;
@ -156,7 +157,7 @@ void fetchDeps(string hash, Environment & env)
cerr << "fetching package dependency "
<< it->first << " <- " << it->second
<< endl;
env[it->first] = getPkg(it->second);
env[it->first] = getPkg(parseHash(it->second));
}
for (Params::iterator it = fileImports.begin();
@ -168,7 +169,7 @@ void fetchDeps(string hash, Environment & env)
string file;
file = getFile(it->second);
file = getFile(parseHash(it->second));
env[it->first] = file;
}
@ -198,7 +199,7 @@ string getFromEnv(const Environment & env, const string & key)
}
string queryPkgId(const string & hash)
string queryPkgId(Hash hash)
{
Params pkgImports, fileImports, arguments;
readPkgDescr(hash, pkgImports, fileImports, arguments);
@ -206,7 +207,7 @@ string queryPkgId(const string & hash)
}
void installPkg(string hash)
void installPkg(Hash hash)
{
string pkgfile;
string src;
@ -223,14 +224,15 @@ void installPkg(string hash)
string id = getFromEnv(env, "id");
/* Construct a path for the installed package. */
path = nixHomeDir + "/pkg/" + id + "-" + hash;
path = nixHomeDir + "/pkg/" + id + "-" + (string) hash;
/* Create the path. */
if (mkdir(path.c_str(), 0777))
throw Error("unable to create directory " + path);
/* Create a log file. */
string logFileName = nixLogDir + "/" + id + "-" + hash + ".log";
string logFileName =
nixLogDir + "/" + id + "-" + (string) hash + ".log";
/* !!! auto-pclose on exit */
FILE * logFile = popen(("tee " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */
if (!logFile)
@ -256,11 +258,11 @@ void installPkg(string hash)
}
/* Try to use a prebuilt. */
string prebuiltHash, prebuiltFile;
if (queryDB(nixDB, dbPrebuilts, hash, prebuiltHash)) {
string prebuiltHashS, prebuiltFile;
if (queryDB(nixDB, dbPrebuilts, hash, prebuiltHashS)) {
try {
prebuiltFile = getFile(prebuiltHash);
prebuiltFile = getFile(parseHash(prebuiltHashS));
} catch (Error e) {
cerr << "cannot obtain prebuilt (ignoring): " << e.what() << endl;
goto build;
@ -339,17 +341,16 @@ void installPkg(string hash)
}
string getPkg(string hash)
string getPkg(Hash hash)
{
string path;
checkHash(hash);
while (!queryDB(nixDB, dbInstPkgs, hash, path))
installPkg(hash);
return path;
}
void runPkg(string hash,
void runPkg(Hash hash,
Strings::iterator firstArg,
Strings::iterator lastArg)
{
@ -389,7 +390,7 @@ void runPkg(string hash,
}
void ensurePkg(string hash)
void ensurePkg(Hash hash)
{
Params pkgImports, fileImports, arguments;
readPkgDescr(hash, pkgImports, fileImports, arguments);
@ -403,10 +404,9 @@ void ensurePkg(string hash)
}
void delPkg(string hash)
void delPkg(Hash hash)
{
string path;
checkHash(hash);
if (queryDB(nixDB, dbInstPkgs, hash, path)) {
int res = system(("chmod -R +w " + path + " && rm -rf " + path).c_str()); // !!! escaping
delDB(nixDB, dbInstPkgs, hash); // not a bug ???
@ -423,7 +423,7 @@ void exportPkgs(string outDir,
outDir = absPath(outDir);
for (Strings::iterator it = firstHash; it != lastHash; it++) {
string hash = *it;
Hash hash = parseHash(*it);
string pkgDir = getPkg(hash);
string tmpFile = outDir + "/export_tmp";
@ -435,42 +435,38 @@ void exportPkgs(string outDir,
string prebuiltHash = hashFile(tmpFile);
string pkgId = queryPkgId(hash);
string prebuiltFile = outDir + "/" +
pkgId + "-" + hash + "-" + prebuiltHash + ".tar.bz2";
pkgId + "-" + (string) hash + "-" + prebuiltHash + ".tar.bz2";
rename(tmpFile.c_str(), prebuiltFile.c_str());
}
}
void registerPrebuilt(string pkgHash, string prebuiltHash)
void registerPrebuilt(Hash pkgHash, Hash prebuiltHash)
{
checkHash(pkgHash);
checkHash(prebuiltHash);
setDB(nixDB, dbPrebuilts, pkgHash, prebuiltHash);
}
string registerFile(string filename)
Hash registerFile(string filename)
{
filename = absPath(filename);
string hash = hashFile(filename);
Hash hash = hashFile(filename);
setDB(nixDB, dbRefs, hash, filename);
return hash;
}
void registerURL(string hash, string url)
void registerURL(Hash hash, string url)
{
checkHash(hash);
setDB(nixDB, dbNetSources, hash, url);
/* !!! currently we allow only one network source per hash */
}
/* This is primarily used for bootstrapping. */
void registerInstalledPkg(string hash, string path)
void registerInstalledPkg(Hash hash, string path)
{
checkHash(hash);
if (path == "")
delDB(nixDB, dbInstPkgs, hash);
else
@ -498,12 +494,13 @@ void verifyDB()
it != fileRefs.end(); it++)
{
try {
if (hashFile(it->second) != it->first) {
Hash hash = parseHash(it->first);
if (hashFile(it->second) != hash) {
cerr << "file " << it->second << " has changed\n";
delDB(nixDB, dbRefs, it->first);
}
} catch (BadRefError e) { /* !!! better error check */
cerr << "file " << it->second << " has disappeared\n";
} catch (Error e) { /* !!! better error check */
cerr << "error: " << e.what() << endl;
delDB(nixDB, dbRefs, it->first);
}
}
@ -544,7 +541,7 @@ void printInfo(Strings::iterator first, Strings::iterator last)
{
for (Strings::iterator it = first; it != last; it++) {
try {
cout << *it << " " << queryPkgId(*it) << endl;
cout << *it << " " << queryPkgId(parseHash(*it)) << endl;
} catch (Error & e) { // !!! more specific
cout << *it << " (descriptor missing)\n";
}
@ -559,7 +556,7 @@ void computeClosure(Strings::iterator first, Strings::iterator last,
set<string> doneSet;
while (!workList.empty()) {
string hash = workList.front();
Hash hash = parseHash(workList.front());
workList.pop_front();
if (doneSet.find(hash) == doneSet.end()) {
@ -605,7 +602,7 @@ void printGraph(Strings::iterator first, Strings::iterator last)
it != allHashes.end(); it++)
{
Params pkgImports, fileImports, arguments;
readPkgDescr(*it, pkgImports, fileImports, arguments);
readPkgDescr(parseHash(*it), pkgImports, fileImports, arguments);
cout << dotQuote(*it) << "[label = \""
<< getFromEnv(arguments, "id")
@ -633,8 +630,8 @@ void fetch(string id)
}
/* Register it by hash. */
string hash = registerFile(fn);
cout << hash << endl;
Hash hash = registerFile(fn);
cout << (string) hash << endl;
}
@ -648,60 +645,60 @@ void fetch(Strings::iterator first, Strings::iterator last)
void printUsage()
{
cerr <<
"Usage: nix SUBCOMMAND OPTIONS...
Subcommands:
init
Initialize the database.
verify
Remove stale entries from the database.
regfile FILENAME...
Register each FILENAME keyed by its hash.
reginst HASH PATH
Register an installed package.
getpkg HASH...
For each HASH, ensure that the package referenced by HASH is
installed. Print out the path of the installation on stdout.
delpkg HASH...
Uninstall the package referenced by each HASH, disregarding any
dependencies that other packages may have on HASH.
listinst
Prints a list of installed packages.
run HASH ARGS...
Run the descriptor referenced by HASH with the given arguments.
ensure HASH...
Like getpkg, but if HASH refers to a run descriptor, fetch only
the dependencies.
export DIR HASH...
Export installed packages to DIR.
regprebuilt HASH1 HASH2
Inform Nix that an export HASH2 can be used to fast-build HASH1.
info HASH...
Print information about the specified descriptors.
closure HASH...
Determine the closure of the set of descriptors under the import
relation, starting at the given roots.
graph HASH...
Like closure, but print a dot graph specification.
fetch ID...
Fetch the objects identified by ID and place them in the Nix
sources directory. ID can be a hash or URL. Print out the hash
of the object.
"Usage: nix SUBCOMMAND OPTIONS...\n\
\n\
Subcommands:\n\
\n\
init\n\
Initialize the database.\n\
\n\
verify\n\
Remove stale entries from the database.\n\
\n\
regfile FILENAME...\n\
Register each FILENAME keyed by its hash.\n\
\n\
reginst HASH PATH\n\
Register an installed package.\n\
\n\
getpkg HASH...\n\
For each HASH, ensure that the package referenced by HASH is\n\
installed. Print out the path of the installation on stdout.\n\
\n\
delpkg HASH...\n\
Uninstall the package referenced by each HASH, disregarding any\n\
dependencies that other packages may have on HASH.\n\
\n\
listinst\n\
Prints a list of installed packages.\n\
\n\
run HASH ARGS...\n\
Run the descriptor referenced by HASH with the given arguments.\n\
\n\
ensure HASH...\n\
Like getpkg, but if HASH refers to a run descriptor, fetch only\n\
the dependencies.\n\
\n\
export DIR HASH...\n\
Export installed packages to DIR.\n\
\n\
regprebuilt HASH1 HASH2\n\
Inform Nix that an export HASH2 can be used to fast-build HASH1.\n\
\n\
info HASH...\n\
Print information about the specified descriptors.\n\
\n\
closure HASH...\n\
Determine the closure of the set of descriptors under the import\n\
relation, starting at the given roots.\n\
\n\
graph HASH...\n\
Like closure, but print a dot graph specification.\n\
\n\
fetch ID...\n\
Fetch the objects identified by ID and place them in the Nix\n\
sources directory. ID can be a hash or URL. Print out the hash\n\
of the object.\n\
";
}
@ -743,29 +740,31 @@ void run(Strings::iterator argCur, Strings::iterator argEnd)
verifyDB();
} else if (cmd == "getpkg") {
for (Strings::iterator it = argCur; it != argEnd; it++) {
string path = getPkg(*it);
string path = getPkg(parseHash(*it));
cout << path << endl;
}
} else if (cmd == "delpkg") {
for_each(argCur, argEnd, delPkg);
for (Strings::iterator it = argCur; it != argEnd; it++)
delPkg(parseHash(*it));
} else if (cmd == "run") {
if (argc < 1) throw argcError;
runPkg(*argCur, argCur + 1, argEnd);
runPkg(parseHash(*argCur), argCur + 1, argEnd);
} else if (cmd == "ensure") {
for_each(argCur, argEnd, ensurePkg);
for (Strings::iterator it = argCur; it != argEnd; it++)
ensurePkg(parseHash(*it));
} else if (cmd == "export") {
if (argc < 1) throw argcError;
exportPkgs(*argCur, argCur + 1, argEnd);
} else if (cmd == "regprebuilt") {
if (argc != 2) throw argcError;
registerPrebuilt(*argCur, argCur[1]);
registerPrebuilt(parseHash(argCur[0]), parseHash(argCur[1]));
} else if (cmd == "regfile") {
for_each(argCur, argEnd, registerFile);
} else if (cmd == "regurl") {
registerURL(argCur[0], argCur[1]);
registerURL(parseHash(argCur[0]), argCur[1]);
} else if (cmd == "reginst") {
if (argc != 2) throw argcError;
registerInstalledPkg(*argCur, argCur[1]);
registerInstalledPkg(parseHash(argCur[0]), argCur[1]);
} else if (cmd == "listinst") {
if (argc != 0) throw argcError;
listInstalledPkgs();

16
src/test.cc Normal file
View file

@ -0,0 +1,16 @@
#include <iostream>
#include "hash.hh"
int main(int argc, char * * argv)
{
Hash h = hashFile("/etc/passwd");
cout << (string) h << endl;
h = parseHash("0b0ffd0538622bfe20b92c4aa57254d9");
cout << (string) h << endl;
return 0;
}

View file

@ -27,53 +27,6 @@ string absPath(string filename, string dir)
}
static string printHash(unsigned char * buf)
{
ostringstream str;
for (int i = 0; i < 16; i++) {
str.fill('0');
str.width(2);
str << hex << (int) buf[i];
}
return str.str();
}
/* Verify that a reference is valid (that is, is a MD5 hash code). */
bool isHash(const string & s)
{
if (s.length() != 32) return false;
for (int i = 0; i < 32; i++) {
char c = s[i];
if (!((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f')))
return false;
}
return true;
}
void checkHash(const string & s)
{
if (!isHash(s)) throw BadRefError("invalid reference: " + s);
}
/* Compute the MD5 hash of a file. */
string hashFile(string filename)
{
unsigned char hash[16];
FILE * file = fopen(filename.c_str(), "rb");
if (!file)
throw BadRefError("file `" + filename + "' does not exist");
int err = md5_stream(file, hash);
fclose(file);
if (err) throw BadRefError("cannot hash file");
return printHash(hash);
}
/* Return the directory part of the given path, i.e., everything
before the final `/'. */
string dirOf(string s)

View file

@ -7,10 +7,6 @@
#include <unistd.h>
extern "C" {
#include "md5.h"
}
using namespace std;
@ -29,12 +25,6 @@ public:
UsageError(string _err) : Error(_err) { };
};
class BadRefError : public Error
{
public:
BadRefError(string _err) : Error(_err) { };
};
typedef vector<string> Strings;
@ -50,9 +40,6 @@ extern string nixHomeDirEnvVar;
string absPath(string filename, string dir = "");
bool isHash(const string & s);
void checkHash(const string & s);
string hashFile(string filename);
string dirOf(string s);
string baseNameOf(string s);