1 : #include "references.hh"
2 : #include "hash.hh"
3 : #include "util.hh"
4 :
5 : #include <cerrno>
6 : #include <map>
7 :
8 : #include <sys/types.h>
9 : #include <sys/stat.h>
10 : #include <unistd.h>
11 : #include <dirent.h>
12 : #include <fcntl.h>
13 :
14 :
15 : namespace nix {
16 :
17 :
18 : static unsigned int refLength = 32; /* characters */
19 :
20 :
21 : static void search(size_t len, const unsigned char * s,
22 : StringSet & ids, StringSet & seen)
23 207 : {
24 207 : static bool initialised = false;
25 207 : static bool isBase32[256];
26 207 : if (!initialised) {
27 23 : for (unsigned int i = 0; i < 256; ++i) isBase32[i] = false;
28 759 : for (unsigned int i = 0; i < base32Chars.size(); ++i)
29 736 : isBase32[(unsigned char) base32Chars[i]] = true;
30 23 : initialised = true;
31 : }
32 :
33 814 : for (unsigned int i = 0; i + refLength <= len; ) {
34 825 : int j;
35 825 : bool match = true;
36 20878 : for (j = refLength - 1; j >= 0; --j)
37 20271 : if (!isBase32[(unsigned char) s[i + j]]) {
38 218 : i += j + 1;
39 218 : match = false;
40 218 : break;
41 : }
42 825 : if (!match) continue;
43 607 : string ref((const char *) s + i, refLength);
44 607 : if (ids.find(ref) != ids.end()) {
45 32 : debug(format("found reference to `%1%' at offset `%2%'")
46 : % ref % i);
47 32 : seen.insert(ref);
48 32 : ids.erase(ref);
49 : }
50 607 : ++i;
51 : }
52 : }
53 :
54 :
55 : void checkPath(const string & path,
56 : StringSet & ids, StringSet & seen)
57 163 : {
58 163 : checkInterrupt();
59 :
60 163 : debug(format("checking `%1%'") % path);
61 :
62 163 : struct stat st;
63 163 : if (lstat(path.c_str(), &st))
64 0 : throw SysError(format("getting attributes of path `%1%'") % path);
65 :
66 163 : if (S_ISDIR(st.st_mode)) {
67 59 : Strings names = readDirectory(path);
68 162 : for (Strings::iterator i = names.begin(); i != names.end(); i++) {
69 103 : search(i->size(), (const unsigned char *) i->c_str(), ids, seen);
70 103 : checkPath(path + "/" + *i, ids, seen);
71 : }
72 : }
73 :
74 104 : else if (S_ISREG(st.st_mode)) {
75 :
76 67 : AutoCloseFD fd = open(path.c_str(), O_RDONLY);
77 67 : if (fd == -1) throw SysError(format("opening file `%1%'") % path);
78 :
79 67 : size_t bufSize = 1024 * 1024;
80 67 : assert(refLength <= bufSize);
81 67 : unsigned char * buf = new unsigned char[bufSize];
82 :
83 67 : size_t left = st.st_size;
84 67 : bool firstBlock = true;
85 :
86 134 : while (left > 0) {
87 67 : checkInterrupt();
88 :
89 67 : size_t read = left > bufSize ? bufSize : left;
90 67 : size_t copiedBytes = 0;
91 :
92 67 : if (!firstBlock) {
93 : /* Move the last (refLength - 1) bytes from the last
94 : block to the start of the buffer to deal with
95 : references that cross block boundaries. */
96 0 : copiedBytes = refLength - 1;
97 0 : if (read + copiedBytes > bufSize)
98 0 : read -= copiedBytes;
99 0 : memcpy(buf, buf + (bufSize - copiedBytes), copiedBytes);
100 : }
101 67 : firstBlock = false;
102 :
103 67 : readFull(fd, buf + copiedBytes, read);
104 67 : left -= read;
105 :
106 67 : search(copiedBytes + read, buf, ids, seen);
107 : }
108 :
109 67 : delete[] buf; /* !!! autodelete */
110 : }
111 :
112 37 : else if (S_ISLNK(st.st_mode)) {
113 37 : string target = readLink(path);
114 37 : search(target.size(), (const unsigned char *) target.c_str(), ids, seen);
115 : }
116 :
117 0 : else throw Error(format("unknown file type: %1%") % path);
118 : }
119 :
120 :
121 : PathSet scanForReferences(const string & path, const PathSet & paths)
122 60 : {
123 60 : std::map<string, Path> backMap;
124 60 : StringSet ids;
125 60 : StringSet seen;
126 :
127 : /* For efficiency (and a higher hit rate), just search for the
128 : hash part of the file name. (This assumes that all references
129 : have the form `HASH-bla'). */
130 245 : for (PathSet::const_iterator i = paths.begin(); i != paths.end(); i++) {
131 185 : string baseName = baseNameOf(*i);
132 185 : string::size_type pos = baseName.find('-');
133 185 : if (pos == string::npos)
134 0 : throw Error(format("bad reference `%1%'") % *i);
135 185 : string s = string(baseName, 0, pos);
136 185 : assert(s.size() == refLength);
137 185 : assert(backMap.find(s) == backMap.end());
138 : // parseHash(htSHA256, s);
139 185 : ids.insert(s);
140 185 : backMap[s] = *i;
141 : }
142 :
143 60 : checkPath(path, ids, seen);
144 :
145 60 : PathSet found;
146 92 : for (StringSet::iterator i = seen.begin(); i != seen.end(); i++) {
147 32 : std::map<string, Path>::iterator j;
148 32 : if ((j = backMap.find(*i)) == backMap.end()) abort();
149 32 : found.insert(j->second);
150 : }
151 :
152 60 : return found;
153 : }
154 :
155 :
156 : }
|