rpm 5.3.7

rpmdb/fprint.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 #include <rpmiotypes.h> /* XXX rpmRC codes. */
00008 #include <rpmio.h>      /* XXX Realpath(). */
00009 #include <rpmmacro.h>   /* XXX for rpmCleanPath */
00010 
00011 #include <rpmtag.h>
00012 #include <rpmdb.h>
00013 
00014 #define _FPRINT_INTERNAL
00015 #include "fprint.h"
00016 
00017 #include "debug.h"
00018 
00019 /*@access hashTable @*/
00020 
00021 fingerPrintCache fpCacheCreate(int sizeHint)
00022 {
00023     fingerPrintCache fpc;
00024 
00025     fpc = xmalloc(sizeof(*fpc));
00026     fpc->ht = htCreate(sizeHint * 2, 0, 1, NULL, NULL);
00027 assert(fpc->ht != NULL);
00028     return fpc;
00029 }
00030 
00031 fingerPrintCache fpCacheFree(fingerPrintCache cache)
00032 {
00033     cache->ht = htFree(cache->ht);
00034     free(cache);
00035     return NULL;
00036 }
00037 
00044 static /*@null@*/ const struct fprintCacheEntry_s * cacheContainsDirectory(
00045                             fingerPrintCache cache,
00046                             const char * dirName)
00047         /*@*/
00048 {
00049     const void ** data;
00050 
00051     if (htGetEntry(cache->ht, dirName, &data, NULL, NULL))
00052         return NULL;
00053     return data[0];
00054 }
00055 
00064 static fingerPrint doLookup(fingerPrintCache cache,
00065                 const char * dirName, const char * baseName, int scareMem)
00066         /*@globals fileSystem, internalState @*/
00067         /*@modifies cache, fileSystem, internalState @*/
00068 {
00069     char dir[PATH_MAX];
00070     const char * cleanDirName;
00071     size_t cdnl;
00072     char * end;             /* points to the '\0' at the end of "buf" */
00073     fingerPrint fp;
00074     struct stat sb;
00075     char * buf;
00076     const struct fprintCacheEntry_s * cacheHit;
00077 
00078     /* assert(*dirName == '/' || !scareMem); */
00079 
00080     /* XXX WATCHOUT: fp.subDir is set below from relocated dirName arg */
00081     cleanDirName = dirName;
00082     cdnl = strlen(cleanDirName);
00083 
00084     if (*cleanDirName == '/') {
00085         if (!scareMem)
00086             cleanDirName =
00087                 rpmCleanPath(strcpy(alloca(cdnl+1), dirName));
00088     } else {
00089         scareMem = 0;   /* XXX causes memory leak */
00090 
00091         /* Using realpath on the arg isn't correct if the arg is a symlink,
00092          * especially if the symlink is a dangling link.  What we 
00093          * do instead is use realpath() on `.' and then append arg to
00094          * the result.
00095          */
00096 
00097         /* if the current directory doesn't exist, we might fail. 
00098            oh well. likewise if it's too long.  */
00099         dir[0] = '\0';
00100         if (Realpath(".", dir) != NULL) {
00101             end = dir + strlen(dir);
00102             if (end[-1] != '/') *end++ = '/';
00103             end = stpncpy(end, cleanDirName, sizeof(dir) - (end - dir));
00104             *end = '\0';
00105             (void)rpmCleanPath(dir); /* XXX possible /../ from concatenation */
00106             end = dir + strlen(dir);
00107             if (end[-1] != '/') *end++ = '/';
00108             *end = '\0';
00109             cleanDirName = dir;
00110             cdnl = end - dir;
00111         }
00112     }
00113     fp.entry = NULL;
00114     fp.subDir = NULL;
00115     fp.baseName = NULL;
00116     /*@-nullret@*/
00117     if (cleanDirName == NULL) return fp;        /* XXX can't happen */
00118     /*@=nullret@*/
00119 
00120     buf = strcpy(alloca(cdnl + 1), cleanDirName);
00121     end = buf + cdnl;
00122 
00123     /* no need to pay attention to that extra little / at the end of dirName */
00124     if (buf[1] && end[-1] == '/') {
00125         end--;
00126         *end = '\0';
00127     }
00128 
00129     while (1) {
00130 
00131         /* as we're stating paths here, we want to follow symlinks */
00132 
00133         cacheHit = cacheContainsDirectory(cache, (*buf != '\0' ? buf : "/"));
00134         if (cacheHit != NULL) {
00135             fp.entry = cacheHit;
00136         } else if (!stat((*buf != '\0' ? buf : "/"), &sb)) {
00137             size_t nb = sizeof(*fp.entry) + (*buf != '\0' ? (end-buf) : 1) + 1;
00138             char * dn = xmalloc(nb);
00139             struct fprintCacheEntry_s * newEntry = (void *)dn;
00140 
00141             /*@-usereleased@*/  /* LCL: contiguous malloc confusion */
00142             dn += sizeof(*newEntry);
00143             strcpy(dn, (*buf != '\0' ? buf : "/"));
00144             newEntry->ino = (ino_t)sb.st_ino;
00145             newEntry->dev = (dev_t)sb.st_dev;
00146             newEntry->dirName = dn;
00147             fp.entry = newEntry;
00148 
00149             /*@-kepttrans -dependenttrans @*/
00150             htAddEntry(cache->ht, dn, fp.entry);
00151             /*@=kepttrans =dependenttrans @*/
00152             /*@=usereleased@*/
00153         }
00154 
00155         if (fp.entry) {
00156             fp.subDir = cleanDirName + (end - buf);
00157             if (fp.subDir[0] == '/' && fp.subDir[1] != '\0')
00158                 fp.subDir++;
00159             if (fp.subDir[0] == '\0' ||
00160             /* XXX don't bother saving '/' as subdir */
00161               (fp.subDir[0] == '/' && fp.subDir[1] == '\0'))
00162                 fp.subDir = NULL;
00163             fp.baseName = baseName;
00164             if (!scareMem && fp.subDir != NULL)
00165                 fp.subDir = xstrdup(fp.subDir);
00166         /*@-compdef@*/ /* FIX: fp.entry.{dirName,dev,ino} undef @*/
00167             return fp;
00168         /*@=compdef@*/
00169         }
00170 
00171         /* stat of '/' just failed! */
00172         if (end == buf + 1)
00173             abort();
00174 
00175         end--;
00176         while ((end > buf) && *end != '/') end--;
00177         if (end == buf)     /* back to stat'ing just '/' */
00178             end++;
00179 
00180         *end = '\0';
00181     }
00182 
00183     /*@notreached@*/
00184 
00185     /*@-compdef@*/ /* FIX: fp.entry.{dirName,dev,ino} undef @*/
00186     /*@-nullret@*/ return fp; /*@=nullret@*/    /* LCL: can't happen. */
00187     /*@=compdef@*/
00188 }
00189 
00190 fingerPrint fpLookup(fingerPrintCache cache, const char * dirName, 
00191                         const char * baseName, int scareMem)
00192 {
00193     return doLookup(cache, dirName, baseName, scareMem);
00194 }
00195 
00196 rpmuint32_t fpHashFunction(rpmuint32_t h, const void * data,
00197                 /*@unused@*/ size_t size)
00198 {
00199     const fingerPrint * fp = data;
00200     const char * chptr = fp->baseName;
00201     unsigned char ch = '\0';
00202 
00203     while (*chptr != '\0') ch ^= *chptr++;
00204 
00205     h |= ((unsigned)ch) << 24;
00206     h |= (((((unsigned)fp->entry->dev) >> 8) ^ fp->entry->dev) & 0xFF) << 16;
00207     h |= fp->entry->ino & 0xFFFF;
00208     
00209     return h;
00210 }
00211 
00212 int fpEqual(const void * key1, const void * key2)
00213 {
00214     const fingerPrint *k1 = key1;
00215     const fingerPrint *k2 = key2;
00216 
00217     /* If the addresses are the same, so are the values. */
00218     if (k1 == k2)
00219         return 0;
00220 
00221     /* Otherwise, compare fingerprints by value. */
00222     /*@-nullpass@*/     /* LCL: whines about (*k2).subdir */
00223     if (FP_EQUAL(*k1, *k2))
00224         return 0;
00225     /*@=nullpass@*/
00226     return 1;
00227 
00228 }
00229 
00230 void fpLookupList(fingerPrintCache cache, const char ** dirNames, 
00231                   const char ** baseNames, const rpmuint32_t * dirIndexes, 
00232                   rpmuint32_t fileCount, fingerPrint * fpList)
00233 {
00234     unsigned i;
00235 
00236     for (i = 0; i < (unsigned) fileCount; i++) {
00237         /* If this is in the same directory as the last file, don't bother
00238            redoing all of this work */
00239         if (i > 0 && dirIndexes[i - 1] == dirIndexes[i]) {
00240             fpList[i].entry = fpList[i - 1].entry;
00241             fpList[i].subDir = fpList[i - 1].subDir;
00242             fpList[i].baseName = baseNames[i];
00243         } else {
00244             fpList[i] = doLookup(cache, dirNames[dirIndexes[i]], baseNames[i],
00245                                  1);
00246         }
00247     }
00248 }
00249 
00250 #ifdef  NOTUSED
00251 
00258 static
00259 void fpLookupHeader(fingerPrintCache cache, Header h, fingerPrint * fpList)
00260         /*@modifies h, cache, *fpList @*/
00261 {
00262     rpmTagData he_p = { .ptr = NULL };
00263     HE_s he_s = { .tag = 0, .t = 0, .p = &he_p, .c = 0, .freeData = 0 };
00264     HE_t he = &he_s;
00265     const char ** baseNames;
00266     const char ** dirNames;
00267     rpmuint32_t * dirIndexes;
00268     rpmTagCount fileCount;
00269     int xx;
00270 
00271     he->tag = RPMTAG_BASENAMES;
00272     xx = headerGet(h, he, 0);
00273     baseNames = he_p.argv;
00274     fileCount = he->c;
00275     if (!xx)
00276         return;
00277 
00278     he->tag = RPMTAG_DIRNAMES;
00279     xx = headerGet(h, he, 0);
00280     dirNames = he_p.argv;
00281     he->tag = RPMTAG_DIRINDEXES;
00282     xx = headerGet(h, he, 0);
00283     dirIndexes = he_p.ui32p;
00284 
00285     fpLookupList(cache, dirNames, baseNames, dirIndexes, fileCount, fpList);
00286 
00287     dirIndexes = _free(dirIndexes);
00288     dirNames = _free(dirNames);
00289     baseNames = _free(baseNames);
00290 }
00291 #endif
00292 
00293 /* XXX fpLookupSubdir should be moved to lib/rpmfi.c somewhen. */
00294 #define _RPMFI_INTERNAL
00295 #include "rpmfi.h"
00296 #define _RPMTE_INTERNAL
00297 #include "rpmte.h"
00298 
00299 void fpLookupSubdir(hashTable symlinks, hashTable fphash, fingerPrintCache fpc,
00300                 void * _p, int filenr)
00301 {
00302     rpmte p = _p;
00303     rpmfi fi = p->fi;
00304     fingerPrint *const fps = fi->fps + filenr;
00305     
00306     struct fingerPrint_s current_fp;
00307     fingerPrint * cfp = &current_fp;
00308     int symlinkcount = 0;
00309 
00310     const char * s;
00311     const char * se;
00312     size_t ns;
00313     char * t;
00314     char * te;
00315 
00316     struct rpmffi_s * ffi = xmalloc(sizeof(*ffi));
00317     ffi->p = p;
00318     ffi->fileno = filenr;
00319 
00320 restart:
00321     *cfp = *fps;
00322     if (cfp->subDir == NULL)
00323         goto exit;
00324 
00325     s = cfp->baseName = te = xstrdup(cfp->subDir);
00326     ns = strlen(s);
00327     se = s + ns - 1;
00328     cfp->subDir = t = NULL;     /* no subDir for now */
00329 
00330     /* Set baseName to the upper most dir */
00331     while (*te != '/' && te < se)
00332         te++;
00333     *te = '\0';
00334 
00335     while (te < se) {
00336         struct rpmffi_s * recs;
00337         int numRecs;
00338         int i;
00339 
00340         recs = NULL;
00341         numRecs = 0;
00342         (void) htGetEntry(symlinks, cfp, &recs, &numRecs, NULL);
00343 
00344         for (i = 0; i < numRecs; i++) {
00345             const char * flink;
00346             const char * link;
00347             int fx;
00348 
00349             fx = recs[i].fileno;
00350             fi =  recs[i].p->fi;
00351             flink = fi->flinks[fx];
00352             if (!(flink && *flink != '\0'))
00353                 continue;
00354 
00355             /* Build a new (directory) fingerprint path. */
00356             /* XXX Paths containing '%' will be macro expanded. */
00357             if (*flink == '/')
00358                 link = rpmGetPath(flink, "/", te+1, "/", NULL);
00359             else if (cfp->subDir == NULL)
00360                 link = rpmGetPath(cfp->entry->dirName, "/",
00361                         flink, "/", te+1, "/", NULL);
00362             else
00363                 link = rpmGetPath(cfp->entry->dirName, "/", cfp->subDir, "/",
00364                         flink, "/", te+1, "/", NULL);
00365 
00366 #ifdef  NOTNOW  /* XXX avoid strlen on fast path */
00367 assert(link[strlen(link)-1] == '/');
00368 #endif
00369 
00370             /* Find the new (directory) fingerprint starting point. */
00371             *fps = fpLookup(fpc, link, fps->baseName, 0);
00372             link = _free(link);
00373 
00374             s = _free(s);
00375             if (++symlinkcount > 50)
00376                 goto exit;
00377             goto restart;
00378         }
00379 
00380         if (cfp->subDir == NULL)
00381             cfp->subDir = s;
00382         else
00383             *t = '/';
00384         t = te;
00385         cfp->baseName = t + 1;
00386 
00387         /* Set baseName to the next lower dir. */
00388         te++;
00389         while (*te != '\0' && *te != '/')
00390             te++;
00391         *te = '\0';
00392 
00393     }
00394     s = _free(s);
00395 
00396 exit:
00397     htAddEntry(fphash, fps, ffi);
00398     return;
00399 }