Main Page   Modules   Compound List   File List   Compound Members   File Members   Related Pages  

lib/transaction.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 #include <rpmlib.h>
00008 #include <rpmmacro.h>   /* XXX for rpmExpand */
00009 
00010 #include "depends.h"
00011 #include "fprint.h"
00012 #include "hash.h"
00013 #include "install.h"
00014 #include "md5.h"
00015 #include "misc.h"
00016 #include "rpmdb.h"
00017 
00018 /* XXX FIXME: merge with existing (broken?) tests in system.h */
00019 /* portability fiddles */
00020 #if STATFS_IN_SYS_STATVFS
00021 # include <sys/statvfs.h>
00022 #else
00023 # if STATFS_IN_SYS_VFS
00024 #  include <sys/vfs.h>
00025 # else
00026 #  if STATFS_IN_SYS_MOUNT
00027 #   include <sys/mount.h>
00028 #  else
00029 #   if STATFS_IN_SYS_STATFS
00030 #    include <sys/statfs.h>
00031 #   endif
00032 #  endif
00033 # endif
00034 #endif
00035 
00036 #include "debug.h"
00037 
00038 /*@access FD_t@*/               /* XXX compared with NULL */
00039 /*@access Header@*/             /* XXX compared with NULL */
00040 /*@access dbiIndexSet@*/
00041 /*@access rpmdb@*/
00042 /*@access rpmTransactionSet@*/
00043 /*@access rpmProblemSet@*/
00044 /*@access rpmProblem@*/
00045 
00046 typedef struct transactionFileInfo {
00047   /* for all packages */
00048     enum rpmTransactionType type;
00049     enum fileActions * actions; 
00050 /*@dependent@*/ fingerPrint * fps; 
00051     uint_32 * fflags;           
00052     uint_32 * fsizes;           
00053     const char ** bnl;          
00054     const char ** dnl;          
00055     const int * dil;            
00056     const char ** fmd5s;        
00057     uint_16 * fmodes;           
00058     Header h;                   
00059     int fc;                     
00060     int dc;                     
00061     char * fstates;             
00062   /* these are for TR_ADDED packages */
00063     const char ** flinks;       
00064     struct availablePackage * ap;
00065     struct sharedFileInfo * replaced;
00066     uint_32 * replacedSizes;
00067   /* for TR_REMOVED packages */
00068     unsigned int record;
00069 } TFI_t;
00070 
00071 struct diskspaceInfo {
00072     dev_t dev;                  
00073     signed long bneeded;        
00074     signed long ineeded;        
00075     int bsize;                  
00076     signed long bavail;         
00077     signed long iavail;         
00078 };
00079 
00080 /* Adjust for root only reserved space. On linux e2fs, this is 5%. */
00081 #define adj_fs_blocks(_nb)      (((_nb) * 21) / 20)
00082 
00083 /* argon thought a shift optimization here was a waste of time...  he's
00084    probably right :-( */
00085 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
00086 
00087 #define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
00088 
00089 #define XFA_SKIPPING(_a)        \
00090     ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPMULTILIB)
00091 
00092 static void freeFi(TFI_t *fi)
00093 {
00094         if (fi->h) {
00095             headerFree(fi->h); fi->h = NULL;
00096         }
00097         if (fi->actions) {
00098             free(fi->actions); fi->actions = NULL;
00099         }
00100         if (fi->replacedSizes) {
00101             free(fi->replacedSizes); fi->replacedSizes = NULL;
00102         }
00103         if (fi->replaced) {
00104             free(fi->replaced); fi->replaced = NULL;
00105         }
00106         if (fi->bnl) {
00107             free(fi->bnl); fi->bnl = NULL;
00108             free(fi->dnl); fi->dnl = NULL;
00109         }
00110         if (fi->flinks) {
00111             free(fi->flinks); fi->flinks = NULL;
00112         }
00113         if (fi->fmd5s) {
00114             free(fi->fmd5s); fi->fmd5s = NULL;
00115         }
00116 
00117         switch (fi->type) {
00118         case TR_REMOVED:
00119             if (fi->fsizes) {
00120                 free(fi->fsizes); fi->fsizes = NULL;
00121             }
00122             if (fi->fflags) {
00123                 free(fi->fflags); fi->fflags = NULL;
00124             }
00125             if (fi->fmodes) {
00126                 free(fi->fmodes); fi->fmodes = NULL;
00127             }
00128             if (fi->fstates) {
00129                 free(fi->fstates); fi->fstates = NULL;
00130             }
00131             if (fi->dil) {
00132                 free((void *)fi->dil); fi->dil = NULL;
00133             }
00134             break;
00135         case TR_ADDED:
00136             break;
00137         }
00138 }
00139 
00140 static void freeFl(rpmTransactionSet ts, TFI_t *flList)
00141 {
00142     TFI_t *fi;
00143     int oc;
00144 
00145     for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
00146         freeFi(fi);
00147     }
00148 }
00149 
00150 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
00151 {
00152     ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
00153 }
00154 
00155 int rpmtransGetKeys(const rpmTransactionSet ts, const void *** ep, int * nep)
00156 {
00157     int rc = 0;
00158 
00159     if (nep) *nep = ts->orderCount;
00160     if (ep) {
00161         const void ** e;
00162         int oc;
00163 
00164         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
00165         for (oc = 0; oc < ts->orderCount; oc++, e++) {
00166             struct availablePackage * alp;
00167             switch (ts->order[oc].type) {
00168             case TR_ADDED:
00169                 alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
00170                 *e = alp->key;
00171                 break;
00172             case TR_REMOVED:
00173                 *e = NULL;
00174                 break;
00175             }
00176         }
00177     }
00178     return rc;
00179 }
00180 
00181 static rpmProblemSet psCreate(void)
00182 {
00183     rpmProblemSet probs;
00184 
00185     probs = xmalloc(sizeof(*probs));    /* XXX memory leak */
00186     probs->numProblems = probs->numProblemsAlloced = 0;
00187     probs->probs = NULL;
00188 
00189     return probs;
00190 }
00191 
00192 static void psAppend(rpmProblemSet probs, rpmProblemType type,
00193                 /*@dependent@*/ const void * key, Header h,
00194                 const char * dn, const char *bn,
00195                 Header altH, unsigned long ulong1)
00196 {
00197     const char *n, *v, *r;
00198     char *t;
00199     rpmProblem p;
00200 
00201     if (probs->numProblems == probs->numProblemsAlloced) {
00202         if (probs->numProblemsAlloced)
00203             probs->numProblemsAlloced *= 2;
00204         else
00205             probs->numProblemsAlloced = 2;
00206         probs->probs = xrealloc(probs->probs,
00207                         probs->numProblemsAlloced * sizeof(*probs->probs));
00208     }
00209 
00210     p = probs->probs + probs->numProblems++;
00211     p->type = type;
00212     p->key = key;
00213     p->ulong1 = ulong1;
00214     p->ignoreProblem = 0;
00215 
00216     if (dn || bn) {
00217         p->str1 =
00218             t = xmalloc((dn ? strlen(dn) : 0) + (bn ? strlen(bn) : 0) + 1);
00219         if (dn) t = stpcpy(t, dn);
00220         if (bn) t = stpcpy(t, bn);
00221     } else
00222         p->str1 = NULL;
00223 
00224     if (h) {
00225         p->h = headerLink(h);
00226         headerNVR(h, &n, &v, &r);
00227         p->pkgNEVR =
00228             t = xmalloc(strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
00229         t = stpcpy(t, n);
00230         t = stpcpy(t, "-");
00231         t = stpcpy(t, v);
00232         t = stpcpy(t, "-");
00233         t = stpcpy(t, r);
00234     } else {
00235         p->pkgNEVR = NULL;
00236         p->h = NULL;
00237     }
00238 
00239     if (altH) {
00240         headerNVR(altH, &n, &v, &r);
00241         p->altNEVR =
00242             t = xmalloc(strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
00243         t = stpcpy(t, n);
00244         t = stpcpy(t, "-");
00245         t = stpcpy(t, v);
00246         t = stpcpy(t, "-");
00247         t = stpcpy(t, r);
00248     } else
00249         p->altNEVR = NULL;
00250 }
00251 
00252 static int archOkay(Header h)
00253 {
00254     void * pkgArch;
00255     int type, count;
00256 
00257     /* make sure we're trying to install this on the proper architecture */
00258     headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
00259 #ifndef DYING
00260     if (type == RPM_INT8_TYPE) {
00261         int_8 * pkgArchNum;
00262         int archNum;
00263 
00264         /* old arch handling */
00265         rpmGetArchInfo(NULL, &archNum);
00266         pkgArchNum = pkgArch;
00267         if (archNum != *pkgArchNum) {
00268             return 0;
00269         }
00270     } else
00271 #endif
00272     {
00273         /* new arch handling */
00274         if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
00275             return 0;
00276         }
00277     }
00278 
00279     return 1;
00280 }
00281 
00282 static int osOkay(Header h)
00283 {
00284     void * pkgOs;
00285     int type, count;
00286 
00287     /* make sure we're trying to install this on the proper os */
00288     headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
00289 #ifndef DYING
00290     if (type == RPM_INT8_TYPE) {
00291         /* v1 packages and v2 packages both used improper OS numbers, so just
00292            deal with it hope things work */
00293         return 1;
00294     } else
00295 #endif
00296     {
00297         /* new os handling */
00298         if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
00299             return 0;
00300         }
00301     }
00302 
00303     return 1;
00304 }
00305 
00306 void rpmProblemSetFree(rpmProblemSet probs)
00307 {
00308     int i;
00309 
00310     for (i = 0; i < probs->numProblems; i++) {
00311         rpmProblem p = probs->probs + i;
00312         if (p->h)       headerFree(p->h);
00313         if (p->pkgNEVR) free((void *)p->pkgNEVR);
00314         if (p->altNEVR) free((void *)p->altNEVR);
00315         if (p->str1) free((void *)p->str1);
00316     }
00317     free(probs);
00318 }
00319 
00320 static /*@observer@*/ const char *const ftstring (enum fileTypes ft)
00321 {
00322     switch (ft) {
00323     case XDIR:  return "directory";
00324     case CDEV:  return "char dev";
00325     case BDEV:  return "block dev";
00326     case LINK:  return "link";
00327     case SOCK:  return "sock";
00328     case PIPE:  return "fifo/pipe";
00329     case REG:   return "file";
00330     }
00331     return "unknown file type";
00332 }
00333 
00334 static enum fileTypes whatis(uint_16 mode)
00335 {
00336     if (S_ISDIR(mode))  return XDIR;
00337     if (S_ISCHR(mode))  return CDEV;
00338     if (S_ISBLK(mode))  return BDEV;
00339     if (S_ISLNK(mode))  return LINK;
00340     if (S_ISSOCK(mode)) return SOCK;
00341     if (S_ISFIFO(mode)) return PIPE;
00342     return REG;
00343 }
00344 
00345 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
00346 
00356 static Header relocateFileList(const rpmTransactionSet ts,
00357                 struct availablePackage * alp,
00358                 Header origH, enum fileActions * actions)
00359 {
00360     static int _printed = 0;
00361     rpmProblemSet probs = ts->probs;
00362     int allowBadRelocate = (ts->ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
00363     rpmRelocation * rawRelocations = alp->relocs;
00364     rpmRelocation * relocations = NULL;
00365     int numRelocations;
00366     const char ** validRelocations;
00367     int_32 validType;
00368     int numValid;
00369     const char ** baseNames;
00370     const char ** dirNames;
00371     int_32 * dirIndexes;
00372     int_32 * newDirIndexes;
00373     int_32 fileCount;
00374     int_32 dirCount;
00375     uint_32 * fFlags = NULL;
00376     uint_16 * fModes = NULL;
00377     char * skipDirList;
00378     Header h;
00379     int nrelocated = 0;
00380     int fileAlloced = 0;
00381     char * fn = NULL;
00382     int haveRelocatedFile = 0;
00383     int len;
00384     int i, j;
00385 
00386     if (!headerGetEntry(origH, RPMTAG_PREFIXES, &validType,
00387                         (void **) &validRelocations, &numValid))
00388         numValid = 0;
00389 
00390     numRelocations = 0;
00391     if (rawRelocations)
00392         while (rawRelocations[numRelocations].newPath ||
00393                rawRelocations[numRelocations].oldPath)
00394             numRelocations++;
00395 
00396     /*
00397      * If no relocations are specified (usually the case), then return the
00398      * original header. If there are prefixes, however, then INSTPREFIXES
00399      * should be added, but, since relocateFileList() can be called more
00400      * than once for the same header, don't bother if already present.
00401      */
00402     if (numRelocations == 0) {
00403         if (numValid) {
00404             if (!headerIsEntry(origH, RPMTAG_INSTPREFIXES))
00405                 headerAddEntry(origH, RPMTAG_INSTPREFIXES,
00406                         validType, validRelocations, numValid);
00407             headerFreeData(validRelocations, validType);
00408         }
00409         /* XXX FIXME multilib file actions need to be checked. */
00410         return headerLink(origH);
00411     }
00412 
00413 #ifdef DYING
00414     h = headerCopy(origH);
00415 #else
00416     h = headerLink(origH);
00417 #endif
00418 
00419     relocations = alloca(sizeof(*relocations) * numRelocations);
00420 
00421     /* Build sorted relocation list from raw relocations. */
00422     for (i = 0; i < numRelocations; i++) {
00423         /* FIXME: default relocations (oldPath == NULL) need to be handled
00424            in the UI, not rpmlib */
00425 
00426         /* FIXME: Trailing /'s will confuse us greatly. Internal ones will 
00427            too, but those are more trouble to fix up. :-( */
00428         relocations[i].oldPath =
00429             stripTrailingChar(alloca_strdup(rawRelocations[i].oldPath), '/');
00430 
00431         /* An old path w/o a new path is valid, and indicates exclusion */
00432         if (rawRelocations[i].newPath) {
00433             relocations[i].newPath =
00434                 stripTrailingChar(alloca_strdup(rawRelocations[i].newPath), '/');
00435 
00436             /* Verify that the relocation's old path is in the header. */
00437             for (j = 0; j < numValid; j++)
00438                 if (!strcmp(validRelocations[j], relocations[i].oldPath)) break;
00439             /* XXX actions check prevents problem from being appended twice. */
00440             if (j == numValid && !allowBadRelocate && actions)
00441                 psAppend(probs, RPMPROB_BADRELOCATE, alp->key, alp->h,
00442                          relocations[i].oldPath, NULL, NULL, 0);
00443         } else {
00444             relocations[i].newPath = NULL;
00445         }
00446     }
00447 
00448     /* stupid bubble sort, but it's probably faster here */
00449     for (i = 0; i < numRelocations; i++) {
00450         int madeSwap;
00451         madeSwap = 0;
00452         for (j = 1; j < numRelocations; j++) {
00453             rpmRelocation tmpReloc;
00454             if (strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0)
00455                 continue;
00456             tmpReloc = relocations[j - 1];
00457             relocations[j - 1] = relocations[j];
00458             relocations[j] = tmpReloc;
00459             madeSwap = 1;
00460         }
00461         if (!madeSwap) break;
00462     }
00463 
00464     if (!_printed) {
00465         _printed = 1;
00466         rpmMessage(RPMMESS_DEBUG, _("========== relocations\n"));
00467         for (i = 0; i < numRelocations; i++) {
00468             if (relocations[i].newPath == NULL)
00469                 rpmMessage(RPMMESS_DEBUG, _("%5d exclude  %s\n"),
00470                         i, relocations[i].oldPath);
00471             else
00472                 rpmMessage(RPMMESS_DEBUG, _("%5d relocate %s -> %s\n"),
00473                         i, relocations[i].oldPath, relocations[i].newPath);
00474         }
00475     }
00476 
00477     /* Add relocation values to the header */
00478     if (numValid) {
00479         const char ** actualRelocations;
00480         int numActual;
00481 
00482         actualRelocations = xmalloc(sizeof(*actualRelocations) * numValid);
00483         numActual = 0;
00484         for (i = 0; i < numValid; i++) {
00485             for (j = 0; j < numRelocations; j++) {
00486                 if (strcmp(validRelocations[i], relocations[j].oldPath))
00487                     continue;
00488                 /* On install, a relocate to NULL means skip the path. */
00489                 if (relocations[j].newPath) {
00490                     actualRelocations[numActual] = relocations[j].newPath;
00491                     numActual++;
00492                 }
00493                 break;
00494             }
00495             if (j == numRelocations) {
00496                 actualRelocations[numActual] = validRelocations[i];
00497                 numActual++;
00498             }
00499         }
00500 
00501         if (numActual)
00502             headerAddEntry(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
00503                        (void **) actualRelocations, numActual);
00504 
00505         free((void *)actualRelocations);
00506         headerFreeData(validRelocations, validType);
00507     }
00508 
00509     headerGetEntry(h, RPMTAG_BASENAMES, NULL, (void **) &baseNames, 
00510                    &fileCount);
00511     headerGetEntry(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
00512     headerGetEntry(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, 
00513                    &dirCount);
00514     headerGetEntry(h, RPMTAG_FILEFLAGS, NULL, (void **) &fFlags, NULL);
00515     headerGetEntry(h, RPMTAG_FILEMODES, NULL, (void **) &fModes, NULL);
00516 
00517     skipDirList = alloca(dirCount * sizeof(*skipDirList));
00518     memset(skipDirList, 0, dirCount * sizeof(*skipDirList));
00519 
00520     newDirIndexes = alloca(sizeof(*newDirIndexes) * fileCount);
00521     memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
00522     dirIndexes = newDirIndexes;
00523 
00524     /*
00525      * For all relocations, we go through sorted file/relocation lists 
00526      * backwards so that /usr/local relocations take precedence over /usr 
00527      * ones.
00528      */
00529 
00530     /* Relocate individual paths. */
00531 
00532     for (i = fileCount - 1; i >= 0; i--) {
00533         char * te;
00534         int fslen;
00535 
00536         /*
00537          * If only adding libraries of different arch into an already
00538          * installed package, skip all other files.
00539          */
00540         if (alp->multiLib && !isFileMULTILIB((fFlags[i]))) {
00541             if (actions) {
00542                 actions[i] = FA_SKIPMULTILIB;
00543                 rpmMessage(RPMMESS_DEBUG, _("excluding multilib path %s%s\n"), 
00544                         dirNames[dirIndexes[i]], baseNames[i]);
00545             }
00546             continue;
00547         }
00548 
00549         len = strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
00550         if (len >= fileAlloced) {
00551             fileAlloced = len * 2;
00552             fn = xrealloc(fn, fileAlloced);
00553         }
00554         te = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]);
00555         fslen = (te - fn);
00556 
00557         /*
00558          * See if this file needs relocating.
00559          */
00560         /*
00561          * XXX FIXME: Would a bsearch of the (already sorted) 
00562          * relocation list be a good idea?
00563          */
00564         for (j = numRelocations - 1; j >= 0; j--) {
00565             len = strlen(relocations[j].oldPath);
00566             if (fslen < len)
00567                 continue;
00568             if (strncmp(relocations[j].oldPath, fn, len))
00569                 continue;
00570             break;
00571         }
00572         if (j < 0) continue;
00573 
00574         /* On install, a relocate to NULL means skip the path. */
00575         if (relocations[j].newPath == NULL) {
00576             enum fileTypes ft = whatis(fModes[i]);
00577             int k;
00578             if (ft == XDIR) {
00579                 /* Start with the parent, looking for directory to exclude. */
00580                 for (k = dirIndexes[i]; k < dirCount; k++) {
00581                     len = strlen(dirNames[k]) - 1;
00582                     while (len > 0 && dirNames[k][len-1] == '/') len--;
00583                     if (len == fslen && !strncmp(dirNames[k], fn, len))
00584                         break;
00585                 }
00586                 if (k >= dirCount)
00587                     continue;
00588                 skipDirList[k] = 1;
00589             }
00590             if (actions) {
00591                 actions[i] = FA_SKIPNSTATE;
00592                 rpmMessage(RPMMESS_DEBUG, _("excluding %s %s\n"),
00593                         ftstring(ft), fn);
00594             }
00595             continue;
00596         }
00597 
00598         if (actions)
00599             rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
00600                     fn, relocations[j].newPath);
00601         nrelocated++;
00602 
00603         len = strlen(relocations[j].newPath);
00604         if (len >= fileAlloced) {
00605             fileAlloced = len * 2;
00606             fn = xrealloc(fn, fileAlloced);
00607         }
00608         strcpy(fn, relocations[j].newPath);
00609 
00610         {   char * s = strrchr(fn, '/');
00611             *s++ = '\0';
00612 
00613             /* fn is the new dirName, and s is the new baseName */
00614             if (strcmp(baseNames[i], s))
00615                 baseNames[i] = alloca_strdup(s);
00616         }
00617 
00618         /* Does this directory already exist in the directory list? */
00619         for (j = 0; j < dirCount; j++)
00620             if (!strcmp(fn, dirNames[j])) break;
00621         
00622         if (j < dirCount) {
00623             dirIndexes[i] = j;
00624             continue;
00625         }
00626 
00627         /* Creating new paths is a pita */
00628         if (!haveRelocatedFile) {
00629             const char ** newDirList;
00630             int k;
00631 
00632             haveRelocatedFile = 1;
00633             newDirList = xmalloc(sizeof(*newDirList) * (dirCount + 1));
00634             for (k = 0; k < dirCount; k++)
00635                 newDirList[k] = alloca_strdup(dirNames[k]);
00636             headerFreeData(dirNames, RPM_STRING_ARRAY_TYPE);
00637             dirNames = newDirList;
00638         } else {
00639             dirNames = xrealloc(dirNames, 
00640                                sizeof(*dirNames) * (dirCount + 1));
00641         }
00642 
00643         dirNames[dirCount] = alloca_strdup(fn);
00644         dirIndexes[i] = dirCount;
00645         dirCount++;
00646     }
00647 
00648     /* Finish off by relocating directories. */
00649     for (i = dirCount - 1; i >= 0; i--) {
00650         for (j = numRelocations - 1; j >= 0; j--) {
00651             int oplen;
00652 
00653             oplen = strlen(relocations[j].oldPath);
00654             if (strncmp(relocations[j].oldPath, dirNames[i], oplen))
00655                 continue;
00656 
00657             /*
00658              * Only subdirectories or complete file paths may be relocated. We
00659              * don't check for '\0' as our directory names all end in '/'.
00660              */
00661             if (!(dirNames[i][oplen] == '/'))
00662                 continue;
00663 
00664             if (relocations[j].newPath) { /* Relocate the path */
00665                 const char *s = relocations[j].newPath;
00666                 char *t = alloca(strlen(s) + strlen(dirNames[i]) - oplen + 1);
00667 
00668                 (void) stpcpy( stpcpy(t, s) , dirNames[i] + oplen);
00669                 if (actions)
00670                     rpmMessage(RPMMESS_DEBUG,
00671                         _("relocating directory %s to %s\n"), dirNames[i], t);
00672                 dirNames[i] = t;
00673                 nrelocated++;
00674             } else {
00675                 if (actions && !skipDirList[i]) {
00676                     rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), 
00677                         dirNames[dirIndexes[i]]);
00678                     actions[i] = FA_SKIPNSTATE;
00679                 }
00680             }
00681             break;
00682         }
00683     }
00684 
00685     /* Save original filenames in header and replace (relocated) filenames. */
00686     if (nrelocated) {
00687         int c;
00688         void * p;
00689         int t;
00690 
00691         p = NULL;
00692         headerGetEntry(h, RPMTAG_BASENAMES, &t, &p, &c);
00693         headerAddEntry(h, RPMTAG_ORIGBASENAMES, t, p, c);
00694         headerFreeData(p, t);
00695 
00696         p = NULL;
00697         headerGetEntry(h, RPMTAG_DIRNAMES, &t, &p, &c);
00698         headerAddEntry(h, RPMTAG_ORIGDIRNAMES, t, p, c);
00699         headerFreeData(p, t);
00700 
00701         p = NULL;
00702         headerGetEntry(h, RPMTAG_DIRINDEXES, &t, &p, &c);
00703         headerAddEntry(h, RPMTAG_ORIGDIRINDEXES, t, p, c);
00704         headerFreeData(p, t);
00705 
00706         headerModifyEntry(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
00707                           baseNames, fileCount);
00708         headerModifyEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
00709                           dirNames, dirCount);
00710         headerModifyEntry(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
00711                           dirIndexes, fileCount);
00712     }
00713 
00714     headerFreeData(baseNames, RPM_STRING_ARRAY_TYPE);
00715     headerFreeData(dirNames, RPM_STRING_ARRAY_TYPE);
00716     if (fn) free(fn);
00717 
00718     return h;
00719 }
00720 
00721 /*
00722  * As the problem sets are generated in an order solely dependent
00723  * on the ordering of the packages in the transaction, and that
00724  * ordering can't be changed, the problem sets must be parallel to
00725  * one another. Additionally, the filter set must be a subset of the
00726  * target set, given the operations available on transaction set.
00727  * This is good, as it lets us perform this trim in linear time, rather
00728  * then logarithmic or quadratic.
00729  */
00730 static int psTrim(rpmProblemSet filter, rpmProblemSet target)
00731 {
00732     rpmProblem f = filter->probs;
00733     rpmProblem t = target->probs;
00734     int gotProblems = 0;
00735 
00736     while ((f - filter->probs) < filter->numProblems) {
00737         if (!f->ignoreProblem) {
00738             f++;
00739             continue;
00740         }
00741         while ((t - target->probs) < target->numProblems) {
00742             if (f->h == t->h && f->type == t->type && t->key == f->key &&
00743                      XSTRCMP(f->str1, t->str1))
00744                 break;
00745             t++;
00746             gotProblems = 1;
00747         }
00748 
00749         if ((t - target->probs) == target->numProblems) {
00750             /* this can't happen ;-) lets be sane if it doesn though */
00751             break;
00752         }
00753 
00754         t->ignoreProblem = f->ignoreProblem;
00755         t++, f++;
00756     }
00757 
00758     if ((t - target->probs) < target->numProblems)
00759         gotProblems = 1;
00760 
00761     return gotProblems;
00762 }
00763 
00764 static int sharedCmp(const void * one, const void * two)
00765 {
00766     const struct sharedFileInfo * a = one;
00767     const struct sharedFileInfo * b = two;
00768 
00769     if (a->otherPkg < b->otherPkg)
00770         return -1;
00771     else if (a->otherPkg > b->otherPkg)
00772         return 1;
00773 
00774     return 0;
00775 }
00776 
00777 static enum fileActions decideFileFate(const char * dirName,
00778                         const char * baseName, short dbMode,
00779                         const char * dbMd5, const char * dbLink, short newMode,
00780                         const char * newMd5, const char * newLink, int newFlags,
00781                         int brokenMd5, rpmtransFlags transFlags)
00782 {
00783     char buffer[1024];
00784     const char * dbAttr, * newAttr;
00785     enum fileTypes dbWhat, newWhat, diskWhat;
00786     struct stat sb;
00787     int i, rc;
00788     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
00789     char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
00790 
00791     (void) stpcpy( stpcpy(filespec, dirName), baseName);
00792 
00793     if (lstat(filespec, &sb)) {
00794         /*
00795          * The file doesn't exist on the disk. Create it unless the new
00796          * package has marked it as missingok, or allfiles is requested.
00797          */
00798         if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
00799            (newFlags & RPMFILE_MISSINGOK)) {
00800             rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
00801                         filespec);
00802             return FA_SKIP;
00803         } else {
00804             return FA_CREATE;
00805         }
00806     }
00807 
00808     diskWhat = whatis(sb.st_mode);
00809     dbWhat = whatis(dbMode);
00810     newWhat = whatis(newMode);
00811 
00812     /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
00813        them in older packages as well */
00814     if (newWhat == XDIR) {
00815         return FA_CREATE;
00816     }
00817 
00818     if (diskWhat != newWhat) {
00819         return save;
00820     } else if (newWhat != dbWhat && diskWhat != dbWhat) {
00821         return save;
00822     } else if (dbWhat != newWhat) {
00823         return FA_CREATE;
00824     } else if (dbWhat != LINK && dbWhat != REG) {
00825         return FA_CREATE;
00826     }
00827 
00828     if (dbWhat == REG) {
00829         if (brokenMd5)
00830             rc = mdfileBroken(filespec, buffer);
00831         else
00832             rc = mdfile(filespec, buffer);
00833 
00834         if (rc) {
00835             /* assume the file has been removed, don't freak */
00836             return FA_CREATE;
00837         }
00838         dbAttr = dbMd5;
00839         newAttr = newMd5;
00840     } else /* dbWhat == LINK */ {
00841         memset(buffer, 0, sizeof(buffer));
00842         i = readlink(filespec, buffer, sizeof(buffer) - 1);
00843         if (i == -1) {
00844             /* assume the file has been removed, don't freak */
00845             return FA_CREATE;
00846         }
00847         dbAttr = dbLink;
00848         newAttr = newLink;
00849      }
00850 
00851     /* this order matters - we'd prefer to CREATE the file if at all
00852        possible in case something else (like the timestamp) has changed */
00853 
00854     if (!strcmp(dbAttr, buffer)) {
00855         /* this config file has never been modified, so just replace it */
00856         return FA_CREATE;
00857     }
00858 
00859     if (!strcmp(dbAttr, newAttr)) {
00860         /* this file is the same in all versions of this package */
00861         return FA_SKIP;
00862     }
00863 
00864     /*
00865      * The config file on the disk has been modified, but
00866      * the ones in the two packages are different. It would
00867      * be nice if RPM was smart enough to at least try and
00868      * merge the difference ala CVS, but...
00869      */
00870     return save;
00871 }
00872 
00873 static int filecmp(short mode1, const char * md51, const char * link1,
00874                    short mode2, const char * md52, const char * link2)
00875 {
00876     enum fileTypes what1 = whatis(mode1);
00877     enum fileTypes what2 = whatis(mode2);
00878 
00879     if (what1 != what2) return 1;
00880 
00881     if (what1 == LINK)
00882         return strcmp(link1, link2);
00883     else if (what1 == REG)
00884         return strcmp(md51, md52);
00885 
00886     return 0;
00887 }
00888 
00889 static int handleInstInstalledFiles(TFI_t * fi, rpmdb db,
00890                                     struct sharedFileInfo * shared,
00891                                     int sharedCount, int reportConflicts,
00892                                     rpmProblemSet probs,
00893                                     rpmtransFlags transFlags)
00894 {
00895     Header h;
00896     int i;
00897     const char ** otherMd5s;
00898     const char ** otherLinks;
00899     const char * otherStates;
00900     uint_32 * otherFlags;
00901     uint_32 * otherSizes;
00902     uint_16 * otherModes;
00903     int numReplaced = 0;
00904 
00905     rpmdbMatchIterator mi;
00906 
00907     mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
00908     h = rpmdbNextIterator(mi);
00909     if (h == NULL) {
00910         rpmdbFreeIterator(mi);
00911         return 1;
00912     }
00913 
00914     headerGetEntryMinMemory(h, RPMTAG_FILEMD5S, NULL,
00915                             (const void **) &otherMd5s, NULL);
00916     headerGetEntryMinMemory(h, RPMTAG_FILELINKTOS, NULL,
00917                             (const void **) &otherLinks, NULL);
00918     headerGetEntryMinMemory(h, RPMTAG_FILESTATES, NULL,
00919                             (const void **) &otherStates, NULL);
00920     headerGetEntryMinMemory(h, RPMTAG_FILEMODES, NULL,
00921                             (const void **) &otherModes, NULL);
00922     headerGetEntryMinMemory(h, RPMTAG_FILEFLAGS, NULL,
00923                             (const void **) &otherFlags, NULL);
00924     headerGetEntryMinMemory(h, RPMTAG_FILESIZES, NULL,
00925                             (const void **) &otherSizes, NULL);
00926 
00927     fi->replaced = xmalloc(sizeof(*fi->replaced) * sharedCount);
00928 
00929     for (i = 0; i < sharedCount; i++, shared++) {
00930         int otherFileNum, fileNum;
00931         otherFileNum = shared->otherFileNum;
00932         fileNum = shared->pkgFileNum;
00933 
00934         /* XXX another tedious segfault, assume file state normal. */
00935         if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
00936             continue;
00937 
00938         if (XFA_SKIPPING(fi->actions[fileNum]))
00939             continue;
00940 
00941         if (filecmp(otherModes[otherFileNum],
00942                         otherMd5s[otherFileNum],
00943                         otherLinks[otherFileNum],
00944                         fi->fmodes[fileNum],
00945                         fi->fmd5s[fileNum],
00946                         fi->flinks[fileNum])) {
00947             if (reportConflicts)
00948                 psAppend(probs, RPMPROB_FILE_CONFLICT, fi->ap->key, fi->ap->h,
00949                         fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
00950             if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
00951                         & RPMFILE_CONFIG) {
00952                 if (!shared->isRemoved)
00953                     fi->replaced[numReplaced++] = *shared;
00954             }
00955         }
00956 
00957         if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
00958             fi->actions[fileNum] = decideFileFate(
00959                         fi->dnl[fi->dil[fileNum]],
00960                         fi->bnl[fileNum],
00961                         otherModes[otherFileNum],
00962                         otherMd5s[otherFileNum],
00963                         otherLinks[otherFileNum],
00964                         fi->fmodes[fileNum],
00965                         fi->fmd5s[fileNum],
00966                         fi->flinks[fileNum],
00967                         fi->fflags[fileNum],
00968                         !headerIsEntry(h, RPMTAG_RPMVERSION),
00969                         transFlags);
00970         }
00971 
00972         fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
00973     }
00974 
00975     free(otherMd5s);
00976     free(otherLinks);
00977     rpmdbFreeIterator(mi);
00978 
00979     fi->replaced = xrealloc(fi->replaced,       /* XXX memory leak */
00980                            sizeof(*fi->replaced) * (numReplaced + 1));
00981     fi->replaced[numReplaced].otherPkg = 0;
00982 
00983     return 0;
00984 }
00985 
00986 static int handleRmvdInstalledFiles(TFI_t * fi, rpmdb db,
00987                                     struct sharedFileInfo * shared,
00988                                     int sharedCount)
00989 {
00990     Header h;
00991     const char * otherStates;
00992     int i;
00993    
00994     rpmdbMatchIterator mi;
00995 
00996     mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
00997     h = rpmdbNextIterator(mi);
00998     if (h == NULL) {
00999         rpmdbFreeIterator(mi);
01000         return 1;
01001     }
01002 
01003     headerGetEntryMinMemory(h, RPMTAG_FILESTATES, NULL,
01004                             (const void **) &otherStates, NULL);
01005 
01006     for (i = 0; i < sharedCount; i++, shared++) {
01007         int otherFileNum, fileNum;
01008         otherFileNum = shared->otherFileNum;
01009         fileNum = shared->pkgFileNum;
01010 
01011         if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
01012             continue;
01013 
01014         fi->actions[fileNum] = FA_SKIP;
01015     }
01016 
01017     rpmdbFreeIterator(mi);
01018 
01019     return 0;
01020 }
01021 
01025 static void handleOverlappedFiles(TFI_t * fi, hashTable ht,
01026                            rpmProblemSet probs, struct diskspaceInfo * dsl)
01027 {
01028     int i, j;
01029     struct diskspaceInfo * ds = NULL;
01030     uint_32 fixupSize = 0;
01031     char * filespec = NULL;
01032     int fileSpecAlloced = 0;
01033   
01034     for (i = 0; i < fi->fc; i++) {
01035         int otherPkgNum, otherFileNum;
01036         const TFI_t ** recs;
01037         int numRecs;
01038 
01039         if (XFA_SKIPPING(fi->actions[i]))
01040             continue;
01041 
01042         j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
01043         if (j > fileSpecAlloced) {
01044             fileSpecAlloced = j * 2;
01045             filespec = xrealloc(filespec, fileSpecAlloced);
01046         }
01047 
01048         (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
01049 
01050         if (dsl) {
01051             ds = dsl;
01052             while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
01053             if (!ds->bsize) ds = NULL;
01054             fixupSize = 0;
01055         }
01056 
01057         /*
01058          * Retrieve all records that apply to this file. Note that the
01059          * file info records were built in the same order as the packages
01060          * will be installed and removed so the records for an overlapped
01061          * files will be sorted in exactly the same order.
01062          */
01063         htGetEntry(ht, &fi->fps[i], (const void ***) &recs, &numRecs, NULL);
01064 
01065         /*
01066          * If this package is being added, look only at other packages
01067          * being added -- removed packages dance to a different tune.
01068          * If both this and the other package are being added, overlapped
01069          * files must be identical (or marked as a conflict). The
01070          * disposition of already installed config files leads to
01071          * a small amount of extra complexity.
01072          *
01073          * If this package is being removed, then there are two cases that
01074          * need to be worried about:
01075          * If the other package is being added, then skip any overlapped files
01076          * so that this package removal doesn't nuke the overlapped files
01077          * that were just installed.
01078          * If both this and the other package are being removed, then each
01079          * file removal from preceding packages needs to be skipped so that
01080          * the file removal occurs only on the last occurence of an overlapped
01081          * file in the transaction set.
01082          *
01083          */
01084 
01085         /* Locate this overlapped file in the set of added/removed packages. */
01086         for (j = 0; j < numRecs && recs[j] != fi; j++)
01087             ;
01088 
01089         /* Find what the previous disposition of this file was. */
01090         otherFileNum = -1;                      /* keep gcc quiet */
01091         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
01092             /* Added packages need only look at other added packages. */
01093             if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
01094                 continue;
01095 
01096             /* TESTME: there are more efficient searches in the world... */
01097             for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
01098                  otherFileNum++) {
01099 
01100                 /* If the addresses are the same, so are the values. */
01101                 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
01102                     break;
01103 
01104                 /* Otherwise, compare fingerprints by value. */
01105                 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
01106                         break;
01107 
01108             }
01109             /* XXX is this test still necessary? */
01110             if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
01111                 break;
01112         }
01113 
01114         switch (fi->type) {
01115         struct stat sb;
01116         case TR_ADDED:
01117             if (otherPkgNum < 0) {
01118                 /* XXX is this test still necessary? */
01119                 if (fi->actions[i] != FA_UNKNOWN)
01120                     break;
01121                 if ((fi->fflags[i] & RPMFILE_CONFIG) && 
01122                         !lstat(filespec, &sb)) {
01123                     /* Here is a non-overlapped pre-existing config file. */
01124                     fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
01125                         ? FA_ALTNAME : FA_BACKUP;
01126                 } else {
01127                     fi->actions[i] = FA_CREATE;
01128                 }
01129                 break;
01130             }
01131 
01132             /* Mark added overlapped non-identical files as a conflict. */
01133             if (probs && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
01134                         recs[otherPkgNum]->fmd5s[otherFileNum],
01135                         recs[otherPkgNum]->flinks[otherFileNum],
01136                         fi->fmodes[i],
01137                         fi->fmd5s[i],
01138                         fi->flinks[i])) {
01139                 psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap->key,
01140                         fi->ap->h, filespec, NULL, recs[otherPkgNum]->ap->h, 0);
01141             }
01142 
01143             /* Try to get the disk accounting correct even if a conflict. */
01144             fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
01145 
01146             if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
01147                 /* Here is an overlapped  pre-existing config file. */
01148                 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
01149                         ? FA_ALTNAME : FA_SKIP;
01150             } else {
01151                 fi->actions[i] = FA_CREATE;
01152             }
01153             break;
01154         case TR_REMOVED:
01155             if (otherPkgNum >= 0) {
01156                 /* Here is an overlapped added file we don't want to nuke. */
01157                 if (recs[otherPkgNum]->actions[otherFileNum] != FA_REMOVE) {
01158                     /* On updates, don't remove files. */
01159                     fi->actions[i] = FA_SKIP;
01160                     break;
01161                 }
01162                 /* Here is an overlapped removed file: skip in previous. */
01163                 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
01164             }
01165             if (XFA_SKIPPING(fi->actions[i]))
01166                 break;
01167             if (fi->fstates[i] != RPMFILE_STATE_NORMAL)
01168                 break;
01169             if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
01170                 fi->actions[i] = FA_REMOVE;
01171                 break;
01172             }
01173                 
01174             /* Here is a pre-existing modified config file that needs saving. */
01175             {   char mdsum[50];
01176                 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
01177                     fi->actions[i] = FA_BACKUP;
01178                     break;
01179                 }
01180             }
01181             fi->actions[i] = FA_REMOVE;
01182             break;
01183         }
01184 
01185         if (ds) {
01186             uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
01187 
01188             switch (fi->actions[i]) {
01189               case FA_BACKUP:
01190               case FA_SAVE:
01191               case FA_ALTNAME:
01192                 ds->ineeded++;
01193                 ds->bneeded += s;
01194                 break;
01195 
01196             /* FIXME: If a two packages share a file (same md5sum), and
01197              * that file is being replaced on disk, will ds->bneeded get
01198              * decremented twice? Quite probably!
01199              */
01200               case FA_CREATE:
01201                 ds->ineeded++;
01202                 ds->bneeded += s;
01203                 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
01204                 break;
01205 
01206               case FA_REMOVE:
01207                 ds->ineeded--;
01208                 ds->bneeded -= s;
01209                 break;
01210 
01211               default:
01212                 break;
01213             }
01214 
01215             ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
01216         }
01217     }
01218     if (filespec) free(filespec);
01219 }
01220 
01221 static int ensureOlder(/*@unused@*/ rpmdb rpmdb, Header new, Header old, rpmProblemSet probs,
01222                         /*@dependent@*/ const void * key)
01223 {
01224     int result, rc = 0;
01225 
01226     if (old == NULL) return 1;
01227 
01228     result = rpmVersionCompare(old, new);
01229     if (result <= 0)
01230         rc = 0;
01231     else if (result > 0) {
01232         rc = 1;
01233         psAppend(probs, RPMPROB_OLDPACKAGE, key, new, NULL, NULL, old, 0);
01234     }
01235 
01236     return rc;
01237 }
01238 
01239 static void skipFiles(TFI_t * fi, int noDocs)
01240 {
01241     int i;
01242     char ** netsharedPaths = NULL;
01243     const char ** fileLangs;
01244     const char ** languages;
01245     const char * s;
01246 
01247     if (!noDocs)
01248         noDocs = rpmExpandNumeric("%{_excludedocs}");
01249 
01250     {   const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
01251         if (tmpPath && *tmpPath != '%')
01252             netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
01253         free((void *)tmpPath);
01254     }
01255 
01256     if (!headerGetEntry(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fileLangs,
01257                         NULL))
01258         fileLangs = NULL;
01259 
01260     s = rpmExpand("%{_install_langs}", NULL);
01261     if (!(s && *s != '%')) {
01262         if (s) free((void *)s);
01263         s = NULL;
01264     }
01265     if (s) {
01266         languages = (const char **) splitString(s, strlen(s), ':');
01267         free((void *)s);
01268     } else
01269         languages = NULL;
01270 
01271     for (i = 0; i < fi->fc; i++) {
01272         char **nsp;
01273 
01274         /* Don't bother with skipped files */
01275         if (XFA_SKIPPING(fi->actions[i]))
01276             continue;
01277 
01278         /*
01279          * Skip net shared paths.
01280          * Net shared paths are not relative to the current root (though
01281          * they do need to take package relocations into account).
01282          */
01283         for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
01284             int len;
01285             const char * dir = fi->dnl[fi->dil[i]];
01286 
01287             len = strlen(*nsp);
01288             if (strncmp(dir, *nsp, len))
01289                 continue;
01290 
01291             /* Only directories or complete file paths can be net shared */
01292             if (!(dir[len] == '/' || dir[len] == '\0'))
01293                 continue;
01294             break;
01295         }
01296 
01297         if (nsp && *nsp) {
01298             fi->actions[i] = FA_SKIPNETSHARED;
01299             continue;
01300         }
01301 
01302         /*
01303          * Skip i18n language specific files.
01304          */
01305         if (fileLangs && languages && *fileLangs[i]) {
01306             const char **lang, *l, *le;
01307             for (lang = languages; *lang; lang++) {
01308                 if (!strcmp(*lang, "all"))
01309                     break;
01310                 for (l = fileLangs[i]; *l; l = le) {
01311                     for (le = l; *le && *le != '|'; le++)
01312                         ;
01313                     if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
01314                         break;
01315                     if (*le == '|') le++;       /* skip over | */
01316                 }
01317                 if (*l)    break;
01318             }
01319             if (*lang == NULL) {
01320                 fi->actions[i] = FA_SKIPNSTATE;
01321                 continue;
01322             }
01323         }
01324 
01325         /*
01326          * Skip documentation if requested.
01327          */
01328         if (noDocs && (fi->fflags[i] & RPMFILE_DOC))
01329             fi->actions[i] = FA_SKIPNSTATE;
01330     }
01331 
01332     if (netsharedPaths) freeSplitString(netsharedPaths);
01333     if (fileLangs) free(fileLangs);
01334     if (languages) freeSplitString((char **)languages);
01335 }
01336 
01337 #define NOTIFY(_ts, _al)        if ((_ts)->notify) (void) (_ts)->notify _al
01338 
01339 int rpmRunTransactions( rpmTransactionSet ts,
01340                         rpmCallbackFunction notify, rpmCallbackData notifyData,
01341                         rpmProblemSet okProbs, rpmProblemSet * newProbs,
01342                         rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
01343 {
01344     int i, j;
01345     int rc, ourrc = 0;
01346     struct availablePackage * alp;
01347     Header * hdrs;
01348     int totalFileCount = 0;
01349     hashTable ht;
01350     TFI_t * flList, * fi;
01351     struct sharedFileInfo * shared, * sharedList;
01352     int numShared;
01353     int flEntries;
01354     int nexti;
01355     int lastFailed;
01356     FD_t fd;
01357     const char ** filesystems;
01358     int filesystemCount;
01359     struct diskspaceInfo * di = NULL;
01360     int oc;
01361     fingerPrintCache fpc;
01362 
01363     /* FIXME: what if the same package is included in ts twice? */
01364 
01365     ts->transFlags = transFlags;
01366     ts->notify = notify;
01367     ts->notifyData = notifyData;
01368     ts->ignoreSet = ignoreSet;
01369     if (ts->currDir)
01370         free((void *)ts->currDir);
01371     ts->currDir = currentDirectory();
01372     ts->chrootDone = 0;
01373     {   time_t t;
01374         time(&t);
01375         ts->id = t;
01376     }
01377 
01378     /* Get available space on mounted file systems. */
01379     if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
01380                 !rpmGetFilesystemList(&filesystems, &filesystemCount)) {
01381         struct stat sb;
01382 
01383         di = alloca(sizeof(*di) * (filesystemCount + 1));
01384 
01385         for (i = 0; (i < filesystemCount) && di; i++) {
01386 #if STATFS_IN_SYS_STATVFS
01387             struct statvfs sfb;
01388             memset(&sfb, 0, sizeof(sfb));
01389             if (statvfs(filesystems[i], &sfb))
01390 #else
01391             struct statfs sfb;
01392 #  if STAT_STATFS4
01393 /* this platform has the 4-argument version of the statfs call.  The last two
01394  * should be the size of struct statfs and 0, respectively.  The 0 is the
01395  * filesystem type, and is always 0 when statfs is called on a mounted
01396  * filesystem, as we're doing.
01397  */
01398             memset(&sfb, 0, sizeof(sfb));
01399             if (statfs(filesystems[i], &sfb, sizeof(sfb), 0))
01400 #  else
01401             memset(&sfb, 0, sizeof(sfb));
01402             if (statfs(filesystems[i], &sfb))
01403 #  endif
01404 #endif
01405             {
01406                 di = NULL;
01407             } else {
01408                 di[i].bsize = sfb.f_bsize;
01409                 di[i].bneeded = 0;
01410                 di[i].ineeded = 0;
01411 #ifdef STATFS_HAS_F_BAVAIL
01412                 di[i].bavail = sfb.f_bavail;
01413 #else
01414 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
01415  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
01416  * it's about all we can do.
01417  */
01418                 di[i].bavail = sfb.f_blocks - sfb.f_bfree;
01419 #endif
01420                 /* XXX Avoid FAT and other file systems that have not inodes. */
01421                 di[i].iavail = (!(sfb.f_ffree == 0 && sfb.f_files == 0))
01422                                 ? sfb.f_ffree : -1;
01423 
01424                 stat(filesystems[i], &sb);
01425                 di[i].dev = sb.st_dev;
01426             }
01427         }
01428 
01429         if (di) di[i].bsize = 0;
01430     }
01431 
01432     ts->probs = *newProbs = psCreate();
01433     hdrs = alloca(sizeof(*hdrs) * ts->addedPackages.size);
01434 
01435     /* ===============================================
01436      * For packages being installed:
01437      * - verify package arch/os.
01438      * - verify package epoch:version-release is newer.
01439      * - count files.
01440      * For packages being removed:
01441      * - count files.
01442      */
01443     /* The ordering doesn't matter here */
01444     for (alp = ts->addedPackages.list;
01445         (alp - ts->addedPackages.list) < ts->addedPackages.size;
01446         alp++)
01447     {
01448         if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
01449             psAppend(ts->probs, RPMPROB_BADARCH, alp->key, alp->h,
01450                         NULL, NULL, NULL, 0);
01451 
01452         if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
01453             psAppend(ts->probs, RPMPROB_BADOS, alp->key, alp->h,
01454                         NULL, NULL, NULL, 0);
01455 
01456         if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
01457             rpmdbMatchIterator mi;
01458             Header oldH;
01459             mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
01460             while ((oldH = rpmdbNextIterator(mi)) != NULL)
01461                 ensureOlder(ts->rpmdb, alp->h, oldH, ts->probs, alp->key);
01462             rpmdbFreeIterator(mi);
01463         }
01464 
01465         /* XXX multilib should not display "already installed" problems */
01466         if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !alp->multiLib) {
01467             rpmdbMatchIterator mi;
01468             mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
01469             rpmdbSetIteratorVersion(mi, alp->version);
01470             rpmdbSetIteratorRelease(mi, alp->release);
01471             while (rpmdbNextIterator(mi) != NULL) {
01472                 psAppend(ts->probs, RPMPROB_PKG_INSTALLED, alp->key, alp->h,
01473                         NULL, NULL, NULL, 0);
01474                 break;
01475             }
01476             rpmdbFreeIterator(mi);
01477         }
01478 
01479         totalFileCount += alp->filesCount;
01480 
01481     }
01482 
01483     /* FIXME: it seems a bit silly to read in all of these headers twice */
01484     /* The ordering doesn't matter here */
01485     if (ts->numRemovedPackages > 0) {
01486         rpmdbMatchIterator mi;
01487         Header h;
01488         int fileCount;
01489 
01490         mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES, NULL, 0);
01491         rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
01492         while ((h = rpmdbNextIterator(mi)) != NULL) {
01493             if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
01494                 totalFileCount += fileCount;
01495         }
01496         rpmdbFreeIterator(mi);
01497     }
01498 
01499     /* ===============================================
01500      * Initialize file list:
01501      */
01502     flEntries = ts->addedPackages.size + ts->numRemovedPackages;
01503     flList = alloca(sizeof(*flList) * (flEntries));
01504 
01505     /*
01506      * FIXME?: we'd be better off assembling one very large file list and
01507      * calling fpLookupList only once. I'm not sure that the speedup is
01508      * worth the trouble though.
01509      */
01510     for (fi = flList, oc = 0; oc < ts->orderCount; fi++, oc++) {
01511         const char **preTrans;
01512         int preTransCount;
01513 
01514         memset(fi, 0, sizeof(*fi));
01515         preTrans = NULL;
01516         preTransCount = 0;
01517 
01518         switch (ts->order[oc].type) {
01519         case TR_ADDED:
01520             i = ts->order[oc].u.addedIndex;
01521             alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
01522 
01523             if (!headerGetEntryMinMemory(alp->h, RPMTAG_BASENAMES, NULL,
01524                                          NULL, &fi->fc)) {
01525                 fi->h = headerLink(alp->h);
01526                 hdrs[i] = headerLink(fi->h);
01527                 continue;
01528             }
01529 
01530             /* Allocate file actions (and initialize to FA_UNKNOWN) */
01531             fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
01532             hdrs[i] = relocateFileList(ts, alp, alp->h, fi->actions);
01533             fi->h = headerLink(hdrs[i]);
01534             fi->ap = alp;
01535             fi->type = TR_ADDED;
01536             break;
01537         case TR_REMOVED:
01538             fi->record = ts->order[oc].u.removed.dboffset;
01539             {   rpmdbMatchIterator mi;
01540 
01541                 mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES,
01542                         &fi->record, sizeof(fi->record));
01543                 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
01544                     fi->h = headerLink(fi->h);
01545                 rpmdbFreeIterator(mi);
01546             }
01547             if (fi->h == NULL) {
01548                 /* ACK! */
01549                 continue;
01550             }
01551             fi->type = TR_REMOVED;
01552             break;
01553         }
01554         if (!headerGetEntry(fi->h, RPMTAG_BASENAMES, NULL,
01555                                      (void **) &fi->bnl, &fi->fc)) {
01556             /* This catches removed packages w/ no file lists */
01557             fi->dc = fi->fc = 0;
01558             continue;
01559         }
01560 
01561         headerGetEntry(fi->h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, 
01562                        NULL);
01563         headerGetEntry(fi->h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, 
01564                        &fi->dc);
01565 
01566         /* actions is initialized earlier for added packages */
01567         if (fi->actions == NULL)
01568             fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
01569 
01570         headerGetEntry(fi->h, RPMTAG_FILEMODES, NULL,
01571                                 (void **) &fi->fmodes, NULL);
01572         headerGetEntry(fi->h, RPMTAG_FILEFLAGS, NULL,
01573                                 (void **) &fi->fflags, NULL);
01574         headerGetEntry(fi->h, RPMTAG_FILESIZES, NULL,
01575                                 (void **) &fi->fsizes, NULL);
01576         headerGetEntry(fi->h, RPMTAG_FILESTATES, NULL,
01577                                 (void **) &fi->fstates, NULL);
01578 
01579         switch (ts->order[oc].type) {
01580         case TR_REMOVED:
01581             headerGetEntry(fi->h, RPMTAG_FILEMD5S, NULL,
01582                                     (void **) &fi->fmd5s, NULL);
01583             headerGetEntry(fi->h, RPMTAG_FILELINKTOS, NULL,
01584                                     (void **) &fi->flinks, NULL);
01585             fi->fsizes = memcpy(xmalloc(fi->fc * sizeof(*fi->fsizes)),
01586                                 fi->fsizes, fi->fc * sizeof(*fi->fsizes));
01587             fi->fflags = memcpy(xmalloc(fi->fc * sizeof(*fi->fflags)),
01588                                 fi->fflags, fi->fc * sizeof(*fi->fflags));
01589             fi->fmodes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmodes)),
01590                                 fi->fmodes, fi->fc * sizeof(*fi->fmodes));
01591             /* XXX there's a tedious segfault here for some version(s) of rpm */
01592             if (fi->fstates)
01593                 fi->fstates = memcpy(xmalloc(fi->fc * sizeof(*fi->fstates)),
01594                                 fi->fstates, fi->fc * sizeof(*fi->fstates));
01595             else
01596                 fi->fstates = xcalloc(1, fi->fc * sizeof(*fi->fstates));
01597             fi->dil = memcpy(xmalloc(fi->fc * sizeof(*fi->dil)),
01598                                 fi->dil, fi->fc * sizeof(*fi->dil));
01599             headerFree(fi->h);
01600             fi->h = NULL;
01601             break;
01602         case TR_ADDED:
01603             headerGetEntryMinMemory(fi->h, RPMTAG_FILEMD5S, NULL,
01604                                     (const void **) &fi->fmd5s, NULL);
01605             headerGetEntryMinMemory(fi->h, RPMTAG_FILELINKTOS, NULL,
01606                                     (const void **) &fi->flinks, NULL);
01607 
01608             /* 0 makes for noops */
01609             fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
01610 
01611             /* Skip netshared paths, not our i18n files, and excluded docs */
01612             skipFiles(fi, ts->transFlags & RPMTRANS_FLAG_NODOCS);
01613             break;
01614         }
01615 
01616         fi->fps = xmalloc(sizeof(*fi->fps) * fi->fc);
01617     }
01618 
01619     /* Open all database indices before installing. */
01620     rpmdbOpenAll(ts->rpmdb);
01621 
01622     chdir("/");
01623     /*@-unrecog@*/ chroot(ts->rootDir); /*@=unrecog@*/
01624     ts->chrootDone = 1;
01625 
01626     ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
01627     fpc = fpCacheCreate(totalFileCount);
01628 
01629     /* ===============================================
01630      * Add fingerprint for each file not skipped.
01631      */
01632     for (fi = flList; (fi - flList) < flEntries; fi++) {
01633         fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
01634         for (i = 0; i < fi->fc; i++) {
01635             if (XFA_SKIPPING(fi->actions[i]))
01636                 continue;
01637             htAddEntry(ht, fi->fps + i, fi);
01638         }
01639     }
01640 
01641     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, flEntries,
01642         NULL, ts->notifyData));
01643 
01644     /* ===============================================
01645      * Compute file disposition for each package in transaction set.
01646      */
01647     for (fi = flList; (fi - flList) < flEntries; fi++) {
01648         dbiIndexSet * matches;
01649         int knownBad;
01650 
01651         NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - flList), flEntries,
01652                NULL, ts->notifyData));
01653 
01654         if (fi->fc == 0) continue;
01655 
01656         /* Extract file info for all files in this package from the database. */
01657         matches = xcalloc(sizeof(*matches), fi->fc);
01658         if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc))
01659             return 1;
01660 
01661         numShared = 0;
01662         for (i = 0; i < fi->fc; i++)
01663             numShared += dbiIndexSetCount(matches[i]);
01664 
01665         /* Build sorted file info list for this package. */
01666         shared = sharedList = xmalloc(sizeof(*sharedList) * (numShared + 1));
01667         for (i = 0; i < fi->fc; i++) {
01668             /*
01669              * Take care not to mark files as replaced in packages that will
01670              * have been removed before we will get here.
01671              */
01672             for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
01673                 int k, ro;
01674                 ro = dbiIndexRecordOffset(matches[i], j);
01675                 knownBad = 0;
01676                 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
01677                     switch (ts->order[k].type) {
01678                     case TR_REMOVED:
01679                         if (ts->order[k].u.removed.dboffset == ro)
01680                             knownBad = ro;
01681                         break;
01682                     case TR_ADDED:
01683                         break;
01684                     }
01685                 }
01686 
01687                 shared->pkgFileNum = i;
01688                 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
01689                 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
01690                 shared->isRemoved = (knownBad == ro);
01691                 shared++;
01692             }
01693             if (matches[i]) {
01694                 dbiFreeIndexSet(matches[i]);
01695                 matches[i] = NULL;
01696             }
01697         }
01698         numShared = shared - sharedList;
01699         shared->otherPkg = -1;
01700         free((void *)matches);
01701 
01702         /* Sort file info by other package index (otherPkg) */
01703         qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
01704 
01705         /* For all files from this package that are in the database ... */
01706         for (i = 0; i < numShared; i = nexti) {
01707             int beingRemoved;
01708 
01709             shared = sharedList + i;
01710 
01711             /* Find the end of the files in the other package. */
01712             for (nexti = i + 1; nexti < numShared; nexti++) {
01713                 if (sharedList[nexti].otherPkg != shared->otherPkg)
01714                     break;
01715             }
01716 
01717             /* Is this file from a package being removed? */
01718             beingRemoved = 0;
01719             for (j = 0; j < ts->numRemovedPackages; j++) {
01720                 if (ts->removedPackages[j] != shared->otherPkg)
01721                     continue;
01722                 beingRemoved = 1;
01723                 break;
01724             }
01725 
01726             /* Determine the fate of each file. */
01727             switch (fi->type) {
01728             case TR_ADDED:
01729                 handleInstInstalledFiles(fi, ts->rpmdb, shared, nexti - i,
01730                 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)),
01731                          ts->probs, ts->transFlags);
01732                 break;
01733             case TR_REMOVED:
01734                 if (!beingRemoved)
01735                     handleRmvdInstalledFiles(fi, ts->rpmdb, shared, nexti - i);
01736                 break;
01737             }
01738         }
01739 
01740         free(sharedList);
01741 
01742         /* Update disk space needs on each partition for this package. */
01743         handleOverlappedFiles(fi, ht,
01744                ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
01745                     ? NULL : ts->probs), di);
01746 
01747         /* Check added package has sufficient space on each partition used. */
01748         switch (fi->type) {
01749         case TR_ADDED:
01750             if (!(di && fi->fc))
01751                 break;
01752             for (i = 0; i < filesystemCount; i++) {
01753                 struct diskspaceInfo * dip = di + i;
01754 
01755                 /* XXX Avoid FAT and other file systems that have not inodes. */
01756                 if (dip->iavail <= 0)
01757                     continue;
01758 
01759                 if (adj_fs_blocks(dip->bneeded) > dip->bavail)
01760                     psAppend(ts->probs, RPMPROB_DISKSPACE, fi->ap->key,
01761                         fi->ap->h, filesystems[i], NULL, NULL,
01762                    (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
01763 
01764                 if (adj_fs_blocks(dip->ineeded) > dip->iavail)
01765                     psAppend(ts->probs, RPMPROB_DISKNODES, fi->ap->key,
01766                         fi->ap->h, filesystems[i], NULL, NULL,
01767                     (adj_fs_blocks(dip->ineeded) - dip->iavail));
01768             }
01769             break;
01770         case TR_REMOVED:
01771             break;
01772         }
01773     }
01774 
01775     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, flEntries,
01776         NULL, ts->notifyData));
01777 
01778     chroot(".");
01779     ts->chrootDone = 0;
01780     chdir(ts->currDir);
01781 
01782     /* ===============================================
01783      * Free unused memory as soon as possible.
01784      */
01785 
01786     for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
01787         if (fi->fc == 0)
01788             continue;
01789         free(fi->bnl); fi->bnl = NULL;
01790         free(fi->dnl); fi->dnl = NULL;
01791         switch (fi->type) {
01792         case TR_ADDED:
01793             free(fi->fmd5s); fi->fmd5s = NULL;
01794             free(fi->flinks); fi->flinks = NULL;
01795             free(fi->fps); fi->fps = NULL;
01796             break;
01797         case TR_REMOVED:
01798             free((void *)fi->dil); fi->dil = NULL;
01799             free(fi->fps); fi->fps = NULL;
01800             break;
01801         }
01802     }
01803 
01804     fpCacheFree(fpc);
01805     htFree(ht);
01806 
01807     /* ===============================================
01808      * If unfiltered problems exist, free memory and return.
01809      */
01810     if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS) ||
01811            (ts->probs->numProblems && (!okProbs || psTrim(okProbs, ts->probs)))) {
01812         *newProbs = ts->probs;
01813 
01814         for (alp = ts->addedPackages.list, fi = flList;
01815                 (alp - ts->addedPackages.list) < ts->addedPackages.size;
01816                 alp++, fi++) {
01817             headerFree(hdrs[alp - ts->addedPackages.list]);
01818         }
01819 
01820         freeFl(ts, flList);
01821         return ts->orderCount;
01822     }
01823 
01824     /* ===============================================
01825      * Install and remove packages.
01826      */
01827 
01828     lastFailed = -2;
01829     for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
01830         switch (ts->order[oc].type) {
01831         case TR_ADDED:
01832             alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
01833             i = ts->order[oc].u.addedIndex;
01834 
01835             if ((fd = alp->fd) == 0) {
01836                 fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
01837                             alp->key, ts->notifyData);
01838                 if (fd) {
01839                     Header h;
01840 
01841                     headerFree(hdrs[i]);
01842                     rc = rpmReadPackageHeader(fd, &h, NULL, NULL, NULL);
01843                     if (rc) {
01844                         (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
01845                                     alp->key, ts->notifyData);
01846                         ourrc++;
01847                         fd = NULL;
01848                     } else {
01849                         hdrs[i] = relocateFileList(ts, alp, h, NULL);
01850                         headerFree(h);
01851                     }
01852                 }
01853             }
01854 
01855             if (fd) {
01856 
01857                 if (alp->multiLib)
01858                     ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
01859 
01860                 if (installBinaryPackage(ts, fd, hdrs[i],
01861                                          alp->key, fi->actions,
01862                                          fi->fc ? fi->replaced : NULL)) {
01863                     ourrc++;
01864                     lastFailed = i;
01865                 }
01866             } else {
01867                 ourrc++;
01868                 lastFailed = i;
01869             }
01870 
01871             headerFree(hdrs[i]);
01872 
01873             if (alp->fd == NULL && fd)
01874                 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
01875                         alp->key, ts->notifyData);
01876             break;
01877         case TR_REMOVED:
01878           { unsigned int offset = fi->record;
01879             Header dbh;
01880 
01881             /* If install failed, then we shouldn't erase. */
01882             if (ts->order[oc].u.removed.dependsOnIndex == lastFailed)
01883                 break;
01884 
01885             {   rpmdbMatchIterator mi = NULL;
01886 
01887                 mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES,
01888                                 &offset, sizeof(offset));
01889 
01890                 dbh = rpmdbNextIterator(mi);
01891                 if (dbh == NULL) {
01892                     rpmdbFreeIterator(mi);
01893                     ourrc++;
01894                     break;
01895                 }
01896                 dbh = headerLink(dbh);
01897                 rpmdbFreeIterator(mi);
01898             }
01899 
01900             if (removeBinaryPackage(ts, offset, dbh, NULL, fi->actions))
01901                 ourrc++;
01902 
01903             headerFree(dbh);
01904           } break;
01905         }
01906         (void) rpmdbSync(ts->rpmdb);
01907     }
01908 
01909     freeFl(ts, flList);
01910 
01911     if (ourrc)
01912         return -1;
01913     else
01914         return 0;
01915 }

Generated at Sun Apr 8 18:43:01 2001 for rpm by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000