Merge pull request #36 from scarabeusiv/master
[platform/upstream/libsolv.git] / ext / pool_fileconflicts.c
index 63847b5..bfd07ef 100644 (file)
@@ -362,8 +362,8 @@ unifywithstat(struct cbdata *cbdata, Id diroff, int dirl)
       if (qx == hx)
        {
          Id off = cbdata->statmap[2 * h + 1];
-         const char *dp = (const char *)cbdata->filesspace + cbdata->norq.elements[off];
-         if (!memcmp(dp, (const char *)statdata, 16))
+         char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
+         if (!memcmp(dp, statdata, 16))
            return cbdata->norq.elements[off + 1];
        }
       h = HASHCHAIN_NEXT(h, hh, cbdata->statmapn);
@@ -399,36 +399,44 @@ unifywithcanon(struct cbdata *cbdata, Id diroff, int dirl)
     dirl--;
   if (!dirl)
     return diroff;
+
   /* find dirname */
   for (i = dirl - 1; i > 0; i--)
     if (cbdata->filesspace[diroff + i] == '/')
       break;
   i++;                         /* include trailing / */
+
+  /* normalize dirname */
   dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, i, strnhash((char *)cbdata->filesspace + diroff, i), 1);
   if (dirnameid == -1)
-    return diroff;             /* some cyclic link */
+    return diroff;             /* hit "in progress" marker, some cyclic link */
+
+  /* sanity check result */
   if (cbdata->filesspace[dirnameid] != '/')
     return diroff;             /* hmm */
   l = strlen((char *)cbdata->filesspace + dirnameid);
   if (l && cbdata->filesspace[dirnameid + l - 1] != '/')
-    return diroff;
-  /* special handling for '.', '..', '' */
+    return diroff;             /* hmm */
+
+  /* special handling for "." and ".." basename */
   if (cbdata->filesspace[diroff + i] == '.')
     {
       if (dirl - i == 1)
        return dirnameid;
       if (dirl - i == 2 && cbdata->filesspace[diroff + i + 1] == '.')
        {
-         dirl = strlen((char *)cbdata->filesspace + dirnameid);
-         if (dirl <= 2)
-           return dirnameid;
-         for (i = dirl - 2; i > 0; i--)
-           if (cbdata->filesspace[diroff + i] == '/')
+         if (l <= 2)
+           return dirnameid;   /* we hit our root */
+         for (i = l - 2; i > 0; i--)
+           if (cbdata->filesspace[dirnameid + i] == '/')
              break;
-         dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + dirnameid, i + 1, strnhash((char *)cbdata->filesspace + dirnameid, i + 1), 1);
+         i++;  /* include trailing / */
+         dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + dirnameid, i, strnhash((char *)cbdata->filesspace + dirnameid, i), 1);
          return dirnameid == -1 ? diroff : dirnameid;
        }
     }
+
+  /* append basename to normalized dirname */
   if (cbdata->rootdirl + l + dirl - i + 1 > cbdata->canonspacen)
     {
       cbdata->canonspacen = cbdata->rootdirl + l + dirl - i + 20;
@@ -438,15 +446,14 @@ unifywithcanon(struct cbdata *cbdata, Id diroff, int dirl)
   strcpy(cbdata->canonspace + cbdata->rootdirl, (char *)cbdata->filesspace + dirnameid);
   strncpy(cbdata->canonspace + cbdata->rootdirl + l, (char *)cbdata->filesspace + diroff + i, dirl - i);
   cbdata->canonspace[cbdata->rootdirl + l + dirl - i] = 0;
-  cbdata->statsmade++;
+
 #if 0
   printf("stat()ing %s\n", cbdata->canonspace);
 #endif
-  if (lstat(cbdata->canonspace, &stb))
-    return diroff;             /* hmm */
-  if (!S_ISLNK(stb.st_mode))
+  cbdata->statsmade++;
+  if (lstat(cbdata->canonspace, &stb) != 0 || !S_ISLNK(stb.st_mode))
     {
-      /* not a symlink, have canon entry */
+      /* not a symlink or stat failed, have new canon entry */
       diroff = addfilesspace(cbdata, l + dirl - i + 2);
       strcpy((char *)cbdata->filesspace + diroff, cbdata->canonspace + cbdata->rootdirl);
       l += dirl - i;
@@ -456,32 +463,33 @@ unifywithcanon(struct cbdata *cbdata, Id diroff, int dirl)
          cbdata->filesspace[diroff + l++] = '/';
          cbdata->filesspace[diroff + l] = 0;
        }
+      /* call normalizedir on new entry for unification purposes */
       dirnameid = normalizedir(cbdata, (char *)cbdata->filesspace + diroff, l, strnhash((char *)cbdata->filesspace + diroff, l), 1);
       return dirnameid == -1 ? diroff : dirnameid;
     }
   /* oh no, a symlink! follow */
