# rpmdb/fprint.c

Go to the documentation of this file.

    00001
    00005 #include "system.h"
    00006
    00007 #include <rpmdb.h>
    00008 #include <rpmmacro.h>   /* XXX for rpmCleanPath */
    00009
    00010 #include "fprint.h"
    00011 #include "debug.h"
    00012
    00013 fingerPrintCache fpCacheCreate(int sizeHint)
    00014 {
    00015     fingerPrintCache fpc;
    00016
    00017     fpc = xmalloc(sizeof(*fpc));
    00018     fpc->ht = htCreate(sizeHint * 2, 0, 1, hashFunctionString,
    00019                        hashEqualityString);
    00020     return fpc;
    00021 }
    00022
    00023 void fpCacheFree(fingerPrintCache cache)
    00024 {
    00025     htFree(cache->ht);
    00026     free(cache);
    00027 }
    00028
    00035 static /*@null@*/ const struct fprintCacheEntry_s * cacheContainsDirectory(
    00036                             fingerPrintCache cache,
    00037                             const char * dirName)
    00038         /*@*/
    00039 {
    00040     const void ** data;
    00041
    00042     if (htGetEntry(cache->ht, dirName, &data, NULL, NULL))
    00043         return NULL;
    00044     return data[0];
    00045 }
    00046
    00055 static fingerPrint doLookup(fingerPrintCache cache,
    00056                 const char * dirName, const char * baseName, int scareMemory)
    00057         /*@modifies cache @*/
    00058 {
    00059     char dir[PATH_MAX];
    00060     const char * cleanDirName;
    00061     size_t cdnl;
    00062     char * end;             /* points to the '\0' at the end of "buf" */
    00063     fingerPrint fp;
    00064     struct stat sb;
    00065     char * buf;
    00066     const struct fprintCacheEntry_s * cacheHit;
    00067
    00068     /* assert(*dirName == '/' || !scareMemory); */
    00069
    00070     /* XXX WATCHOUT: fp.subDir is set below from relocated dirName arg */
    00071     cleanDirName = dirName;
    00072     cdnl = strlen(cleanDirName);
    00073
    00074     if (*cleanDirName == '/') {
    00075         /*@-branchstate@*/
    00076         if (!scareMemory)
    00077             cleanDirName =
    00078                 rpmCleanPath(strcpy(alloca(cdnl+1), dirName));
    00079         /*@=branchstate@*/
    00080     } else {
    00081         scareMemory = 0;        /* XXX causes memory leak */
    00082
    00083         /* Using realpath on the arg isn't correct if the arg is a symlink,
    00084          * especially if the symlink is a dangling link.  What we
    00085          * do instead is use realpath() on `.' and then append arg to
    00086          * the result.
    00087          */
    00088
    00089         /* if the current directory doesn't exist, we might fail.
    00090            oh well. likewise if it's too long.  */
    00091         dir[0] = '\0';
    00092         /*@-branchstate@*/
    00093         if (realpath(".", dir) != NULL) {
    00094             end = dir + strlen(dir);
    00095             if (end[-1] != '/') *end++ = '/';
    00096             end = stpncpy(end, cleanDirName, sizeof(dir) - (end - dir));
    00097             *end = '\0';
    00098             (void)rpmCleanPath(dir); /* XXX possible /../ from concatenation */
    00099             end = dir + strlen(dir);
    00100             if (end[-1] != '/') *end++ = '/';
    00101             *end = '\0';
    00102             cleanDirName = dir;
    00103             cdnl = end - dir;
    00104         }
    00105         /*@=branchstate@*/
    00106     }
    00107     fp.entry = NULL;
    00108     fp.subDir = NULL;
    00109     fp.baseName = NULL;
    00110     /*@-nullret@*/
    00111     if (cleanDirName == NULL) return fp;        /* XXX can't happen */
    00112     /*@=nullret@*/
    00113
    00114     buf = strcpy(alloca(cdnl + 1), cleanDirName);
    00115     end = buf + cdnl;
    00116
    00117     /* no need to pay attention to that extra little / at the end of dirName */
    00118     if (buf[1] && end[-1] == '/') {
    00119         end--;
    00120         *end = '\0';
    00121     }
    00122
    00123     while (1) {
    00124
    00125         /* as we're stating paths here, we want to follow symlinks */
    00126
    00127         cacheHit = cacheContainsDirectory(cache, (*buf != '\0' ? buf : "/"));
    00128         if (cacheHit != NULL) {
    00129             fp.entry = cacheHit;
    00130         } else if (!stat((*buf != '\0' ? buf : "/"), &sb)) {
    00131             size_t nb = sizeof(*fp.entry) + (*buf != '\0' ? (end-buf) : 1) + 1;
    00132             char * dn = xmalloc(nb);
    00133             struct fprintCacheEntry_s * newEntry = (void *)dn;
    00134
    00135             /*@-usereleased@*/  /* LCL: contiguous malloc confusion */
    00136             dn += sizeof(*newEntry);
    00137             strcpy(dn, (*buf != '\0' ? buf : "/"));
    00138             newEntry->ino = sb.st_ino;
    00139             newEntry->dev = sb.st_dev;
    00140             newEntry->isFake = 0;
    00141             newEntry->dirName = dn;
    00142             fp.entry = newEntry;
    00143
    00144             /*@-kepttrans -dependenttrans @*/
    00145             htAddEntry(cache->ht, dn, fp.entry);
    00146             /*@=kepttrans =dependenttrans @*/
    00147             /*@=usereleased@*/
    00148         }
    00149
    00150         if (fp.entry) {
    00151             fp.subDir = cleanDirName + (end - buf);
    00152             if (fp.subDir[0] == '/' && fp.subDir[1] != '\0')
    00153                 fp.subDir++;
    00154             if (fp.subDir[0] == '\0' ||
    00155             /* XXX don't bother saving '/' as subdir */
    00156                (fp.subDir[0] == '/' && fp.subDir[1] == '\0'))
    00157                 fp.subDir = NULL;
    00158             fp.baseName = baseName;
    00159             if (!scareMemory && fp.subDir != NULL)
    00160                 fp.subDir = xstrdup(fp.subDir);
    00161         /*@-compdef@*/ /* FIX: fp.entry.{dirName,dev,ino,isFake} undef @*/
    00162             return fp;
    00163         /*@=compdef@*/
    00164         }
    00165
    00166         /* stat of '/' just failed! */
    00167         if (end == buf + 1)
    00168             abort();
    00169
    00170         end--;
    00171         while ((end > buf) && *end != '/') end--;
    00172         if (end == buf)     /* back to stat'ing just '/' */
    00173             end++;
    00174
    00175         *end = '\0';
    00176     }
    00177
    00178     /*@notreached@*/
    00179
    00180     /*@-compdef@*/ /* FIX: fp.entry.{dirName,dev,ino,isFake} undef @*/
    00181     /*@-nullret@*/ return fp; /*@=nullret@*/    /* LCL: can't happen. */
    00182     /*@=compdef@*/
    00183 }
    00184
    00185 fingerPrint fpLookup(fingerPrintCache cache, const char * dirName,
    00186                         const char * baseName, int scareMemory)
    00187 {
    00188     return doLookup(cache, dirName, baseName, scareMemory);
    00189 }
    00190
    00191 unsigned int fpHashFunction(const void * key)
    00192 {
    00193     const fingerPrint * fp = key;
    00194     unsigned int hash = 0;
    00195     char ch;
    00196     const char * chptr;
    00197
    00198     ch = 0;
    00199     chptr = fp->baseName;
    00200     while (*chptr != '\0') ch ^= *chptr++;
    00201
    00202     hash |= ((unsigned)ch) << 24;
    00203     hash |= (((((unsigned)fp->entry->dev) >> 8) ^ fp->entry->dev) & 0xFF) << 16;
    00204     hash |= fp->entry->ino & 0xFFFF;
    00205
    00206     return hash;
    00207 }
    00208
    00209 int fpEqual(const void * key1, const void * key2)
    00210 {
    00211     const fingerPrint *k1 = key1;
    00212     const fingerPrint *k2 = key2;
    00213
    00214     /* If the addresses are the same, so are the values. */
    00215     if (k1 == k2)
    00216         return 0;
    00217
    00218     /* Otherwise, compare fingerprints by value. */
    00219     /*@-nullpass@*/     /* LCL: whines about (*k2).subdir */
    00220     if (FP_EQUAL(*k1, *k2))
    00221         return 0;
    00222     /*@=nullpass@*/
    00223     return 1;
    00224
    00225 }
    00226
    00227 void fpLookupList(fingerPrintCache cache, const char ** dirNames,
    00228                   const char ** baseNames, const int * dirIndexes,
    00229                   int fileCount, fingerPrint * fpList)
    00230 {
    00231     int i;
    00232
    00233     for (i = 0; i < fileCount; i++) {
    00234         /* If this is in the same directory as the last file, don't bother
    00235            redoing all of this work */
    00236         if (i > 0 && dirIndexes[i - 1] == dirIndexes[i]) {
    00237             fpList[i].entry = fpList[i - 1].entry;
    00238             fpList[i].subDir = fpList[i - 1].subDir;
    00239             fpList[i].baseName = baseNames[i];
    00240         } else {
    00241             fpList[i] = doLookup(cache, dirNames[dirIndexes[i]], baseNames[i],
    00242                                  1);
    00243         }
    00244     }
    00245 }
    00246
    00247 void fpLookupHeader(fingerPrintCache cache, Header h, fingerPrint * fpList)
    00248 {
    00249     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
    00250     HFD_t hfd = headerFreeData;
    00251     const char ** baseNames, ** dirNames;
    00252     rpmTagType bnt, dnt;
    00253     int_32 * dirIndexes;
    00254     int fileCount;
    00255     int xx;
    00256
    00257     if (!hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, &fileCount))
    00258         return;
    00259
    00260     xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
    00261     xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
    00262     fpLookupList(cache, dirNames, baseNames, dirIndexes, fileCount, fpList);
    00263     dirNames = hfd(dirNames, dnt);
    00264     baseNames = hfd(baseNames, bnt);
    00265 }