2 * Copyright (c) 2009-2013, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
17 #include "repo_rpmdb.h"
18 #include "pool_fileconflicts.h"
25 Queue lookat; /* conflict candidates */
26 Queue lookat_dir; /* not yet conflicting directories */
30 unsigned int cflmapused;
34 unsigned int dirmapused;
39 unsigned int lastdiridx; /* last diridx we have seen */
40 unsigned int lastdirhash; /* strhash of last dir we have seen */
42 Id idx; /* index of package we're looking at */
43 Id hx; /* used in findfileconflicts2_cb, limit to files matching hx */
45 Id dirid; /* used in findfileconflicts2_cb, limit to dirs matching dirid */
46 Id dirhash; /* used in findfileconflicts2_cb, limit to dirs matching dirhash */
49 unsigned char *filesspace;
50 unsigned int filesspacen;
54 unsigned int normapused;
59 unsigned int statmapused;
71 #define FILESSPACE_BLOCK 255
74 growhash(Hashtable map, Hashval *mapnp)
76 Hashval mapn = *mapnp;
77 Hashval newn = (mapn + 1) * 2 - 1;
82 m = solv_calloc(newn + 1, 2 * sizeof(Id));
83 for (i = 0; i <= mapn; i++)
95 h = HASHCHAIN_NEXT(h, hh, newn);
98 m[2 * h + 1] = map[2 * i + 1];
106 finddirs_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
108 struct cbdata *cbdata = cbdatav;
111 Id oidx, idx = cbdata->idx;
116 h = hx & cbdata->dirmapn;
117 hh = HASHCHAIN_START;
120 qx = cbdata->dirmap[2 * h];
125 h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
132 cbdata->dirmap[2 * h] = hx;
133 cbdata->dirmap[2 * h + 1] = idx;
134 if (++cbdata->dirmapused * 2 > cbdata->dirmapn)
135 cbdata->dirmap = growhash(cbdata->dirmap, &cbdata->dirmapn);
138 oidx = cbdata->dirmap[2 * h + 1];
141 /* found a conflict, this dir may be used in multiple packages */
144 MAPSET(&cbdata->idxmap, oidx);
145 cbdata->dirmap[2 * h + 1] = -1;
146 cbdata->dirconflicts++;
148 MAPSET(&cbdata->idxmap, idx);
152 isindirmap(struct cbdata *cbdata, Id hx)
157 h = hx & cbdata->dirmapn;
158 hh = HASHCHAIN_START;
161 qx = cbdata->dirmap[2 * h];
165 return cbdata->dirmap[2 * h + 1] == -1 ? 1 : 0;
166 h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
171 findfileconflicts_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
173 struct cbdata *cbdata = cbdatav;
174 int isdir = S_ISDIR(info->mode);
184 dp = fn + info->dirlen;
185 if (info->diridx != cbdata->lastdiridx)
187 cbdata->lastdiridx = info->diridx;
188 cbdata->lastdirhash = strnhash(fn, dp - fn);
190 dhx = cbdata->lastdirhash;
191 /* this mirrors the "if (!hx) hx = strlen(fn) + 1" in finddirs_cb */
192 if (!isindirmap(cbdata, dhx ? dhx : dp - fn + 1))
194 hx = strhash_cont(dp, dhx);
198 h = hx & cbdata->cflmapn;
199 hh = HASHCHAIN_START;
202 qx = cbdata->cflmap[2 * h];
207 h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
214 cbdata->cflmap[2 * h] = hx;
215 cbdata->cflmap[2 * h + 1] = (isdir ? ~idx : idx);
216 if (++cbdata->cflmapused * 2 > cbdata->cflmapn)
217 cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn);
220 oidx = cbdata->cflmap[2 * h + 1];
226 /* both are directories. delay the conflict, keep oidx in slot */
227 queue_push2(&cbdata->lookat_dir, hx, idx);
231 /* now have file, had directories before. */
232 cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */
233 /* dump all delayed directory hits for hx */
234 for (i = 0; i < cbdata->lookat_dir.count; i += 2)
235 if (cbdata->lookat_dir.elements[i] == hx)
237 queue_push2(&cbdata->lookat, hx, cbdata->lookat_dir.elements[i + 1]);
238 queue_push2(&cbdata->lookat, 0, 0);
241 else if (oidx == idx)
242 return; /* no conflicts with ourself, please */
243 queue_push2(&cbdata->lookat, hx, oidx);
244 queue_push2(&cbdata->lookat, 0, 0);
245 queue_push2(&cbdata->lookat, hx, idx);
246 queue_push2(&cbdata->lookat, 0, 0);
249 /* same as findfileconflicts_cb, but
250 * - hashes with just the basename
251 * - sets idx in a map instead of pushing to lookat
252 * - sets the hash element to -1 if there may be a conflict
255 findfileconflicts_basename_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
257 struct cbdata *cbdata = cbdatav;
258 int isdir = S_ISDIR(info->mode);
268 dp = fn + info->dirlen;
273 h = hx & cbdata->cflmapn;
274 hh = HASHCHAIN_START;
277 qx = cbdata->cflmap[2 * h];
282 h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
289 cbdata->cflmap[2 * h] = hx;
290 cbdata->cflmap[2 * h + 1] = (isdir ? -idx - 2 : idx);
291 if (++cbdata->cflmapused * 2 > cbdata->cflmapn)
292 cbdata->cflmap = growhash(cbdata->cflmap, &cbdata->cflmapn);
295 oidx = cbdata->cflmap[2 * h + 1];
301 /* both are directories. delay the conflict, keep oidx in slot */
302 queue_push2(&cbdata->lookat_dir, hx, idx);
306 /* now have file, had directories before. */
307 cbdata->cflmap[2 * h + 1] = oidx; /* make it a file */
308 /* dump all delayed directory hits for hx */
309 for (i = 0; i < cbdata->lookat_dir.count; i += 2)
310 if (cbdata->lookat_dir.elements[i] == hx)
311 MAPSET(&cbdata->idxmap, cbdata->lookat_dir.elements[i + 1]);
313 else if (oidx == idx)
314 return; /* no conflicts with ourself, please */
316 MAPSET(&cbdata->idxmap, oidx);
317 MAPSET(&cbdata->idxmap, idx);
319 cbdata->cflmap[2 * h + 1] = -1;
323 addfilesspace(struct cbdata *cbdata, int len)
325 unsigned int off = cbdata->filesspacen;
326 cbdata->filesspace = solv_extend(cbdata->filesspace, cbdata->filesspacen, len, 1, FILESSPACE_BLOCK);
327 cbdata->filesspacen += len;
332 unifywithstat(struct cbdata *cbdata, Id diroff, int dirl)
339 unsigned char statdata[16 + sizeof(stb.st_dev) + sizeof(stb.st_ino)];
341 if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == '/')
342 cbdata->filesspace[diroff + dirl - 1] = 0;
344 i = stat((char *)cbdata->filesspace + diroff, &stb);
345 if (dirl > 1 && cbdata->filesspace[diroff + dirl - 1] == 0)
346 cbdata->filesspace[diroff + dirl - 1] = '/';
349 memset(statdata, 0, 16);
350 memcpy(statdata + 8, &stb.st_dev, sizeof(stb.st_dev));
351 memcpy(statdata, &stb.st_ino, sizeof(stb.st_ino));
353 for (i = 15; i >= 0; i--)
354 hx = (unsigned int)hx * 13 + statdata[i];
355 h = hx & cbdata->statmapn;
356 hh = HASHCHAIN_START;
359 qx = cbdata->statmap[2 * h];
364 Id off = cbdata->statmap[2 * h + 1];
365 char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
366 if (!memcmp(dp, statdata, 16))
367 return cbdata->norq.elements[off + 1];
369 h = HASHCHAIN_NEXT(h, hh, cbdata->statmapn);
371 /* new stat result. work. */
372 nspaceoff = addfilesspace(cbdata, 16);
373 memcpy(cbdata->filesspace + nspaceoff, statdata, 16);
374 queue_push2(&cbdata->norq, nspaceoff, nspaceoff);
375 cbdata->statmap[2 * h] = hx;
376 cbdata->statmap[2 * h + 1] = cbdata->norq.count - 2;
377 if (++cbdata->statmapused * 2 > cbdata->statmapn)
378 cbdata->statmap = growhash(cbdata->statmap, &cbdata->statmapn);
382 /* forward declaration */
383 static Id normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create);
386 unifywithcanon(struct cbdata *cbdata, Id diroff, int dirl)
393 printf("UNIFY %.*s\n", dirl, (char *)cbdata->filesspace + diroff);
395 if (!dirl || cbdata->filesspace[diroff] != '/')
398 while (dirl && cbdata->filesspace[diroff + dirl - 1] == '/')
403 for (i = dirl - 1; i > 0; i--)
404 if (cbdata->filesspace[diroff + i] == '/')
406 i++; /* include trailing / */
408 /* normalize dirname */
409 dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, i, strnhash((char *)cbdata->filesspace + diroff, i), 1);
411 return diroff; /* hit "in progress" marker, some cyclic link */
413 /* sanity check result */
414 if (cbdata->filesspace[dirnameid] != '/')
415 return diroff; /* hmm */
416 l = strlen((char *)cbdata->filesspace + dirnameid);
417 if (l && cbdata->filesspace[dirnameid + l - 1] != '/')
418 return diroff; /* hmm */
420 /* special handling for "." and ".." basename */
421 if (cbdata->filesspace[diroff + i] == '.')
425 if (dirl - i == 2 && cbdata->filesspace[diroff + i + 1] == '.')
428 return dirnameid; /* we hit our root */
429 for (i = l - 2; i > 0; i--)
430 if (cbdata->filesspace[dirnameid + i] == '/')
432 i++; /* include trailing / */
433 dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + dirnameid, i, strnhash((char *)cbdata->filesspace + dirnameid, i), 1);
434 return dirnameid == -1 ? diroff : dirnameid;
438 /* append basename to normalized dirname */
439 if (cbdata->rootdirl + l + dirl - i + 1 > cbdata->canonspacen)
441 cbdata->canonspacen = cbdata->rootdirl + l + dirl - i + 20;
442 cbdata->canonspace = solv_realloc(cbdata->canonspace, cbdata->canonspacen);
443 strcpy(cbdata->canonspace, cbdata->rootdir);
445 strcpy(cbdata->canonspace + cbdata->rootdirl, (char *)cbdata->filesspace + dirnameid);
446 strncpy(cbdata->canonspace + cbdata->rootdirl + l, (char *)cbdata->filesspace + diroff + i, dirl - i);
447 cbdata->canonspace[cbdata->rootdirl + l + dirl - i] = 0;
450 printf("stat()ing %s\n", cbdata->canonspace);
453 if (lstat(cbdata->canonspace, &stb))
454 return diroff; /* hmm */
455 if (!S_ISLNK(stb.st_mode))
457 /* not a symlink, have new canon entry */
458 diroff = addfilesspace(cbdata, l + dirl - i + 2);
459 strcpy((char *)cbdata->filesspace + diroff, cbdata->canonspace + cbdata->rootdirl);
462 if (cbdata->filesspace[diroff + l - 1] != '/')
464 cbdata->filesspace[diroff + l++] = '/';
465 cbdata->filesspace[diroff + l] = 0;
467 /* call normalizedir on new entry for unification purposes */
468 dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, l, strnhash((char *)cbdata->filesspace + diroff, l), 1);
469 return dirnameid == -1 ? diroff : dirnameid;
471 /* oh no, a symlink! follow */
472 lo = cbdata->rootdirl + l + dirl - i + 1;
473 if (lo + stb.st_size + 2 > cbdata->canonspacen)
475 cbdata->canonspacen = lo + stb.st_size + 20;
476 cbdata->canonspace = solv_realloc(cbdata->canonspace, cbdata->canonspacen);
478 ll = readlink(cbdata->canonspace, cbdata->canonspace + lo, stb.st_size);
479 if (ll < 0 || ll > stb.st_size)
480 return diroff; /* hmm */
482 return dirnameid; /* empty means current dir */
483 if (cbdata->canonspace[lo + ll - 1] != '/')
484 cbdata->canonspace[lo + ll++] = '/'; /* add trailing / */
485 cbdata->canonspace[lo + ll] = 0; /* zero terminate */
486 if (cbdata->canonspace[lo] != '/')
488 /* relative link, concatenate to dirname */
489 memmove(cbdata->canonspace + cbdata->rootdirl + l, cbdata->canonspace + lo, ll + 1);
490 lo = cbdata->rootdirl;
493 dirnameid = normalizedir(cbdata, cbdata->canonspace + lo, ll, strnhash(cbdata->canonspace + lo, ll), 1);
494 return dirnameid == -1 ? diroff : dirnameid;
498 * map a directory (containing a trailing /) into a number.
499 * for unifywithstat this is the offset to the 16 byte stat result.
500 * for unifywithcanon this is the offset to the normailzed dir.
503 normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create)
512 h = hx & cbdata->normapn;
513 hh = HASHCHAIN_START;
516 qx = cbdata->normap[2 * h];
521 Id off = cbdata->normap[2 * h + 1];
522 char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
523 if (!strncmp(dp, dir, dirl) && dp[dirl] == 0)
524 return cbdata->norq.elements[off + 1];
526 h = HASHCHAIN_NEXT(h, hh, cbdata->normapn);
531 if (dir >= (const char *)cbdata->filesspace && dir < (const char *)cbdata->filesspace + cbdata->filesspacen)
533 /* can happen when called from unifywithcanon */
534 Id off = dir - (const char *)cbdata->filesspace;
535 nspaceoff = addfilesspace(cbdata, dirl + 1);
536 dir = (const char *)cbdata->filesspace + off;
539 nspaceoff = addfilesspace(cbdata, dirl + 1);
541 memcpy(cbdata->filesspace + nspaceoff, dir, dirl);
542 cbdata->filesspace[nspaceoff + dirl] = 0;
543 mycnt = cbdata->norq.count;
544 queue_push2(&cbdata->norq, nspaceoff, -1); /* -1: in progress */
545 cbdata->normap[2 * h] = hx;
546 cbdata->normap[2 * h + 1] = mycnt;
547 if (++cbdata->normapused * 2 > cbdata->normapn)
548 cbdata->normap = growhash(cbdata->normap, &cbdata->normapn);
551 nspaceoff = unifywithstat(cbdata, nspaceoff, dirl);
553 nspaceoff = unifywithcanon(cbdata, nspaceoff, dirl);
554 cbdata->norq.elements[mycnt + 1] = nspaceoff; /* patch in result */
556 if (!cbdata->usestat)
557 printf("%s normalized to %d: %s\n", cbdata->filesspace + cbdata->norq.elements[mycnt], nspaceoff, cbdata->filesspace + nspaceoff);
563 findfileconflicts_alias_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
565 int isdir = S_ISDIR(info->mode);
566 struct cbdata *cbdata = cbdatav;
576 dp = fn + info->dirlen;
577 if (info->diridx != cbdata->lastdiridx)
579 cbdata->lastdiridx = info->diridx;
580 cbdata->lastdirhash = 0;
582 dp = fn + info->dirlen;
587 h = hx & cbdata->cflmapn;
588 hh = HASHCHAIN_START;
591 qx = cbdata->cflmap[2 * h];
596 h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
598 if (!qx || cbdata->cflmap[2 * h + 1] != -1)
600 if (!cbdata->lastdirhash)
601 cbdata->lastdirhash = strnhash(fn, dp - fn);
602 dirid = normalizedir(cbdata, fn, dp - fn, cbdata->lastdirhash, 1);
603 queue_push2(&cbdata->lookat, hx, idx);
604 queue_push2(&cbdata->lookat, cbdata->lastdirhash, isdir ? -dirid : dirid);
608 findfileconflicts2_cb(void *cbdatav, const char *fn, struct filelistinfo *info)
610 struct cbdata *cbdata = cbdatav;
618 dp = fn + info->dirlen;
619 if (info->diridx != cbdata->lastdiridx)
621 cbdata->lastdiridx = info->diridx;
622 cbdata->lastdirhash = strnhash(fn, dp - fn);
626 if (cbdata->lastdirhash != cbdata->dirhash)
632 hx = cbdata->lastdirhash;
633 hx = strhash_cont(dp, hx);
637 if ((Id)hx != cbdata->hx)
639 if (cbdata->dirid && cbdata->dirid != normalizedir(cbdata, fn, dp - fn, cbdata->dirhash, 0))
641 strncpy(md5padded, info->digest, 32);
643 md5padded[33] = info->color;
644 /* printf("%d, hx %x -> %s %d %s\n", cbdata->idx, hx, fn, info->mode, info->digest); */
645 off = addfilesspace(cbdata, strlen(fn) + (34 + 1));
646 memcpy(cbdata->filesspace + off, (unsigned char *)md5padded, 34);
647 strcpy((char *)cbdata->filesspace + off + 34, fn);
648 queue_push(&cbdata->files, off);
652 lookat_idx_cmp(const void *ap, const void *bp, void *dp)
654 const Id *a = ap, *b = bp;
655 unsigned int ahx, bhx;
656 if (a[1] - b[1] != 0) /* idx */
658 if (a[3] - b[3] != 0) /* dirid */
660 ahx = (unsigned int)a[0]; /* can be < 0 */
661 bhx = (unsigned int)b[0];
663 return ahx < bhx ? -1 : 1;
664 ahx = (unsigned int)a[2]; /* dhx */
665 bhx = (unsigned int)b[2];
667 return ahx < bhx ? -1 : 1;
672 lookat_hx_cmp(const void *ap, const void *bp, void *dp)
674 const Id *a = ap, *b = bp;
675 unsigned int ahx, bhx;
677 ahx = (unsigned int)a[0]; /* can be < 0 */
678 bhx = (unsigned int)b[0];
680 return ahx < bhx ? -1 : 1;
681 adirid = a[3] < 0 ? -a[3] : a[3];
682 bdirid = b[3] < 0 ? -b[3] : b[3];
683 if (adirid - bdirid != 0) /* dirid */
684 return adirid - bdirid;
686 return a[3] > 0 ? -1 : 1; /* bring positive dirids to front */
687 if (a[1] - b[1] != 0) /* idx */
689 ahx = (unsigned int)a[2]; /* dhx */
690 bhx = (unsigned int)b[2];
692 return ahx < bhx ? -1 : 1;
697 conflicts_cmp(const void *ap, const void *bp, void *dp)
702 if (a[0] != b[0]) /* filename1 */
703 return strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
704 if (a[3] != b[3]) /* filename2 */
705 return strcmp(pool_id2str(pool, a[3]), pool_id2str(pool, b[3]));
706 if (a[1] != b[1]) /* pkgid1 */
708 if (a[4] != b[4]) /* pkgid2 */
714 iterate_solvable_dirs(Pool *pool, Id p, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata)
716 Repodata *lastdata = 0;
720 dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST);
721 while (dataiterator_step(&di))
723 if (di.data == lastdata && di.kv.id == lastdirid)
726 lastdirid = di.kv.id;
727 cb(cbdata, repodata_dir2str(di.data, di.kv.id, ""), 0);
729 dataiterator_free(&di);
732 /* before calling the expensive findfileconflicts_basename_cb we check if any of
733 * the basenames match. This only makes sense when cbdata->create is off.
736 precheck_solvable_files(struct cbdata *cbdata, Pool *pool, Id p)
743 dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST);
744 while (dataiterator_step(&di))
746 hx = strhash(di.kv.str);
748 hx = strlen(di.kv.str) + 1;
749 h = hx & cbdata->cflmapn;
750 hh = HASHCHAIN_START;
753 qx = cbdata->cflmap[2 * h];
761 h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
766 dataiterator_free(&di);
772 pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, int flags, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata)
774 int i, j, cflmapn, idxmapset;
775 struct cbdata cbdata;
776 unsigned int now, start;
778 Repo *installed = pool->installed;
780 int obsoleteusescolors = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESCOLORS);
782 queue_empty(conflicts);
786 now = start = solv_timems(0);
787 POOL_DEBUG(SOLV_DEBUG_STATS, "searching for file conflicts\n");
788 POOL_DEBUG(SOLV_DEBUG_STATS, "packages: %d, cutoff %d\n", pkgs->count, cutoff);
790 memset(&cbdata, 0, sizeof(cbdata));
791 cbdata.aliases = flags & FINDFILECONFLICTS_CHECK_DIRALIASING;
793 if (cbdata.aliases && (flags & FINDFILECONFLICTS_USE_ROOTDIR) != 0)
795 cbdata.rootdir = pool_get_rootdir(pool);
796 if (cbdata.rootdir && !strcmp(cbdata.rootdir, "/"))
799 cbdata.rootdirl = strlen(cbdata.rootdir);
803 queue_init(&cbdata.lookat);
804 queue_init(&cbdata.lookat_dir);
805 map_init(&cbdata.idxmap, pkgs->count);
808 cutoff = pkgs->count;
810 /* avarage file list size: 200 files per package */
811 /* avarage dir count: 20 dirs per package */
813 /* first pass: scan dirs */
816 cflmapn = (cutoff + 3) * 64;
817 while ((cflmapn & (cflmapn - 1)) != 0)
818 cflmapn = cflmapn & (cflmapn - 1);
819 cbdata.dirmap = solv_calloc(cflmapn, 2 * sizeof(Id));
820 cbdata.dirmapn = cflmapn - 1; /* make it a mask */
823 for (i = 0; i < pkgs->count; i++)
825 p = pkgs->elements[i];
829 if ((flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed)
831 if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
833 iterate_solvable_dirs(pool, p, finddirs_cb, &cbdata);
834 if (MAPTST(&cbdata.idxmap, i))
839 handle = (*handle_cb)(pool, p, handle_cbdata);
842 rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_ONLYDIRS, finddirs_cb, &cbdata);
843 if (MAPTST(&cbdata.idxmap, i))
846 POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap size: %d, used %d\n", cbdata.dirmapn + 1, cbdata.dirmapused);
847 POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap memory usage: %d K\n", (cbdata.dirmapn + 1) * 2 * (int)sizeof(Id) / 1024);
848 POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap creation took %d ms\n", solv_timems(now));
849 POOL_DEBUG(SOLV_DEBUG_STATS, "dir conflicts found: %d, idxmap %d of %d\n", cbdata.dirconflicts, idxmapset, pkgs->count);
852 /* second pass: scan files */
853 now = solv_timems(0);
854 cflmapn = (cutoff + 3) * 128;
855 while ((cflmapn & (cflmapn - 1)) != 0)
856 cflmapn = cflmapn & (cflmapn - 1);
857 cbdata.cflmap = solv_calloc(cflmapn, 2 * sizeof(Id));
858 cbdata.cflmapn = cflmapn - 1; /* make it a mask */
860 for (i = 0; i < pkgs->count; i++)
862 if (!cbdata.aliases && !MAPTST(&cbdata.idxmap, i))
864 p = pkgs->elements[i];
868 if (cbdata.aliases && !cbdata.create && FINDFILECONFLICTS_USE_SOLVABLEFILELIST)
870 if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
871 if (!precheck_solvable_files(&cbdata, pool, p))
874 /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if
875 * the file is a directory or not */
876 handle = (*handle_cb)(pool, p, handle_cbdata);
879 cbdata.lastdiridx = -1;
880 rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, cbdata.aliases ? findfileconflicts_basename_cb : findfileconflicts_cb, &cbdata);
883 POOL_DEBUG(SOLV_DEBUG_STATS, "filemap size: %d, used %d\n", cbdata.cflmapn + 1, cbdata.cflmapused);
884 POOL_DEBUG(SOLV_DEBUG_STATS, "filemap memory usage: %d K\n", (cbdata.cflmapn + 1) * 2 * (int)sizeof(Id) / 1024);
885 POOL_DEBUG(SOLV_DEBUG_STATS, "filemap creation took %d ms\n", solv_timems(now));
886 POOL_DEBUG(SOLV_DEBUG_STATS, "lookat_dir size: %d\n", cbdata.lookat_dir.count);
887 queue_free(&cbdata.lookat_dir);
889 /* we need another pass for aliases */
892 now = solv_timems(0);
893 /* make sure the first offset is not zero */
894 addfilesspace(&cbdata, 1);
895 cflmapn = (cutoff + 3) * 16;
896 while ((cflmapn & (cflmapn - 1)) != 0)
897 cflmapn = cflmapn & (cflmapn - 1);
898 cbdata.normap = solv_calloc(cflmapn, 2 * sizeof(Id));
899 cbdata.normapn = cflmapn - 1; /* make it a mask */
902 cbdata.statmap = solv_calloc(cflmapn, 2 * sizeof(Id));
903 cbdata.statmapn = cflmapn - 1; /* make it a mask */
906 for (i = 0; i < pkgs->count; i++)
908 if (!MAPTST(&cbdata.idxmap, i))
910 p = pkgs->elements[i];
912 /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if
913 * the file is a directory or not */
914 handle = (*handle_cb)(pool, p, handle_cbdata);
917 cbdata.lastdiridx = -1;
918 rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, findfileconflicts_alias_cb, &cbdata);
920 POOL_DEBUG(SOLV_DEBUG_STATS, "normap size: %d, used %d\n", cbdata.normapn + 1, cbdata.normapused);
921 POOL_DEBUG(SOLV_DEBUG_STATS, "normap memory usage: %d K\n", (cbdata.normapn + 1) * 2 * (int)sizeof(Id) / 1024);
922 POOL_DEBUG(SOLV_DEBUG_STATS, "stats made: %d\n", cbdata.statsmade);
925 POOL_DEBUG(SOLV_DEBUG_STATS, "statmap size: %d, used %d\n", cbdata.statmapn + 1, cbdata.statmapused);
926 POOL_DEBUG(SOLV_DEBUG_STATS, "statmap memory usage: %d K\n", (cbdata.statmapn + 1) * 2 * (int)sizeof(Id) / 1024);
928 cbdata.statmap = solv_free(cbdata.statmap);
930 cbdata.canonspace = solv_free(cbdata.canonspace);
931 cbdata.canonspacen = 0;
932 POOL_DEBUG(SOLV_DEBUG_STATS, "alias processing took %d ms\n", solv_timems(now));
935 cbdata.dirmap = solv_free(cbdata.dirmap);
937 cbdata.dirmapused = 0;
938 cbdata.cflmap = solv_free(cbdata.cflmap);
940 cbdata.cflmapused = 0;
942 now = solv_timems(0);
944 map_free(&cbdata.idxmap);
946 /* sort and unify/prune */
947 POOL_DEBUG(SOLV_DEBUG_STATS, "raw candidates: %d, pruning\n", cbdata.lookat.count / 4);
948 solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool);
949 for (i = j = 0; i < cbdata.lookat.count; )
952 Id hx = cbdata.lookat.elements[i];
953 Id idx = cbdata.lookat.elements[i + 1];
954 Id dhx = cbdata.lookat.elements[i + 2];
955 Id dirid = cbdata.lookat.elements[i + 3];
957 for (; i < cbdata.lookat.count && hx == cbdata.lookat.elements[i] && (dirid == cbdata.lookat.elements[i + 3] || dirid == -cbdata.lookat.elements[i + 3]); i += 4)
959 if (idx == cbdata.lookat.elements[i + 1] && dhx == cbdata.lookat.elements[i + 2])
960 continue; /* ignore duplicates */
964 continue; /* all have a neg dirid */
965 cbdata.lookat.elements[j++] = hx;
966 cbdata.lookat.elements[j++] = idx;
967 cbdata.lookat.elements[j++] = dhx;
968 cbdata.lookat.elements[j++] = dirid;
971 idx = cbdata.lookat.elements[i + 1];
972 dhx = cbdata.lookat.elements[i + 2];
973 cbdata.lookat.elements[j++] = hx;
974 cbdata.lookat.elements[j++] = idx;
975 cbdata.lookat.elements[j++] = dhx;
976 cbdata.lookat.elements[j++] = dirid;
979 queue_truncate(&cbdata.lookat, j);
980 POOL_DEBUG(SOLV_DEBUG_STATS, "candidates now: %d\n", cbdata.lookat.count / 4);
982 /* third pass: collect file info for all files that match a hx */
983 solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_idx_cmp, pool);
984 queue_init(&cbdata.files);
985 for (i = 0; i < cbdata.lookat.count; i += 4)
987 Id idx = cbdata.lookat.elements[i + 1];
988 int iterflags = RPM_ITERATE_FILELIST_WITHMD5 | RPM_ITERATE_FILELIST_NOGHOSTS;
989 if (obsoleteusescolors)
990 iterflags |= RPM_ITERATE_FILELIST_WITHCOL;
991 p = pkgs->elements[idx];
992 handle = (*handle_cb)(pool, p, handle_cbdata);
995 int fstart = cbdata.files.count;
996 queue_push(&cbdata.files, idx);
997 queue_push(&cbdata.files, 0);
999 cbdata.hx = cbdata.lookat.elements[i];
1000 cbdata.dirhash = cbdata.lookat.elements[i + 2];
1001 cbdata.dirid = cbdata.lookat.elements[i + 3];
1002 cbdata.lastdiridx = -1;
1004 rpm_iterate_filelist(handle, iterflags, findfileconflicts2_cb, &cbdata);
1005 cbdata.files.elements[fstart + 1] = cbdata.files.count;
1006 cbdata.lookat.elements[i + 1] = fstart;
1007 if (i + 4 >= cbdata.lookat.count || cbdata.lookat.elements[i + 4 + 1] != idx)
1012 cbdata.normap = solv_free(cbdata.normap);
1015 /* forth pass: for each hx we have, compare all matching files against all other matching files */
1016 solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool);
1017 for (i = 0; i < cbdata.lookat.count - 4; i += 4)
1019 Id hx = cbdata.lookat.elements[i];
1020 Id pstart = cbdata.lookat.elements[i + 1];
1021 Id dirid = cbdata.lookat.elements[i + 3];
1022 Id pidx = cbdata.files.elements[pstart];
1023 Id pend = cbdata.files.elements[pstart + 1];
1024 if (cbdata.lookat.elements[i + 4] != hx)
1025 continue; /* no package left with that hx */
1026 for (j = i + 4; j < cbdata.lookat.count && cbdata.lookat.elements[j] == hx && cbdata.lookat.elements[j + 3] == dirid; j += 4)
1028 Id qstart = cbdata.lookat.elements[j + 1];
1029 Id qidx = cbdata.files.elements[qstart];
1030 Id qend = cbdata.files.elements[qstart + 1];
1032 if (pidx >= cutoff && qidx >= cutoff)
1033 continue; /* no conflicts between packages with idx >= cutoff */
1034 for (ii = pstart + 2; ii < pend; ii++)
1035 for (jj = qstart + 2; jj < qend; jj++)
1037 char *fsi = (char *)cbdata.filesspace + cbdata.files.elements[ii];
1038 char *fsj = (char *)cbdata.filesspace + cbdata.files.elements[jj];
1041 /* compare just the basenames, the dirs match */
1042 char *bsi = strrchr(fsi + 34, '/');
1043 char *bsj = strrchr(fsj + 34, '/');
1044 if ((!bsi || !bsj) && bsi != bsj)
1046 if (strcmp(bsi, bsj))
1047 continue; /* different file names */
1051 if (strcmp(fsi + 34, fsj + 34))
1052 continue; /* different file names */
1054 if (!strcmp(fsi, fsj))
1055 continue; /* md5 sum matches */
1056 if (obsoleteusescolors && fsi[33] && fsj[33] && (fsi[33] & fsj[33]) == 0)
1057 continue; /* colors do not conflict */
1058 queue_push(conflicts, pool_str2id(pool, fsi + 34, 1));
1059 queue_push(conflicts, pkgs->elements[pidx]);
1060 queue_push(conflicts, pool_str2id(pool, fsi, 1));
1061 queue_push(conflicts, pool_str2id(pool, fsj + 34, 1));
1062 queue_push(conflicts, pkgs->elements[qidx]);
1063 queue_push(conflicts, pool_str2id(pool, fsj, 1));
1067 POOL_DEBUG(SOLV_DEBUG_STATS, "filespace size: %d K\n", cbdata.filesspacen / 1024);
1068 POOL_DEBUG(SOLV_DEBUG_STATS, "candidate check took %d ms\n", solv_timems(now));
1069 cbdata.filesspace = solv_free(cbdata.filesspace);
1070 cbdata.filesspacen = 0;
1071 queue_free(&cbdata.lookat);
1072 queue_free(&cbdata.files);
1073 if (conflicts->count > 6)
1074 solv_sort(conflicts->elements, conflicts->count / 6, 6 * sizeof(Id), conflicts_cmp, pool);
1075 POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file conflicts\n", conflicts->count / 6);
1076 POOL_DEBUG(SOLV_DEBUG_STATS, "file conflict detection took %d ms\n", solv_timems(start));
1078 return conflicts->count;