-  if (cbdata->rootdirl + l + dirl - i + stb.st_size + 2 > cbdata->canonspacen)
+  lo = cbdata->rootdirl + l + dirl - i + 1;
+  if (lo + stb.st_size + 2 > cbdata->canonspacen)
     {
-      cbdata->canonspacen = cbdata->rootdirl + l + dirl - i + stb.st_size + 20;
+      cbdata->canonspacen = lo + stb.st_size + 20;
       cbdata->canonspace = solv_realloc(cbdata->canonspace, cbdata->canonspacen);
     }
-  lo = cbdata->rootdirl + l + dirl - i + 1;
   ll = readlink(cbdata->canonspace, cbdata->canonspace + lo, stb.st_size);
   if (ll < 0 || ll > stb.st_size)
     return diroff;             /* hmm */
   if (ll == 0)
-    return dirnameid;
+    return dirnameid;          /* empty means current dir */
   if (cbdata->canonspace[lo + ll - 1] != '/')
     cbdata->canonspace[lo + ll++] = '/';       /* add trailing / */
-  cbdata->canonspace[lo + ll] = 0;
+  cbdata->canonspace[lo + ll] = 0;             /* zero terminate */
   if (cbdata->canonspace[lo] != '/')
     {
-      /* relative link, concatenate */
+      /* relative link, concatenate to dirname */
       memmove(cbdata->canonspace + cbdata->rootdirl + l, cbdata->canonspace + lo, ll + 1);
       lo = cbdata->rootdirl;
       ll += l;
     }
-  dirnameid = normalizedir(cbdata, (char *)cbdata->canonspace + lo, ll, strnhash((char *)cbdata->canonspace + lo, ll), 1);
+  dirnameid = normalizedir(cbdata, cbdata->canonspace + lo, ll, strnhash(cbdata->canonspace + lo, ll), 1);
   return dirnameid == -1 ? diroff : dirnameid;
 }
 
@@ -510,7 +518,7 @@ normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create
       if (qx == hx)
        {
          Id off = cbdata->normap[2 * h + 1];
-         const char *dp = (const char *)cbdata->filesspace + cbdata->norq.elements[off];
+         char *dp = (char *)cbdata->filesspace + cbdata->norq.elements[off];
          if (!strncmp(dp, dir, dirl) && dp[dirl] == 0)
            return cbdata->norq.elements[off + 1];
        }
@@ -532,22 +540,21 @@ normalizedir(struct cbdata *cbdata, const char *dir, int dirl, Id hx, int create
     memcpy(cbdata->filesspace + nspaceoff, dir, dirl);
   cbdata->filesspace[nspaceoff + dirl] = 0;
   mycnt = cbdata->norq.count;
-  queue_push2(&cbdata->norq, nspaceoff, -1);
+  queue_push2(&cbdata->norq, nspaceoff, -1);   /* -1: in progress */
   cbdata->normap[2 * h] = hx;
-  cbdata->normap[2 * h + 1] = cbdata->norq.count - 2;
+  cbdata->normap[2 * h + 1] = mycnt;
+  if (++cbdata->normapused * 2 > cbdata->normapn)
+    cbdata->normap = growhash(cbdata->normap, &cbdata->normapn);
   /* unify */
   if (cbdata->usestat)
     nspaceoff = unifywithstat(cbdata, nspaceoff, dirl);
   else
     nspaceoff = unifywithcanon(cbdata, nspaceoff, dirl);
-  /* update */
-  cbdata->norq.elements[mycnt + 1] = nspaceoff;
+  cbdata->norq.elements[mycnt + 1] = nspaceoff;        /* patch in result */
 #if 0
   if (!cbdata->usestat)
     printf("%s normalized to %d: %s\n", cbdata->filesspace + cbdata->norq.elements[mycnt], nspaceoff, cbdata->filesspace + nspaceoff);
 #endif
-  if (++cbdata->normapused * 2 > cbdata->normapn)
-    cbdata->normap = growhash(cbdata->normap, &cbdata->normapn);
   return nspaceoff;
 }
 
