7 #include <rpm/rpmfileutil.h> /* for rpmCleanPath */
9 #include "lib/rpmdb_internal.h"
10 #include "lib/rpmfi_internal.h"
11 #include "lib/fprint.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, rstrhash, 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))
64 fingerPrint fpLookup(fingerPrintCache cache,
65 const char * dirName, const char * baseName, int scareMemory)
68 const char * cleanDirName;
70 char * end; /* points to the '\0' at the end of "buf" */
75 const struct fprintCacheEntry_s * cacheHit;
77 /* assert(*dirName == '/' || !scareMemory); */
79 /* XXX WATCHOUT: fp.subDir is set below from relocated dirName arg */
80 cleanDirName = dirName;
81 cdnl = strlen(cleanDirName);
83 if (*cleanDirName == '/') {
85 cdnbuf = xstrdup(dirName);
86 char trailingslash = (cdnbuf[strlen(cdnbuf)-1] == '/');
87 cdnbuf = rpmCleanPath(cdnbuf);
89 cdnbuf = rstrcat(&cdnbuf, "/");
91 cleanDirName = cdnbuf;
92 cdnl = strlen(cleanDirName);
95 scareMemory = 0; /* XXX causes memory leak */
97 /* Using realpath on the arg isn't correct if the arg is a symlink,
98 * especially if the symlink is a dangling link. What we
99 * do instead is use realpath() on `.' and then append arg to
103 /* if the current directory doesn't exist, we might fail.
104 oh well. likewise if it's too long. */
106 if (realpath(".", dir) != NULL) {
107 end = dir + strlen(dir);
108 if (end[-1] != '/') *end++ = '/';
109 end = stpncpy(end, cleanDirName, sizeof(dir) - (end - dir));
111 (void)rpmCleanPath(dir); /* XXX possible /../ from concatenation */
112 end = dir + strlen(dir);
113 if (end[-1] != '/') *end++ = '/';
122 if (cleanDirName == NULL) goto exit; /* XXX can't happen */
124 buf = xstrdup(cleanDirName);
127 /* no need to pay attention to that extra little / at the end of dirName */
128 if (buf[1] && end[-1] == '/') {
135 /* as we're stating paths here, we want to follow symlinks */
137 cacheHit = cacheContainsDirectory(cache, (*buf != '\0' ? buf : "/"));
138 if (cacheHit != NULL) {
140 } else if (!stat((*buf != '\0' ? buf : "/"), &sb)) {
141 struct fprintCacheEntry_s * newEntry = xmalloc(sizeof(* newEntry));
143 newEntry->ino = sb.st_ino;
144 newEntry->dev = sb.st_dev;
145 newEntry->dirName = xstrdup((*buf != '\0' ? buf : "/"));
148 rpmFpEntryHashAddEntry(cache->ht, newEntry->dirName, fp.entry);
152 fp.subDir = cleanDirName + (end - buf);
153 if (fp.subDir[0] == '/' && fp.subDir[1] != '\0')
155 if (fp.subDir[0] == '\0' ||
156 /* XXX don't bother saving '/' as subdir */
157 (fp.subDir[0] == '/' && fp.subDir[1] == '\0'))
159 fp.baseName = baseName;
160 if (!scareMemory && fp.subDir != NULL)
161 fp.subDir = xstrdup(fp.subDir);
162 /* FIX: fp.entry.{dirName,dev,ino} undef @*/
166 /* stat of '/' just failed! */
171 while ((end > buf) && *end != '/') end--;
172 if (end == buf) /* back to stat'ing just '/' */
181 /* FIX: fp.entry.{dirName,dev,ino} undef @*/
185 unsigned int fpHashFunction(const fingerPrint * fp)
187 unsigned int hash = 0;
190 hash = rstrhash(fp->baseName);
191 if (fp->subDir) hash ^= rstrhash(fp->subDir);
193 hash ^= ((unsigned)fp->entry->dev);
194 for (j=0; j<4; j++) hash ^= ((fp->entry->ino >> (8*j)) & 0xFF) << ((3-j)*8);
199 int fpEqual(const fingerPrint * k1, const fingerPrint * k2)
201 /* If the addresses are the same, so are the values. */
205 /* Otherwise, compare fingerprints by value. */
206 if (FP_EQUAL(*k1, *k2))
212 void fpLookupList(fingerPrintCache cache, const char ** dirNames,
213 const char ** baseNames, const uint32_t * dirIndexes,
214 int fileCount, fingerPrint * fpList)
218 for (i = 0; i < fileCount; i++) {
219 /* If this is in the same directory as the last file, don't bother
220 redoing all of this work */
221 if (i > 0 && dirIndexes[i - 1] == dirIndexes[i]) {
222 fpList[i].entry = fpList[i - 1].entry;
223 fpList[i].subDir = fpList[i - 1].subDir;
224 fpList[i].baseName = baseNames[i];
226 fpList[i] = fpLookup(cache, dirNames[dirIndexes[i]], baseNames[i],
232 void fpLookupSubdir(rpmFpHash symlinks, rpmFpHash fphash, fingerPrintCache fpc, rpmte p, int filenr)
234 rpmfi fi = rpmteFI(p);
235 struct fingerPrint_s current_fp;
236 char *endsubdir, *endbasename, *currentsubdir;
239 struct rpmffi_s * recs;
242 fingerPrint *fp = rpmfiFpsIndex(fi, filenr);
243 int symlinkcount = 0;
244 struct rpmffi_s ffi = { p, filenr};
246 if (fp->subDir == NULL) {
247 rpmFpHashAddEntry(fphash, fp, ffi);
251 lensubDir = strlen(fp->subDir);
253 currentsubdir = xstrdup(fp->subDir);
255 /* Set baseName to the upper most dir */
256 current_fp.baseName = endbasename = currentsubdir;
257 while (*endbasename != '/' && endbasename < currentsubdir + lensubDir - 1)
261 current_fp.subDir = endsubdir = NULL; // no subDir for now
263 while (endbasename < currentsubdir + lensubDir - 1) {
267 rpmFpHashGetEntry(symlinks, ¤t_fp,
268 &recs, &numRecs, NULL);
270 for (i=0; i<numRecs; i++) {
273 char const *linktarget;
276 foundfi = rpmteFI(recs[i].p);
277 fiFX = rpmfiFX(foundfi);
279 filenr = recs[i].fileno;
280 rpmfiSetFX(foundfi, filenr);
281 linktarget = rpmfiFLink(foundfi);
283 if (linktarget && *linktarget != '\0') {
284 /* this "directory" is a symlink */
286 if (*linktarget != '/') {
287 rstrscat(&link, current_fp.entry->dirName,
288 current_fp.subDir ? "/" : "",
289 current_fp.subDir ? current_fp.subDir : "",
292 rstrscat(&link, linktarget, "/", NULL);
293 if (strlen(endbasename+1)) {
294 rstrscat(&link, endbasename+1, "/", NULL);
297 *fp = fpLookup(fpc, link, fp->baseName, 0);
303 /* setup current_fp for the new path */
306 if (fp->subDir == NULL) {
307 /* directory exists - no need to look for symlinks */
308 rpmFpHashAddEntry(fphash, fp, ffi);
311 lensubDir = strlen(fp->subDir);
312 currentsubdir = xstrdup(fp->subDir);
313 current_fp.subDir = endsubdir = NULL; // no subDir for now
315 /* Set baseName to the upper most dir */
316 current_fp.baseName = currentsubdir;
317 endbasename = currentsubdir;
318 while (*endbasename != '/' &&
319 endbasename < currentsubdir + lensubDir - 1)
325 rpmfiSetFX(foundfi, fiFX);
327 if (symlinkcount>50) {
328 // found too many symlinks in the path
329 // most likley a symlink cicle
331 // TODO warning/error
335 continue; // restart loop after symlink
338 if (current_fp.subDir == NULL) {
339 /* after first round set former baseName as subDir */
340 current_fp.subDir = currentsubdir;
342 *endsubdir = '/'; // rejoin the former baseName with subDir
344 endsubdir = endbasename;
346 /* set baseName to the next lower dir */
348 while (*endbasename != '\0' && *endbasename != '/')
351 current_fp.baseName = endsubdir+1;
355 rpmFpHashAddEntry(fphash, fp, ffi);