5 #include <sys/signal.h>
9 #include <rpmmacro.h> /* XXX for rpmGetPath */
12 /*@access dbiIndexSet@*/
13 /*@access dbiIndexRecord@*/
20 extern int _noDirTokens;
22 const char *rpmdb_filenames[] = {
34 /* XXX the signal handling in here is not thread safe */
36 /* the requiredbyIndex isn't stricly necessary. In a perfect world, we could
37 have each header keep a list of packages that need it. However, we
38 can't reserve space in the header for extra information so all of the
39 required packages would move in the database every time a package was
40 added or removed. Instead, each package (or virtual package) name
41 keeps a list of package offsets of packages that might depend on this
42 one. Version numbers still need verification, but it gets us in the
43 right area w/o a linear search through the database. */
47 dbiIndex nameIndex, fileIndex, groupIndex, providesIndex;
48 dbiIndex requiredbyIndex, conflictsIndex, triggerIndex;
51 static sigset_t signalMask;
53 static void blockSignals(void)
57 sigfillset(&newMask); /* block all signals */
58 sigprocmask(SIG_BLOCK, &newMask, &signalMask);
61 static void unblockSignals(void)
63 sigprocmask(SIG_SETMASK, &signalMask, NULL);
66 static int openDbFile(const char * prefix, const char * dbpath, const char * shortName,
67 int justCheck, int mode, int perms, dbiIndex * db, DBI_TYPE type)
69 int len = (prefix ? strlen(prefix) : 0) + strlen(dbpath) + strlen(shortName) + 1;
70 char * filename = alloca(len);
73 switch (urlIsURL(dbpath)) {
75 if (prefix && *prefix) strcat(filename, prefix);
80 strcat(filename, dbpath);
81 strcat(filename, shortName);
83 if (!justCheck || !rpmfileexists(filename)) {
84 if ((*db = dbiOpenIndex(filename, mode, perms, type)) == NULL)
91 static /*@only@*/ rpmdb newRpmdb(void)
93 rpmdb db = xmalloc(sizeof(*db));
97 db->groupIndex = NULL;
98 db->providesIndex = NULL;
99 db->requiredbyIndex = NULL;
100 db->conflictsIndex = NULL;
101 db->triggerIndex = NULL;
105 int openDatabase(const char * prefix, const char * dbpath, rpmdb *rpmdbp, int mode,
106 int perms, int flags)
111 struct flock lockinfo;
112 int justcheck = flags & RPMDB_FLAG_JUSTCHECK;
113 int minimal = flags & RPMDB_FLAG_MINIMAL;
119 if (!(perms & 0600)) /* XXX sanity */
122 /* we should accept NULL as a valid prefix */
123 if (!prefix) prefix="";
126 if (dbpath[i - 1] != '/') {
127 filename = alloca(i + 2);
128 strcpy(filename, dbpath);
130 filename[i + 1] = '\0';
134 filename = alloca(strlen(prefix) + strlen(dbpath) + 40);
137 switch (urlIsURL(dbpath)) {
139 strcat(filename, prefix);
144 strcat(filename, dbpath);
145 (void)rpmCleanPath(filename);
147 rpmMessage(RPMMESS_DEBUG, _("opening database mode 0x%x in %s\n"),
150 if (filename[strlen(filename)-1] != '/')
151 strcat(filename, "/");
152 strcat(filename, "packages.rpm");
156 if (!justcheck || !rpmfileexists(filename)) {
157 db->pkgs = fadOpen(filename, mode, perms);
158 if (Ferror(db->pkgs)) {
159 rpmError(RPMERR_DBOPEN, _("failed to open %s: %s\n"), filename,
160 Fstrerror(db->pkgs));
164 /* try and get a lock - this is released by the kernel when we close
166 lockinfo.l_whence = 0;
167 lockinfo.l_start = 0;
171 lockinfo.l_type = F_WRLCK;
172 if (Fcntl(db->pkgs, F_SETLK, (void *) &lockinfo)) {
173 rpmError(RPMERR_FLOCK, _("cannot get %s lock on database"),
179 lockinfo.l_type = F_RDLCK;
180 if (Fcntl(db->pkgs, F_SETLK, (void *) &lockinfo)) {
181 rpmError(RPMERR_FLOCK, _("cannot get %s lock on database"),
189 rc = openDbFile(prefix, dbpath, "nameindex.rpm", justcheck, mode, perms,
190 &db->nameIndex, DBI_HASH);
193 *rpmdbp = xmalloc(sizeof(struct rpmdb_s));
195 *rpmdbp = db; /* structure assignment */
202 rc = openDbFile(prefix, dbpath, "fileindex.rpm", justcheck, mode, perms,
203 &db->fileIndex, DBI_HASH);
205 /* We used to store the fileindexes as complete paths, rather then
206 plain basenames. Let's see which version we are... */
208 * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
209 * XXX db1/db2 linkage) conditions.
211 if (!justcheck && !dbiGetFirstKey(db->fileIndex, &akey)) {
212 if (strchr(akey, '/')) {
213 rpmError(RPMERR_OLDDB, _("old format database is present; "
214 "use --rebuilddb to generate a new format database"));
221 rc = openDbFile(prefix, dbpath, "providesindex.rpm", justcheck, mode, perms,
222 &db->providesIndex, DBI_HASH);
224 rc = openDbFile(prefix, dbpath, "requiredby.rpm", justcheck, mode, perms,
225 &db->requiredbyIndex, DBI_HASH);
227 rc = openDbFile(prefix, dbpath, "conflictsindex.rpm", justcheck, mode, perms,
228 &db->conflictsIndex, DBI_HASH);
230 rc = openDbFile(prefix, dbpath, "groupindex.rpm", justcheck, mode, perms,
231 &db->groupIndex, DBI_HASH);
233 rc = openDbFile(prefix, dbpath, "triggerindex.rpm", justcheck, mode, perms,
234 &db->triggerIndex, DBI_HASH);
236 if (rc || justcheck || rpmdbp == NULL)
244 static int doRpmdbOpen (const char * prefix, /*@out@*/ rpmdb * rpmdbp,
245 int mode, int perms, int flags)
247 const char * dbpath = rpmGetPath("%{_dbpath}", NULL);
250 if (!(dbpath && dbpath[0] != '%')) {
251 rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
254 rc = openDatabase(prefix, dbpath, rpmdbp, mode, perms, flags);
259 int rpmdbOpenForTraversal(const char * prefix, rpmdb * rpmdbp)
261 return doRpmdbOpen(prefix, rpmdbp, O_RDONLY, 0644, RPMDB_FLAG_MINIMAL);
264 int rpmdbOpen (const char * prefix, rpmdb *rpmdbp, int mode, int perms)
266 return doRpmdbOpen(prefix, rpmdbp, mode, perms, 0);
269 int rpmdbInit (const char * prefix, int perms)
272 return doRpmdbOpen(prefix, &db, (O_CREAT | O_RDWR), perms, RPMDB_FLAG_JUSTCHECK);
275 void rpmdbClose (rpmdb db)
277 if (db->pkgs != NULL) Fclose(db->pkgs);
278 if (db->fileIndex) dbiCloseIndex(db->fileIndex);
279 if (db->groupIndex) dbiCloseIndex(db->groupIndex);
280 if (db->nameIndex) dbiCloseIndex(db->nameIndex);
281 if (db->providesIndex) dbiCloseIndex(db->providesIndex);
282 if (db->requiredbyIndex) dbiCloseIndex(db->requiredbyIndex);
283 if (db->conflictsIndex) dbiCloseIndex(db->conflictsIndex);
284 if (db->triggerIndex) dbiCloseIndex(db->triggerIndex);
288 int rpmdbFirstRecNum(rpmdb db) {
289 return fadFirstOffset(db->pkgs);
292 int rpmdbNextRecNum(rpmdb db, unsigned int lastOffset) {
294 return fadNextOffset(db->pkgs, lastOffset);
297 static Header doGetRecord(rpmdb db, unsigned int offset, int pristine)
300 const char ** fileNames;
304 (void)Fseek(db->pkgs, offset, SEEK_SET);
306 h = headerRead(db->pkgs, HEADER_MAGIC_NO);
308 if (pristine || h == NULL) return h;
310 /* the RPM used to build much of RH 5.1 could produce packages whose
311 file lists did not have leading /'s. Now is a good time to fix
314 /* If this tag isn't present, either no files are in the package or
315 we're dealing with a package that has just the compressed file name
317 if (!headerGetEntryMinMemory(h, RPMTAG_OLDFILENAMES, NULL,
318 (void **) &fileNames, &fileCount)) return h;
320 for (i = 0; i < fileCount; i++)
321 if (*fileNames[i] != '/') break;
323 if (i == fileCount) {
325 } else { /* bad header -- let's clean it up */
326 const char ** newFileNames = alloca(sizeof(*newFileNames) * fileCount);
327 for (i = 0; i < fileCount; i++) {
328 char * newFileName = alloca(strlen(fileNames[i]) + 2);
329 if (*fileNames[i] != '/') {
330 newFileName[0] = '/';
331 newFileName[1] = '\0';
333 newFileName[0] = '\0';
334 strcat(newFileName, fileNames[i]);
335 newFileNames[i] = newFileName;
340 headerModifyEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
341 newFileNames, fileCount);
344 /* The file list was moved to a more compressed format which not
345 only saves memory (nice), but gives fingerprinting a nice, fat
346 speed boost (very nice). Go ahead and convert old headers to
347 the new style (this is a noop for new headers) */
353 Header rpmdbGetRecord(rpmdb db, unsigned int offset)
355 return doGetRecord(db, offset, 0);
358 int rpmdbFindByFile(rpmdb db, const char * filespec, dbiIndexSet * matches)
360 const char * dirName;
361 const char * baseName;
362 fingerPrint fp1, fp2;
363 dbiIndexSet allMatches = NULL;
365 fingerPrintCache fpc;
368 if ((baseName = strrchr(filespec, '/')) != NULL) {
372 len = baseName - filespec + 1;
373 t = strncpy(alloca(len + 1), filespec, len);
382 fpc = fpCacheCreate(20);
383 fp1 = fpLookup(fpc, dirName, baseName, 1);
385 rc = dbiSearchIndex(db->fileIndex, baseName, &allMatches);
387 dbiFreeIndexSet(allMatches);
393 *matches = dbiCreateIndexSet();
395 while (i < dbiIndexSetCount(allMatches)) {
396 const char ** baseNames, ** dirNames;
398 unsigned int recoff = dbiIndexRecordOffset(allMatches, i);
399 unsigned int prevoff;
402 if ((h = rpmdbGetRecord(db, recoff)) == NULL) {
407 headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL,
408 (void **) &baseNames, NULL);
409 headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL,
410 (void **) &dirIndexes, NULL);
411 headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL,
412 (void **) &dirNames, NULL);
415 int num = dbiIndexRecordFileNumber(allMatches, i);
417 fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
418 if (FP_EQUAL(fp1, fp2))
419 dbiAppendIndexRecord(*matches, dbiIndexRecordOffset(allMatches, i), dbiIndexRecordFileNumber(allMatches, i));
423 recoff = dbiIndexRecordOffset(allMatches, i);
424 } while (i < dbiIndexSetCount(allMatches) &&
425 (i == 0 || recoff == prevoff));
433 dbiFreeIndexSet(allMatches);
439 if (dbiIndexSetCount(*matches) == 0) {
440 dbiFreeIndexSet(*matches);
448 int rpmdbFindByProvides(rpmdb db, const char * filespec, dbiIndexSet * matches) {
449 return dbiSearchIndex(db->providesIndex, filespec, matches);
452 int rpmdbFindByRequiredBy(rpmdb db, const char * filespec, dbiIndexSet * matches) {
453 return dbiSearchIndex(db->requiredbyIndex, filespec, matches);
456 int rpmdbFindByConflicts(rpmdb db, const char * filespec, dbiIndexSet * matches) {
457 return dbiSearchIndex(db->conflictsIndex, filespec, matches);
460 int rpmdbFindByTriggeredBy(rpmdb db, const char * filespec, dbiIndexSet * matches) {
461 return dbiSearchIndex(db->triggerIndex, filespec, matches);
464 int rpmdbFindByGroup(rpmdb db, const char * group, dbiIndexSet * matches) {
465 return dbiSearchIndex(db->groupIndex, group, matches);
468 int rpmdbFindPackage(rpmdb db, const char * name, dbiIndexSet * matches) {
469 return dbiSearchIndex(db->nameIndex, name, matches);
472 static void removeIndexEntry(dbiIndex dbi, const char * key, dbiIndexRecord rec,
473 int tolerant, const char * idxName)
475 dbiIndexSet matches = NULL;
478 rc = dbiSearchIndex(dbi, key, &matches);
481 if (dbiRemoveIndexRecord(matches, rec) && !tolerant) {
482 rpmError(RPMERR_DBCORRUPT, _("package %s not listed in %s"),
485 dbiUpdateIndex(dbi, key, matches);
486 /* errors from above will be reported from dbindex.c */
491 rpmError(RPMERR_DBCORRUPT, _("package %s not found in %s"),
495 break; /* error message already generated from dbindex.c */
498 dbiFreeIndexSet(matches);
503 int rpmdbRemove(rpmdb db, unsigned int offset, int tolerant)
506 char * name, * group;
510 char ** baseNames, ** providesList, ** requiredbyList;
511 char ** conflictList, ** triggerList;
515 h = rpmdbGetRecord(db, offset);
517 rpmError(RPMERR_DBCORRUPT, _("cannot read header at %d for uninstall"),
522 rec = dbiReturnIndexRecordInstance(offset, 0);
526 if (!headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count)) {
527 rpmError(RPMERR_DBCORRUPT, _("package has no name"));
529 rpmMessage(RPMMESS_DEBUG, _("removing name index\n"));
530 removeIndexEntry(db->nameIndex, name, rec, tolerant, "name index");
533 if (!headerGetEntry(h, RPMTAG_GROUP, &type, (void **) &group, &count)) {
534 rpmMessage(RPMMESS_DEBUG, _("package has no group\n"));
536 rpmMessage(RPMMESS_DEBUG, _("removing group index\n"));
537 removeIndexEntry(db->groupIndex, group, rec, tolerant, "group index");
540 if (headerGetEntry(h, RPMTAG_PROVIDENAME, &type, (void **) &providesList,
542 for (i = 0; i < count; i++) {
543 rpmMessage(RPMMESS_DEBUG, _("removing provides index for %s\n"),
545 removeIndexEntry(db->providesIndex, providesList[i], rec, tolerant,
546 "providesfile index");
551 if (headerGetEntry(h, RPMTAG_REQUIRENAME, &type, (void **) &requiredbyList,
553 /* There could be dups in requiredByLIst, and the list is sorted.
554 Rather then sort the list, be tolerant of missing entries
555 as they should just indicate duplicated requirements. */
557 for (i = 0; i < count; i++) {
558 rpmMessage(RPMMESS_DEBUG, _("removing requiredby index for %s\n"),
560 removeIndexEntry(db->requiredbyIndex, requiredbyList[i], rec,
561 1, "requiredby index");
563 free(requiredbyList);
566 if (headerGetEntry(h, RPMTAG_TRIGGERNAME, &type, (void **) &triggerList,
568 /* triggerList often contains duplicates */
569 for (i = 0; i < count; i++) {
570 rpmMessage(RPMMESS_DEBUG, _("removing trigger index for %s\n"),
572 removeIndexEntry(db->triggerIndex, triggerList[i], rec,
578 if (headerGetEntry(h, RPMTAG_CONFLICTNAME, &type, (void **) &conflictList,
580 for (i = 0; i < count; i++) {
581 rpmMessage(RPMMESS_DEBUG, _("removing conflict index for %s\n"),
583 removeIndexEntry(db->conflictsIndex, conflictList[i], rec,
584 tolerant, "conflict index");
589 if (headerGetEntry(h, RPMTAG_BASENAMES, &type, (void **) &baseNames,
591 for (i = 0; i < count; i++) {
592 rpmMessage(RPMMESS_DEBUG, _("removing file index for %s\n"),
594 /* structure assignment */
595 rec = dbiReturnIndexRecordInstance(offset, i);
596 removeIndexEntry(db->fileIndex, baseNames[i], rec, tolerant,
601 rpmMessage(RPMMESS_DEBUG, _("package has no files\n"));
604 fadFree(db->pkgs, offset);
606 dbiSyncIndex(db->nameIndex);
607 dbiSyncIndex(db->groupIndex);
608 dbiSyncIndex(db->fileIndex);
612 dbiFreeIndexRecordInstance(rec);
618 static int addIndexEntry(dbiIndex idx, const char *index, unsigned int offset,
619 unsigned int fileNumber)
621 dbiIndexSet set = NULL;
624 rc = dbiSearchIndex(idx, index, &set);
628 dbiFreeIndexSet(set);
632 /*@notreached@*/ break;
633 case 1: /* new item */
634 set = dbiCreateIndexSet();
640 dbiAppendIndexRecord(set, offset, fileNumber);
641 if (dbiUpdateIndex(idx, index, set))
642 exit(EXIT_FAILURE); /* XXX W2DO? return 1; */
645 dbiFreeIndexSet(set);
652 int rpmdbAdd(rpmdb db, Header dbentry)
654 unsigned int dboffset;
656 const char ** baseNames;
657 const char ** providesList;
658 const char ** requiredbyList;
659 const char ** conflictList;
660 const char ** triggerList;
663 int count = 0, providesCount = 0, requiredbyCount = 0, conflictCount = 0;
664 int triggerCount = 0;
669 headerGetEntry(dbentry, RPMTAG_NAME, &type, (void **) &name, &count);
670 headerGetEntry(dbentry, RPMTAG_GROUP, &type, (void **) &group, &count);
672 if (!group) group = "Unknown";
676 headerGetEntry(dbentry, RPMTAG_BASENAMES, &type, (void **)
680 const char ** newBaseNames;
683 len = count * sizeof(*baseNames);
684 for (i = 0; i < count; i++)
685 len += strlen(baseNames[i]) + 1;
686 newBaseNames = xmalloc(len);
687 data = (char *) newBaseNames + count;
688 for (i = 0; i < count; i++) {
689 newBaseNames[i] = data;
690 data = stpcpy(data, baseNames[i]);
693 expandFilelist(dbentry);
696 headerGetEntry(dbentry, RPMTAG_PROVIDENAME, &type, (void **) &providesList,
698 headerGetEntry(dbentry, RPMTAG_REQUIRENAME, &type,
699 (void **) &requiredbyList, &requiredbyCount);
700 headerGetEntry(dbentry, RPMTAG_CONFLICTNAME, &type,
701 (void **) &conflictList, &conflictCount);
702 headerGetEntry(dbentry, RPMTAG_TRIGGERNAME, &type,
703 (void **) &triggerList, &triggerCount);
707 newSize = headerSizeof(dbentry, HEADER_MAGIC_NO);
708 dboffset = fadAlloc(db->pkgs, newSize);
712 /* XXX TODO: set max. no. of bytes to write */
713 (void)Fseek(db->pkgs, dboffset, SEEK_SET);
714 fdSetContentLength(db->pkgs, newSize);
715 rc = headerWrite(db->pkgs, dbentry, HEADER_MAGIC_NO);
716 fdSetContentLength(db->pkgs, -1);
720 rpmError(RPMERR_DBCORRUPT, _("cannot allocate space for database"));
724 /* Now update the appropriate indexes */
725 if (addIndexEntry(db->nameIndex, name, dboffset, 0))
727 if (addIndexEntry(db->groupIndex, group, dboffset, 0))
730 for (i = 0; i < triggerCount; i++) {
731 /* don't add duplicates */
732 for (j = 0; j < i; j++)
733 if (!strcmp(triggerList[i], triggerList[j])) break;
735 rc += addIndexEntry(db->triggerIndex, triggerList[i], dboffset, 0);
738 for (i = 0; i < conflictCount; i++)
739 rc += addIndexEntry(db->conflictsIndex, conflictList[i], dboffset, 0);
741 for (i = 0; i < requiredbyCount; i++)
742 rc += addIndexEntry(db->requiredbyIndex, requiredbyList[i],
745 for (i = 0; i < providesCount; i++)
746 rc += addIndexEntry(db->providesIndex, providesList[i], dboffset, 0);
748 for (i = 0; i < count; i++) {
749 rc += addIndexEntry(db->fileIndex, baseNames[i], dboffset, i);
752 dbiSyncIndex(db->nameIndex);
753 dbiSyncIndex(db->groupIndex);
754 dbiSyncIndex(db->fileIndex);
755 dbiSyncIndex(db->providesIndex);
756 dbiSyncIndex(db->requiredbyIndex);
757 dbiSyncIndex(db->triggerIndex);
762 if (requiredbyCount) free(requiredbyList);
763 if (providesCount) free(providesList);
764 if (conflictCount) free(conflictList);
765 if (triggerCount) free(triggerList);
766 if (count) free(baseNames);
771 int rpmdbUpdateRecord(rpmdb db, int offset, Header newHeader)
774 int oldSize, newSize;
777 oldHeader = doGetRecord(db, offset, 1);
778 if (oldHeader == NULL) {
779 rpmError(RPMERR_DBCORRUPT, _("cannot read header at %d for update"),
784 oldSize = headerSizeof(oldHeader, HEADER_MAGIC_NO);
785 headerFree(oldHeader);
788 expandFilelist(newHeader);
790 newSize = headerSizeof(newHeader, HEADER_MAGIC_NO);
791 if (oldSize != newSize) {
792 rpmMessage(RPMMESS_DEBUG, _("header changed size!"));
793 if (rpmdbRemove(db, offset, 1))
796 if (rpmdbAdd(db, newHeader))
801 /* XXX TODO: set max. no. of bytes to write */
802 (void)Fseek(db->pkgs, offset, SEEK_SET);
804 fdSetContentLength(db->pkgs, newSize);
805 rc = headerWrite(db->pkgs, newHeader, HEADER_MAGIC_NO);
806 fdSetContentLength(db->pkgs, -1);
814 void rpmdbRemoveDatabase(const char * rootdir, const char * dbpath)
817 const char **rpmdbfnp;
821 if (dbpath[i - 1] != '/') {
822 filename = alloca(i);
823 strcpy(filename, dbpath);
825 filename[i + 1] = '\0';
829 filename = alloca(strlen(rootdir) + strlen(dbpath) + 40);
831 for (rpmdbfnp = rpmdb_filenames; *rpmdbfnp; rpmdbfnp++) {
832 sprintf(filename, "%s/%s/%s", rootdir, dbpath, *rpmdbfnp);
836 sprintf(filename, "%s/%s", rootdir, dbpath);
841 int rpmdbMoveDatabase(const char * rootdir, const char * olddbpath, const char * newdbpath)
844 const char **rpmdbfnp;
845 char * ofilename, * nfilename;
848 i = strlen(olddbpath);
849 if (olddbpath[i - 1] != '/') {
850 ofilename = alloca(i + 2);
851 strcpy(ofilename, olddbpath);
853 ofilename[i + 1] = '\0';
854 olddbpath = ofilename;
857 i = strlen(newdbpath);
858 if (newdbpath[i - 1] != '/') {
859 nfilename = alloca(i + 2);
860 strcpy(nfilename, newdbpath);
862 nfilename[i + 1] = '\0';
863 newdbpath = nfilename;
866 ofilename = alloca(strlen(rootdir) + strlen(olddbpath) + 40);
867 nfilename = alloca(strlen(rootdir) + strlen(newdbpath) + 40);
869 for (rpmdbfnp = rpmdb_filenames; *rpmdbfnp; rpmdbfnp++) {
870 sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, *rpmdbfnp);
871 sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, *rpmdbfnp);
872 if (Rename(ofilename, nfilename)) rc = 1;
879 unsigned int recOffset;
880 unsigned int fileNumber;
884 static int intMatchCmp(const void * one, const void * two)
886 const struct intMatch * a = one;
887 const struct intMatch * b = two;
889 if (a->recOffset < b->recOffset)
891 else if (a->recOffset > b->recOffset)
897 int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList,
900 int numIntMatches = 0;
901 int intMatchesAlloced = numItems;
902 struct intMatch * intMatches;
907 const char ** dirNames, ** baseNames;
908 const char ** fullBaseNames;
909 int_32 * dirIndexes, * fullDirIndexes;
910 fingerPrintCache fpc;
912 /* this may be worth batching by baseName, but probably not as
913 baseNames are quite unique as it is */
915 intMatches = xcalloc(intMatchesAlloced, sizeof(*intMatches));
917 /* Gather all matches from the database */
918 for (i = 0; i < numItems; i++) {
919 dbiIndexSet matches = NULL;
920 switch (dbiSearchIndex(db->fileIndex, fpList[i].baseName, &matches)) {
925 dbiFreeIndexSet(matches);
930 /*@notreached@*/ break;
932 if ((numIntMatches + dbiIndexSetCount(matches)) >= intMatchesAlloced) {
933 intMatchesAlloced += dbiIndexSetCount(matches);
934 intMatchesAlloced += intMatchesAlloced / 5;
935 intMatches = xrealloc(intMatches,
936 sizeof(*intMatches) * intMatchesAlloced);
939 for (j = 0; j < dbiIndexSetCount(matches); j++) {
941 intMatches[numIntMatches].recOffset = dbiIndexRecordOffset(matches, j);
942 intMatches[numIntMatches].fileNumber = dbiIndexRecordFileNumber(matches, j);
943 intMatches[numIntMatches].fpNum = i;
950 dbiFreeIndexSet(matches);
955 qsort(intMatches, numIntMatches, sizeof(*intMatches), intMatchCmp);
956 /* intMatches is now sorted by (recnum, filenum) */
958 for (i = 0; i < numItems; i++)
959 matchList[i] = dbiCreateIndexSet();
961 fpc = fpCacheCreate(numIntMatches);
963 /* For each set of files matched in a package ... */
964 for (start = 0; start < numIntMatches; start = end) {
965 struct intMatch * im;
969 im = intMatches + start;
971 /* Find the end of the set of matched files in this package. */
972 for (end = start + 1; end < numIntMatches; end++) {
973 if (im->recOffset != intMatches[end].recOffset)
978 /* Compute fingerprints for each file match in this package. */
979 h = rpmdbGetRecord(db, im->recOffset);
985 headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL,
986 (void **) &dirNames, NULL);
987 headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL,
988 (void **) &fullBaseNames, &fc);
989 headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL,
990 (void **) &fullDirIndexes, NULL);
992 baseNames = xcalloc(num, sizeof(*baseNames));
993 dirIndexes = xcalloc(num, sizeof(*dirIndexes));
994 for (i = 0; i < num; i++) {
995 baseNames[i] = fullBaseNames[im[i].fileNumber];
996 dirIndexes[i] = fullDirIndexes[im[i].fileNumber];
999 fps = xcalloc(num, sizeof(*fps));
1000 fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
1003 free(fullBaseNames);
1007 /* Add db (recnum,filenum) to list for fingerprint matches. */
1008 for (i = 0; i < num; i++) {
1010 if (FP_EQUAL_DIFFERENT_CACHE(fps[i], fpList[j]))
1011 dbiAppendIndexRecord(matchList[j], im[i].recOffset, im[i].fileNumber);