@@ -721,8 +728,8 @@ iterate_solvable_dirs(Pool *pool, Id p, void (*cb)(void *, const char *, struct
   dataiterator_free(&di);
 }
 
-/* before calling the expensive findfileconflicts_basename_cb we check if any of
- * the basenames match. This only makes sense when cbdata->create is off.
+/* before calling the expensive findfileconflicts_cb we check if any of
+ * the files match. This only makes sense when cbdata->create is off.
  */
 static int
 precheck_solvable_files(struct cbdata *cbdata, Pool *pool, Id p)
@@ -731,13 +738,42 @@ precheck_solvable_files(struct cbdata *cbdata, Pool *pool, Id p)
   Id hx, qx;
   Hashval h, hh;
   int found = 0;
+  int aliases = cbdata->aliases;
+  unsigned int lastdirid = -1;
+  Hashval lastdirhash = 0;
+  int lastdirlen = 0;
+  int checkthisdir = 0;
+  Repodata *lastrepodata = 0;
 
   dataiterator_init(&di, pool, 0, p, SOLVABLE_FILELIST, 0, SEARCH_COMPLETE_FILELIST);
   while (dataiterator_step(&di))
     {
-      hx = strhash(di.kv.str);
-      if (!hx)
-       hx = strlen(di.kv.str) + 1;
+      if (aliases)
+       {
+         /* hash just the basename */
+         hx = strhash(di.kv.str);
+         if (!hx)
+           hx = strlen(di.kv.str) + 1;
+       }
+      else
+       {
+         /* hash the full path */
+         if (di.data != lastrepodata || di.kv.id != lastdirid)
+           {
+             const char *dir;
+             lastrepodata = di.data;
+             lastdirid = di.kv.id;
+             dir = repodata_dir2str(lastrepodata, lastdirid, "");
+             lastdirlen = strlen(dir);
+             lastdirhash = strhash(dir);
+             checkthisdir =  isindirmap(cbdata, lastdirhash ? lastdirhash : lastdirlen + 1);
+           }
+         if (!checkthisdir)
+           continue;
+         hx = strhash_cont(di.kv.str, lastdirhash);
+         if (!hx)
+           hx = lastdirlen + strlen(di.kv.str) + 1;
+       }
       h = hx & cbdata->cflmapn;
       hh = HASHCHAIN_START;
       for (;;)
@@ -814,10 +850,10 @@ pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, in
       idxmapset = 0;
       for (i = 0; i < pkgs->count; i++)
        {
-         p = pkgs->elements[i];
-         cbdata.idx = i;
          if (i == cutoff)
            cbdata.create = 0;
+         cbdata.idx = i;
+         p = pkgs->elements[i];
          if ((flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed)
            {
              if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
@@ -851,13 +887,13 @@ pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, in
   cbdata.create = 1;
   for (i = 0; i < pkgs->count; i++)
     {
+      if (i == cutoff)
+       cbdata.create = 0;
       if (!cbdata.aliases && !MAPTST(&cbdata.idxmap, i))
        continue;
-      p = pkgs->elements[i];
       cbdata.idx = i;
-      if (i == cutoff)
-       cbdata.create = 0;
-      if (cbdata.aliases && !cbdata.create && FINDFILECONFLICTS_USE_SOLVABLEFILELIST)
+      p = pkgs->elements[i];
+      if (!cbdata.create && (flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed)
        {
          if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed)
            if (!precheck_solvable_files(&cbdata, pool, p))
@@ -1030,13 +1066,13 @@ pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, in
                char *fsj = (char *)cbdata.filesspace + cbdata.files.elements[jj];
                if (cbdata.aliases)
                  {
-                   /* compare just the basenames, the dirs match */
+                   /* compare just the basenames, the dirs match because of the dirid */
                    char *bsi = strrchr(fsi + 34, '/');
                    char *bsj = strrchr(fsj + 34, '/');
-                   if ((!bsi || !bsj) && bsi != bsj)
+                   if (!bsi || !bsj)
                      continue;
                    if (strcmp(bsi, bsj))
-                     continue; /* different file names */
+                     continue; /* different base names */
                  }
                else
                  {
@@ -1044,7 +1080,7 @@ pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, in
                      continue; /* different file names */
                  }
                if (!strcmp(fsi, fsj))
-                 continue;     /* md5 sum matches */
+                 continue;     /* file digests match, no conflict */
                if (obsoleteusescolors && fsi[33] && fsj[33] && (fsi[33] & fsj[33]) == 0)
                  continue;     /* colors do not conflict */
                queue_push(conflicts, pool_str2id(pool, fsi + 34, 1));
@@ -1067,6 +1103,6 @@ pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, in
   POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file conflicts\n", conflicts->count / 6);
   POOL_DEBUG(SOLV_DEBUG_STATS, "file conflict detection took %d ms\n", solv_timems(start));
 
-  return conflicts->count;
+  return conflicts->count / 6;
 }