7 #include <rpm/rpmfileutil.h> /* for rpmCleanPath */
9 #include "lib/rpmdb_internal.h"
10 #include "lib/rpmfi_internal.h"
11 #include "lib/fprint.h"
12 #include "lib/rpmhash.h"
16 /* Create new hash table type rpmFpEntryHash */
17 #include "lib/rpmhash.C"
22 #define HASHTYPE rpmFpEntryHash
23 #define HTKEYTYPE const char *
24 #define HTDATATYPE const struct fprintCacheEntry_s *
25 #include "lib/rpmhash.C"
27 fingerPrintCache fpCacheCreate(int sizeHint)
31 fpc = xmalloc(sizeof(*fpc));
32 fpc->ht = rpmFpEntryHashCreate(sizeHint, hashFunctionString, strcmp,
33 (rpmFpEntryHashFreeKey)free,
34 (rpmFpEntryHashFreeData)free);
38 fingerPrintCache fpCacheFree(fingerPrintCache cache)
41 cache->ht = rpmFpEntryHashFree(cache->ht);
48 * Find directory name entry in cache.
49 * @param cache pointer to fingerprint cache
50 * @param dirName string to locate in cache
51 * @return pointer to directory name entry (or NULL if not found).
53 static const struct fprintCacheEntry_s * cacheContainsDirectory(
54 fingerPrintCache cache,
57 const struct fprintCacheEntry_s ** data;
59 if (rpmFpEntryHashGetEntry(cache->ht, dirName, &data, NULL, NULL))
65 * Return finger print of a file path.
66 * @param cache pointer to fingerprint cache
67 * @param dirName leading directory name of path
68 * @param baseName file name of path
70 * @return pointer to the finger print associated with a file path.
72 fingerPrint fpLookup(fingerPrintCache cache,
73 const char * dirName, const char * baseName, int scareMemory)
76 const char * cleanDirName;
78 char * end; /* points to the '\0' at the end of "buf" */
83 const struct fprintCacheEntry_s * cacheHit;
85 /* assert(*dirName == '/' || !scareMemory); */
87 /* XXX WATCHOUT: fp.subDir is set below from relocated dirName arg */
88 cleanDirName = dirName;
89 cdnl = strlen(cleanDirName);
91 if (*cleanDirName == '/') {
93 cdnbuf = xstrdup(dirName);
94 char trailingslash = (cdnbuf[strlen(cdnbuf)-1] == '/');
95 cdnbuf = rpmCleanPath(cdnbuf);
97 cdnbuf = rstrcat(&cdnbuf, "/");
99 cleanDirName = cdnbuf;
100 cdnl = strlen(cleanDirName);
103 scareMemory = 0; /* XXX causes memory leak */
105 /* Using realpath on the arg isn't correct if the arg is a symlink,
106 * especially if the symlink is a dangling link. What we
107 * do instead is use realpath() on `.' and then append arg to
111 /* if the current directory doesn't exist, we might fail.
112 oh well. likewise if it's too long. */
114 if (realpath(".", dir) != NULL) {
115 end = dir + strlen(dir);
116 if (end[-1] != '/') *end++ = '/';
117 end = stpncpy(end, cleanDirName, sizeof(dir) - (end - dir));
119 (void)rpmCleanPath(dir); /* XXX possible /../ from concatenation */
120 end = dir + strlen(dir);
121 if (end[-1] != '/') *end++ = '/';
130 if (cleanDirName == NULL) goto exit; /* XXX can't happen */
132 buf = xstrdup(cleanDirName);
135 /* no need to pay attention to that extra little / at the end of dirName */
136 if (buf[1] && end[-1] == '/') {
143 /* as we're stating paths here, we want to follow symlinks */
145 cacheHit = cacheContainsDirectory(cache, (*buf != '\0' ? buf : "/"));
146 if (cacheHit != NULL) {
148 } else if (!stat((*buf != '\0' ? buf : "/"), &sb)) {
149 struct fprintCacheEntry_s * newEntry = xmalloc(sizeof(* newEntry));
151 newEntry->ino = sb.st_ino;
152 newEntry->dev = sb.st_dev;
153 newEntry->dirName = xstrdup((*buf != '\0' ? buf : "/"));
156 rpmFpEntryHashAddEntry(cache->ht, newEntry->dirName, fp.entry);
160 fp.subDir = cleanDirName + (end - buf);
161 if (fp.subDir[0] == '/' && fp.subDir[1] != '\0')
163 if (fp.subDir[0] == '\0' ||
164 /* XXX don't bother saving '/' as subdir */
165 (fp.subDir[0] == '/' && fp.subDir[1] == '\0'))
167 fp.baseName = baseName;
168 if (!scareMemory && fp.subDir != NULL)
169 fp.subDir = xstrdup(fp.subDir);
170 /* FIX: fp.entry.{dirName,dev,ino} undef @*/
174 /* stat of '/' just failed! */
179 while ((end > buf) && *end != '/') end--;
180 if (end == buf) /* back to stat'ing just '/' */
189 /* FIX: fp.entry.{dirName,dev,ino} undef @*/
193 unsigned int fpHashFunction(const fingerPrint * fp)
195 unsigned int hash = 0;
198 hash = hashFunctionString(fp->baseName);
199 if (fp->subDir) hash ^= hashFunctionString(fp->subDir);
201 hash ^= ((unsigned)fp->entry->dev);
202 for (j=0; j<4; j++) hash ^= ((fp->entry->ino >> (8*j)) & 0xFF) << ((3-j)*8);
207 int fpEqual(const fingerPrint * k1, const fingerPrint * k2)
209 /* If the addresses are the same, so are the values. */
213 /* Otherwise, compare fingerprints by value. */
214 if (FP_EQUAL(*k1, *k2))
220 void fpLookupList(fingerPrintCache cache, const char ** dirNames,
221 const char ** baseNames, const uint32_t * dirIndexes,
222 int fileCount, fingerPrint * fpList)
226 for (i = 0; i < fileCount; i++) {
227 /* If this is in the same directory as the last file, don't bother
228 redoing all of this work */
229 if (i > 0 && dirIndexes[i - 1] == dirIndexes[i]) {
230 fpList[i].entry = fpList[i - 1].entry;
231 fpList[i].subDir = fpList[i - 1].subDir;
232 fpList[i].baseName = baseNames[i];
234 fpList[i] = fpLookup(cache, dirNames[dirIndexes[i]], baseNames[i],
240 void fpLookupSubdir(rpmFpHash symlinks, rpmFpHash fphash, fingerPrintCache fpc, rpmte p, int filenr)
242 rpmfi fi = rpmteFI(p);
243 struct fingerPrint_s current_fp;
244 char *endsubdir, *endbasename, *currentsubdir;
247 struct rpmffi_s * recs;
250 fingerPrint *fp = rpmfiFpsIndex(fi, filenr);
251 int symlinkcount = 0;
252 struct rpmffi_s ffi = { p, filenr};
254 if (fp->subDir == NULL) {
255 rpmFpHashAddEntry(fphash, fp, ffi);
259 lensubDir = strlen(fp->subDir);
261 currentsubdir = xstrdup(fp->subDir);
263 /* Set baseName to the upper most dir */
264 current_fp.baseName = endbasename = currentsubdir;
265 while (*endbasename != '/' && endbasename < currentsubdir + lensubDir - 1)
269 current_fp.subDir = endsubdir = NULL; // no subDir for now
271 while (endbasename < currentsubdir + lensubDir - 1) {
275 rpmFpHashGetEntry(symlinks, ¤t_fp,
276 &recs, &numRecs, NULL);
278 for (i=0; i<numRecs; i++) {
281 char const *linktarget;
284 foundfi = rpmteFI(recs[i].p);
285 fiFX = rpmfiFX(foundfi);
287 filenr = recs[i].fileno;
288 rpmfiSetFX(foundfi, filenr);
289 linktarget = rpmfiFLink(foundfi);
291 if (linktarget && *linktarget != '\0') {
292 /* this "directory" is a symlink */
294 if (*linktarget != '/') {
295 rstrscat(&link, current_fp.entry->dirName,
296 current_fp.subDir ? "/" : "",
297 current_fp.subDir ? current_fp.subDir : "",
300 rstrscat(&link, linktarget, "/", NULL);
301 if (strlen(endbasename+1)) {
302 rstrscat(&link, endbasename+1, "/", NULL);
305 *fp = fpLookup(fpc, link, fp->baseName, 0);
311 /* setup current_fp for the new path */
314 if (fp->subDir == NULL) {
315 /* directory exists - no need to look for symlinks */
316 rpmFpHashAddEntry(fphash, fp, ffi);
319 lensubDir = strlen(fp->subDir);
320 currentsubdir = xstrdup(fp->subDir);
321 current_fp.subDir = endsubdir = NULL; // no subDir for now
323 /* Set baseName to the upper most dir */
324 current_fp.baseName = currentsubdir;
325 endbasename = currentsubdir;
326 while (*endbasename != '/' &&
327 endbasename < currentsubdir + lensubDir - 1)
333 rpmfiSetFX(foundfi, fiFX);
335 if (symlinkcount>50) {
336 // found too many symlinks in the path
337 // most likley a symlink cicle
339 // TODO warning/error
343 continue; // restart loop after symlink
346 if (current_fp.subDir == NULL) {
347 /* after first round set former baseName as subDir */
348 current_fp.subDir = currentsubdir;
350 *endsubdir = '/'; // rejoin the former baseName with subDir
352 endsubdir = endbasename;
354 /* set baseName to the next lower dir */
356 while (*endbasename != '\0' && *endbasename != '/')
359 current_fp.baseName = endsubdir+1;
363 rpmFpHashAddEntry(fphash, fp, ffi);