| 1 | #include "references.hh"
|
| 2 | #include "hash.hh"
|
| 3 | #include "util.hh"
|
| 4 | #include "archive.hh"
|
| 5 |
|
| 6 | #include <map>
|
| 7 | #include <cstdlib>
|
| 8 | #include <cassert>
|
| 9 | #include <format>
|
| 10 |
|
| 11 | namespace nix {
|
| 12 |
|
| 13 |
|
| 14 | static unsigned int refLength = 32;
|
| 15 |
|
| 16 |
|
| 17 | static void search(const unsigned char * s, unsigned int len,
|
| 18 | StringSet & hashes, StringSet & seen)
|
| 19 | {
|
| 20 | for (unsigned int i = 0; i + refLength <= len; ) {
|
| 21 | int j;
|
| 22 | bool match = true;
|
| 23 | for (j = refLength - 1; j >= 0; --j)
|
| 24 | if (base32Values[(unsigned char) s[i + j]] == -1) {
|
| 25 | i += j + 1;
|
| 26 | match = false;
|
| 27 | break;
|
| 28 | }
|
| 29 | if (!match) continue;
|
| 30 | string ref((const char *) s + i, refLength);
|
| 31 | if (hashes.find(ref) != hashes.end()) {
|
| 32 | debug(std::format("found reference to `{}' at offset `{}'", ref, i));
|
| 33 | seen.insert(ref);
|
| 34 | hashes.erase(ref);
|
| 35 | }
|
| 36 | ++i;
|
| 37 | }
|
| 38 | }
|
| 39 |
|
| 40 |
|
| 41 | struct RefScanSink : Sink
|
| 42 | {
|
| 43 | HashSink hashSink;
|
| 44 | StringSet hashes;
|
| 45 | StringSet seen;
|
| 46 |
|
| 47 | string tail;
|
| 48 |
|
| 49 | RefScanSink() : hashSink(htSHA256) { }
|
| 50 |
|
| 51 | void operator () (const unsigned char * data, size_t len);
|
| 52 | };
|
| 53 |
|
| 54 |
|
| 55 | void RefScanSink::operator () (const unsigned char * data, size_t len)
|
| 56 | {
|
| 57 | hashSink(data, len);
|
| 58 |
|
| 59 | |
| 60 | fragment, so search in the concatenation of the tail of the
|
| 61 | previous fragment and the start of the current fragment.
|
| 62 | string s = tail + string((const char *) data, len > refLength ? refLength : len);
|
| 63 | search((const unsigned char *) s.data(), s.size(), hashes, seen);
|
| 64 |
|
| 65 | search(data, len, hashes, seen);
|
| 66 |
|
| 67 | unsigned int tailLen = len <= refLength ? len : refLength;
|
| 68 | tail =
|
| 69 | string(tail, tail.size() < refLength - tailLen ? 0 : tail.size() - (refLength - tailLen)) +
|
| 70 | string((const char *) data + len - tailLen, tailLen);
|
| 71 | }
|
| 72 |
|
| 73 |
|
| 74 | PathSet scanForReferences(const string & path,
|
| 75 | const PathSet & refs, HashResult & hash)
|
| 76 | {
|
| 77 | RefScanSink sink;
|
| 78 | std::map<string, Path> backMap;
|
| 79 |
|
| 80 | |
| 81 | hash part of the file name. (This assumes that all references
|
| 82 | have the form `HASH-bla').
|
| 83 | for (const auto& i : refs) {
|
| 84 | string baseName = baseNameOf(i);
|
| 85 | string::size_type pos = baseName.find('-');
|
| 86 | if (pos == string::npos)
|
| 87 | throw Error(std::format("bad reference `{}'", i));
|
| 88 | string s = string(baseName, 0, pos);
|
| 89 | assert(s.size() == refLength);
|
| 90 | assert(backMap.find(s) == backMap.end());
|
| 91 | |
| 92 | sink.hashes.insert(s);
|
| 93 | backMap[s] = i;
|
| 94 | }
|
| 95 |
|
| 96 |
|
| 97 | dumpPath(path, sink);
|
| 98 |
|
| 99 |
|
| 100 | PathSet found;
|
| 101 | for (auto& i : sink.seen) {
|
| 102 | std::map<string, Path>::iterator j;
|
| 103 | if ((j = backMap.find(i)) == backMap.end()) abort();
|
| 104 | found.insert(j->second);
|
| 105 | }
|
| 106 |
|
| 107 | hash = sink.hashSink.finish();
|
| 108 |
|
| 109 | return found;
|
| 110 | }
|
| 111 |
|
| 112 |
|
| 113 | }
|