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