- start with fileconflicts detection support
authorMichael Schroeder <mls@suse.de>
Fri, 19 Jun 2009 15:35:24 +0000 (17:35 +0200)
committerMichael Schroeder <mls@suse.de>
Fri, 19 Jun 2009 15:35:24 +0000 (17:35 +0200)
src/hash.h
tools/CMakeLists.txt
tools/pool_fileconflicts.c [new file with mode: 0644]
tools/pool_fileconflicts.h [new file with mode: 0644]
tools/repo_rpmdb.c
tools/repo_rpmdb.h

index 829085d..b1b9199 100644 (file)
@@ -51,6 +51,16 @@ strnhash(const char *str, unsigned len)
   return r;
 }
 
+static inline Hashval
+strhash_cont(const char *str, Hashval r)
+{
+  unsigned int c;
+  while ((c = *(const unsigned char *)str++) != 0)
+    r += (r << 3) + c;
+  return r;
+}
+
+
 /* hash for rel
  * rel -> hash
  */
index 5f20cf0..741bb10 100644 (file)
@@ -57,6 +57,10 @@ TARGET_LINK_LIBRARIES( dumpsolv satsolver)
 ADD_EXECUTABLE( mergesolv mergesolv.c )
 TARGET_LINK_LIBRARIES( mergesolv satsolver toolstuff)
 
+SET(findfileconflicts_SOURCES findfileconflicts.c pool_fileconflicts.c repo_rpmdb.c )
+ADD_EXECUTABLE( findfileconflicts ${findfileconflicts_SOURCES} )
+TARGET_LINK_LIBRARIES( findfileconflicts satsolver ${RPMDB_LIBRARY} ${EXPAT_LIBRARY} )
+
 install(TARGETS
                 mergesolv
                 dumpsolv
diff --git a/tools/pool_fileconflicts.c b/tools/pool_fileconflicts.c
new file mode 100644 (file)
index 0000000..5b1c4a0
--- /dev/null
@@ -0,0 +1,382 @@
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "hash.h"
+#include "repo_rpmdb.h"
+
+struct cbdata {
+  Pool *pool;
+  Queue lookat;
+  Queue lookat_dir;
+
+  Hashval *cflmap;
+  Hashmask cflmapn;
+  unsigned int cflmapused;
+
+  Hashval *dirmap;
+  Hashmask dirmapn;
+  unsigned int dirmapused;
+
+  Hashval idx;
+  unsigned int hx;
+
+  Queue files;
+  unsigned char *filesspace;
+  unsigned int filesspacen;
+};
+
+#define FILESSPACE_BLOCK 255
+
+static Hashval *
+doublehash(Hashval *map, Hashmask *mapnp)
+{
+  Hashmask mapn = *mapnp;
+  Hashmask i, hx, qx, h, hh;
+  Hashmask nn = (mapn + 1) * 2 - 1;
+  Hashmask *m;
+
+  m = sat_calloc(nn + 1, 2 * sizeof(Id));
+  for (i = 0; i <= mapn; i++)
+    {
+      hx = map[2 * i];
+      if (!hx)
+       continue;
+      h = hx & nn;
+      hh = HASHCHAIN_START;
+      for (;;)
+       {
+         qx = m[2 * h];
+         if (!qx)
+           break;
+         h = HASHCHAIN_NEXT(h, hh, nn);
+       }
+      m[2 * h] = hx;
+      m[2 * h + 1] = map[2 * i + 1];
+    }
+  sat_free(map);
+  *mapnp = nn;
+  return m;
+}
+
+static void
+finddirs_cb(void *cbdatav, char *fn, int fmode, char *md5)
+{
+  struct cbdata *cbdata = cbdatav;
+  Hashmask h, hh, hx, qx;
+  Hashval idx = cbdata->idx;
+
+  hx = strhash(fn);
+  if (!hx)
+    hx = strlen(fn) + 1;
+  h = hx & cbdata->dirmapn;
+  hh = HASHCHAIN_START;
+  for (;;)
+    {
+      qx = cbdata->dirmap[2 * h];
+      if (!qx)
+       break;
+      if (qx == hx)
+       break;
+      h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
+    }
+  if (!qx)
+    {
+      /* a miss */
+      cbdata->dirmap[2 * h] = hx;
+      cbdata->dirmap[2 * h + 1] = idx;
+      cbdata->dirmapused++;
+      if (cbdata->dirmapused * 2 > cbdata->dirmapn)
+       cbdata->dirmap = doublehash(cbdata->dirmap, &cbdata->dirmapn);
+      return;
+    }
+  if (cbdata->dirmap[2 * h + 1] == idx)
+    return;
+  /* found a conflict, this dir is used in multiple packages */
+  cbdata->dirmap[2 * h + 1] = -1;
+}
+
+static inline int
+isindirmap(struct cbdata *cbdata, Hashmask hx)
+{
+  Hashmask h, hh, qx;
+
+  h = hx & cbdata->dirmapn;
+  hh = HASHCHAIN_START;
+  for (;;)
+    {
+      qx = cbdata->dirmap[2 * h];
+      if (!qx)
+       return 0;
+      if (qx == hx)
+       return cbdata->dirmap[2 * h + 1] == -1 ? 1 : 0;
+      h = HASHCHAIN_NEXT(h, hh, cbdata->dirmapn);
+    }
+}
+
+static void
+findfileconflicts_cb(void *cbdatav, char *fn, int fmode, char *md5)
+{
+  struct cbdata *cbdata = cbdatav;
+  int isdir = S_ISDIR(fmode);
+  char *dp;
+  Hashval idx, qidx;
+  Hashmask qx, h, hx, hh, dhx;
+
+  idx = cbdata->idx;
+
+  dp = strrchr(fn, '/');
+  if (!dp)
+    return;
+  dhx = strnhash(fn, dp + 1 - fn);
+  if (!dhx)
+    dhx = 1 + dp + 1 - fn;
+#if 1
+  if (!isindirmap(cbdata, dhx))
+    return;
+#endif
+
+  hx = strhash_cont(dp + 1, dhx);
+  if (!hx)
+    hx = strlen(fn) + 1;
+
+  h = hx & cbdata->cflmapn;
+  hh = HASHCHAIN_START;
+  for (;;)
+    {
+      qx = cbdata->cflmap[2 * h];
+      if (!qx)
+       break;
+      if (qx == hx)
+       break;
+      h = HASHCHAIN_NEXT(h, hh, cbdata->cflmapn);
+    }
+  if (!qx)
+    {
+      cbdata->cflmap[2 * h] = hx;
+      cbdata->cflmap[2 * h + 1] = (isdir ? ~idx : idx);
+      cbdata->cflmapused++;
+      if (cbdata->cflmapused * 2 > cbdata->cflmapn)
+       cbdata->cflmap = doublehash(cbdata->cflmap, &cbdata->cflmapn);
+      return;
+    }
+  qidx = cbdata->cflmap[2 * h + 1];
+  if ((int)qidx < 0)
+    {
+      int i;
+      qidx = ~qidx;
+      if (isdir)
+       {
+         /* delay the conflict */
+          queue_push2(&cbdata->lookat_dir, hx, qidx);
+          queue_push2(&cbdata->lookat_dir, hx, idx);
+         return;
+       }
+      cbdata->cflmap[2 * h + 1] = qidx;
+      for (i = 0; i < cbdata->lookat_dir.count; i += 2)
+       if (cbdata->lookat_dir.elements[i] == hx)
+         queue_push2(&cbdata->lookat, hx, cbdata->lookat_dir.elements[i + 1]);
+    }
+  if (qidx == idx)
+    return;    /* no conflicts with ourself, please */
+  queue_push2(&cbdata->lookat, hx, qidx);
+  queue_push2(&cbdata->lookat, hx, idx);
+}
+
+static inline void
+addfilesspace(struct cbdata *cbdata, unsigned char *data, int len)
+{
+  cbdata->filesspace = sat_extend(cbdata->filesspace, cbdata->filesspacen, len, 1, FILESSPACE_BLOCK);
+  memcpy(cbdata->filesspace + cbdata->filesspacen, data, len);
+  cbdata->filesspacen += len;
+}
+
+static void
+findfileconflicts2_cb(void *cbdatav, char *fn, int fmode, char *md5)
+{
+  struct cbdata *cbdata = cbdatav;
+  unsigned int hx = strhash(fn);
+  char md5padded[33];
+
+  if (!hx)
+    hx = strlen(fn) + 1;
+  if (hx != cbdata->hx)
+    return;
+  strncpy(md5padded, md5, 32);
+  md5padded[32] = 0;
+  // printf("%d, hx %x -> %s   %d %s\n", cbdata->idx, hx, fn, fmode, md5);
+  queue_push(&cbdata->files, cbdata->filesspacen);
+  addfilesspace(cbdata, (unsigned char *)md5padded, 33);
+  addfilesspace(cbdata, (unsigned char *)fn, strlen(fn) + 1);
+}
+
+static int cand_sort(const void *ap, const void *bp, void *dp)
+{
+  const Id *a = ap;
+  const Id *b = bp;
+
+  unsigned int ax = (unsigned int)a[0];
+  unsigned int bx = (unsigned int)b[0];
+  if (ax < bx)
+    return -1;
+  if (ax > bx)
+    return 1;
+  return (a[1] < 0 ? -a[1] : a[1]) - (b[1] < 0 ? -b[1] : b[1]);
+}
+
+static int conflicts_cmp(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  const Id *a = ap;
+  const Id *b = bp;
+  if (a[0] != b[0])
+    return strcmp(id2str(pool, a[0]), id2str(pool, b[0]));
+  if (a[1] != b[1])
+    return a[1] - b[1];
+  if (a[3] != b[3])
+    return a[3] - b[3];
+  return 0;
+}
+
+int
+pool_findfileconflicts(Pool *pool, Queue *pkgs, Queue *conflicts, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata)
+{
+  int i, j, cflmapn;
+  unsigned int hx;
+  struct cbdata cbdata;
+  unsigned int now, start;
+  void *handle;
+
+  queue_empty(conflicts);
+  if (!pkgs->count)
+    return 0;
+
+  now = start = sat_timems(0);
+  printf("packages: %d\n", pkgs->count);
+
+  memset(&cbdata, 0, sizeof(cbdata));
+  cbdata.pool = pool;
+  queue_init(&cbdata.lookat);
+  queue_init(&cbdata.lookat_dir);
+  queue_init(&cbdata.files);
+
+  /* avarage file list size: 200 files per package */
+  /* avarage dir count: 20 dirs per package */
+
+  /* first pass: scan dirs */
+  cflmapn = pkgs->count * 64;
+  while ((cflmapn & (cflmapn - 1)) != 0)
+    cflmapn = cflmapn & (cflmapn - 1);
+  cbdata.dirmap = sat_calloc(cflmapn, 2 * sizeof(Id));
+  cbdata.dirmapn = cflmapn - 1;        /* make it a mask */
+  for (i = 0; i < pkgs->count; i++)
+    {
+      Id p = pkgs->elements[i];
+      cbdata.idx = p;
+      handle = (*handle_cb)(pool, p, handle_cbdata);
+      if (handle)
+        rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_ONLYDIRS, finddirs_cb, &cbdata);
+    }
+
+  printf("dirmap size: %d used %d\n", cbdata.dirmapn + 1, cbdata.dirmapused);
+  printf("dirmap memory usage: %d K\n", (cbdata.dirmapn + 1) * 2 * (int)sizeof(Id) / 1024);
+  printf("dirmap creation took %d ms\n", sat_timems(now));
+
+  /* second pass: scan files */
+  now = sat_timems(0);
+  cflmapn = pkgs->count * 128;
+  while ((cflmapn & (cflmapn - 1)) != 0)
+    cflmapn = cflmapn & (cflmapn - 1);
+  cbdata.cflmap = sat_calloc(cflmapn, 2 * sizeof(Id));
+  cbdata.cflmapn = cflmapn - 1;        /* make it a mask */
+  for (i = 0; i < pkgs->count; i++)
+    {
+      Id p = pkgs->elements[i];
+      cbdata.idx = p;
+      handle = (*handle_cb)(pool, p, handle_cbdata);
+      if (handle)
+        rpm_iterate_filelist(handle, 0, findfileconflicts_cb, &cbdata);
+    }
+
+  printf("filemap size: %d used %d\n", cbdata.cflmapn + 1, cbdata.cflmapused);
+  printf("filemap memory usage: %d K\n", (cbdata.cflmapn + 1) * 2 * (int)sizeof(Id) / 1024);
+  printf("filemap creation took %d ms\n", sat_timems(now));
+
+  cbdata.dirmap = sat_free(cbdata.dirmap);
+  cbdata.dirmapn = 0;
+  cbdata.dirmapused = 0;
+  cbdata.cflmap = sat_free(cbdata.cflmap);
+  cbdata.cflmapn = 0;
+  cbdata.cflmapused = 0;
+
+  now = sat_timems(0);
+  printf("lookat_dir size: %d\n", cbdata.lookat_dir.count);
+  queue_free(&cbdata.lookat_dir);
+  sat_sort(cbdata.lookat.elements, cbdata.lookat.count / 2, sizeof(Id) * 2, &cand_sort, pool);
+  /* unify */
+  for (i = j = 0; i < cbdata.lookat.count; i += 2)
+    {
+      hx = cbdata.lookat.elements[i];
+      Id p = cbdata.lookat.elements[i + 1];
+      if (j && hx == cbdata.lookat.elements[j - 2] && p == cbdata.lookat.elements[j - 1])
+       continue;
+      cbdata.lookat.elements[j++] = hx;
+      cbdata.lookat.elements[j++] = p;
+    }
+  printf("candidates: %d\n", cbdata.lookat.count / 2);
+
+  /* third pass: scan candidates */
+  for (i = 0; i < cbdata.lookat.count - 2; i += 2)
+    {
+      int pend, ii, jj;
+      Id p = cbdata.lookat.elements[i + 1];
+
+      hx = cbdata.lookat.elements[i];
+      if (cbdata.lookat.elements[i + 2] != hx)
+       continue;       /* no package left */
+      queue_empty(&cbdata.files);
+      cbdata.filesspace = sat_free(cbdata.filesspace);
+      cbdata.filesspacen = 0;
+
+      cbdata.idx = p;
+      cbdata.hx = cbdata.lookat.elements[i];
+      handle = (*handle_cb)(pool, p, handle_cbdata);
+      if (!handle)
+       continue;
+      rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_WITHMD5, findfileconflicts2_cb, &cbdata);
+
+      pend = cbdata.files.count;
+      for (j = i + 2; j < cbdata.lookat.count && cbdata.lookat.elements[j] == hx; j++)
+       {
+         Id q = cbdata.lookat.elements[j + 1];
+         cbdata.idx = q;
+         handle = (*handle_cb)(pool, q, handle_cbdata);
+         if (!handle)
+           continue;
+         rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_WITHMD5, findfileconflicts2_cb, &cbdata);
+          for (ii = 0; ii < pend; ii++)
+           for (jj = pend; jj < cbdata.files.count; jj++)
+             {
+               if (strcmp((char *)cbdata.filesspace + cbdata.files.elements[ii] + 33, (char *)cbdata.filesspace + cbdata.files.elements[jj] + 33))
+                 continue;
+               if (!strcmp((char *)cbdata.filesspace + cbdata.files.elements[ii], (char *)cbdata.filesspace + cbdata.files.elements[jj]))
+                 continue;
+               queue_push(conflicts, str2id(pool, (char *)cbdata.filesspace + cbdata.files.elements[ii] + 33, 1));
+               queue_push(conflicts, p);
+               queue_push(conflicts, str2id(pool, (char *)cbdata.filesspace + cbdata.files.elements[ii], 1));
+               queue_push(conflicts, q);
+               queue_push(conflicts, str2id(pool, (char *)cbdata.filesspace + cbdata.files.elements[jj], 1));
+             }
+       }
+    }
+  cbdata.filesspace = sat_free(cbdata.filesspace);
+  cbdata.filesspacen = 0;
+  printf("candidate check took %d ms\n", sat_timems(now));
+  if (conflicts->count > 5)
+    sat_sort(conflicts->elements, conflicts->count / 5, 5 * sizeof(Id), conflicts_cmp, pool);
+  (*handle_cb)(pool, 0, handle_cbdata);
+  printf("conflict detection took %d ms\n", sat_timems(start));
+  return conflicts->count;
+}
+
diff --git a/tools/pool_fileconflicts.h b/tools/pool_fileconflicts.h
new file mode 100644 (file)
index 0000000..cb080f2
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef POOL_FILECONFLICTS_H
+#define POOL_FILECONFLICTS_H
+
+#include "pool.h"
+
+extern int pool_findfileconflicts(Pool *pool, Queue *pkgs, Queue *conflicts, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata);
+
+#endif
index bdd58d2..ddb6b68 100644 (file)
@@ -55,6 +55,8 @@
 #define TAG_ARCH               1022
 #define TAG_FILESIZES          1028
 #define TAG_FILEMODES          1030
+#define TAG_FILEMD5S           1035
+#define TAG_FILELINKTOS                1036
 #define TAG_SOURCERPM          1044
 #define TAG_PROVIDENAME                1047
 #define TAG_REQUIREFLAGS       1048
@@ -1358,7 +1360,10 @@ repo_add_rpmdb(Repo *repo, Repo *ref, const char *rootdir, int flags)
              exit(1);
            }
          if (dbdata.size > rpmheadsize)
-           rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + dbdata.size);
+           {
+             rpmheadsize = dbdata.size + 128;
+             rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
+           }
          memcpy(buf, dbdata.data, 8);
          rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
          rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
@@ -1585,7 +1590,10 @@ repo_add_rpmdb(Repo *repo, Repo *ref, const char *rootdir, int flags)
              exit(1);
            }
          if (dbdata.size > rpmheadsize)
-           rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + dbdata.size);
+           {
+             rpmheadsize = dbdata.size + 128;
+             rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
+           }
          memcpy(buf, dbdata.data, 8);
          rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
          rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
@@ -1736,7 +1744,10 @@ repo_add_rpms(Repo *repo, const char **rpms, int nrpms, int flags)
       l = sigdsize + sigcnt * 16;
       headerend = headerstart + 16 + l;
       if (l > rpmheadsize)
-       rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + l);
+       {
+         rpmheadsize = l + 128;
+         rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
+       }
       if (fread(rpmhead->data, l, 1, fp) != 1)
        {
          fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
@@ -1775,3 +1786,351 @@ repo_add_rpms(Repo *repo, const char **rpms, int nrpms, int flags)
   if (!(flags & REPO_NO_INTERNALIZE))
     repodata_internalize(data);
 }
+
+static inline void
+linkhash(const char *lt, char *hash)
+{
+  unsigned int r = 0;
+  const unsigned char *str = (const unsigned char *)lt;
+  int l, c;
+
+  l = strlen(lt);
+  while ((c = *str++) != 0)
+    r += (r << 3) + c;
+  sprintf(hash, "%08x", r);
+  sprintf(hash + 8, "%08x", l);
+  sprintf(hash + 16, "%08x", 0);
+  sprintf(hash + 24, "%08x", 0);
+}
+
+void
+rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, char *, int, char *), void *cbdata)
+{
+  RpmHead *rpmhead = rpmhandle;
+  char **bn;
+  char **dn;
+  char **md = 0;
+  char **lt = 0;
+  unsigned int *di;
+  unsigned int *fm;
+  int cnt, dcnt, cnt2;
+  int i, l1, l;
+  char *space = 0;
+  int spacen = 0;
+  char md5[33], *md5p = 0;
+
+  dn = headstringarray(rpmhead, TAG_DIRNAMES, &dcnt);
+  if (!dn)
+    return;
+  if ((flags & RPM_ITERATE_FILELIST_ONLYDIRS) != 0)
+    {
+      for (i = 0; i < dcnt; i++)
+       (*cb)(cbdata, dn[i], 0, (char *)0);
+      sat_free(dn);
+      return;
+    }
+  bn = headstringarray(rpmhead, TAG_BASENAMES, &cnt);
+  if (!bn)
+    {
+      sat_free(dn);
+      return;
+    }
+  di = headint32array(rpmhead, TAG_DIRINDEXES, &cnt2);
+  if (!di || cnt != cnt2)
+    {
+      sat_free(di);
+      sat_free(bn);
+      sat_free(dn);
+      return;
+    }
+  fm = headint16array(rpmhead, TAG_FILEMODES, &cnt2);
+  if (!fm || cnt != cnt2)
+    {
+      sat_free(fm);
+      sat_free(di);
+      sat_free(bn);
+      sat_free(dn);
+      return;
+    }
+  if ((flags & RPM_ITERATE_FILELIST_WITHMD5) != 0)
+    {
+      md = headstringarray(rpmhead, TAG_FILEMD5S, &cnt2);
+      if (!md || cnt != cnt2)
+       {
+         sat_free(md);
+         sat_free(fm);
+         sat_free(di);
+         sat_free(bn);
+         sat_free(dn);
+         return;
+       }
+    }
+  for (i = 0; i < cnt; i++)
+    {
+      if (di[i] >= dcnt)
+       continue;
+      l1 = strlen(dn[di[i]]);
+      if (l1 == 0)
+       continue;
+      l = l1 + strlen(bn[i]) + 1;
+      if (l > spacen)
+       {
+         spacen = l + 16;
+         space = sat_realloc(space, spacen);
+       }
+      strcpy(space, dn[di[i]]);
+      strcpy(space + l1, bn[i]);
+      if (md)
+       {
+         md5p = md[i];
+         if (S_ISLNK(fm[i]))
+           {
+             md5p = 0;
+             if (!lt)
+               {
+                 lt = headstringarray(rpmhead, TAG_FILELINKTOS, &cnt2);
+                 if (cnt != cnt2)
+                   lt = sat_free(lt);
+               }
+             if (lt)
+               {
+                 linkhash(lt[i], md5);
+                 md5p = md5;
+               }
+           }
+         if (!md5p)
+           md5p = "";
+       }
+      (*cb)(cbdata, space, fm[i], md5p);
+    }
+  sat_free(lt);
+  sat_free(md);
+  sat_free(fm);
+  sat_free(di);
+  sat_free(bn);
+  sat_free(dn);
+}
+
+
+struct rpm_by_state {
+  RpmHead *rpmhead;
+  int rpmheadsize;
+
+  int dbopened;
+  DB_ENV *dbenv;
+  DB *db;
+  int byteswapped;
+};
+
+void *
+rpm_byrpmdbid(Id rpmdbid, const char *rootdir, void **statep)
+{
+  struct rpm_by_state *state = *statep;
+  unsigned char buf[16];
+  DBT dbkey;
+  DBT dbdata;
+  RpmHead *rpmhead;
+
+  if (!rpmdbid)
+    {
+      /* close down */
+      if (!state)
+       return 0;
+      if (state->db)
+       state->db->close(state->db, 0);
+      if (state->dbenv)
+        state->dbenv->close(state->dbenv, 0);
+      sat_free(state->rpmhead);
+      sat_free(state);
+      *statep = (void *)0;
+      return 0;
+    }
+
+  if (!state)
+    {
+      state = sat_calloc(1, sizeof(*state));
+      *statep = state;
+    }
+  if (!state->dbopened)
+    {
+      char dbpath[PATH_MAX];
+      state->dbopened = 1;
+      if (db_env_create(&state->dbenv, 0))
+       {
+         perror("db_env_create");
+         state->dbenv = 0;
+         return 0;
+       }
+      if (!rootdir)
+       rootdir = "";
+      snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir);
+#ifdef FEDORA
+      if (state->dbenv->open(state->dbenv, dbpath, DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL, 0))
+#else
+      if (state->dbenv->open(state->dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0))
+#endif
+       {
+         perror("dbenv open");
+         state->dbenv->close(state->dbenv, 0);
+         state->dbenv = 0;
+         return 0;
+       }
+      if (db_create(&state->db, state->dbenv, 0))
+       {
+         perror("db_create");
+         state->db = 0;
+         state->dbenv->close(state->dbenv, 0);
+         state->dbenv = 0;
+         return 0;
+       }
+      if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
+       {
+         perror("db->open var/lib/rpm/Packages");
+         state->db->close(state->db, 0);
+         state->db = 0;
+         state->dbenv->close(state->dbenv, 0);
+         state->dbenv = 0;
+         return 0;
+       }
+      if (state->db->get_byteswapped(state->db, &state->byteswapped))
+       {
+         perror("db->get_byteswapped");
+         state->db->close(state->db, 0);
+         state->db = 0;
+         state->dbenv->close(state->dbenv, 0);
+         state->dbenv = 0;
+         return 0;
+       }
+    }
+  memcpy(buf, &rpmdbid, 4);
+  if (state->byteswapped)
+    {
+      unsigned char bx;
+      bx = buf[0]; buf[0] = buf[3]; buf[3] = bx;
+      bx = buf[1]; buf[1] = buf[2]; buf[2] = bx;
+    }
+  memset(&dbkey, 0, sizeof(dbkey));
+  memset(&dbdata, 0, sizeof(dbdata));
+  dbkey.data = buf;
+  dbkey.size = 4;
+  dbdata.data = 0;
+  dbdata.size = 0;
+  if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
+    {
+      perror("db->get");
+      return 0;
+    }
+  if (dbdata.size < 8)
+    {
+      fprintf(stderr, "corrupt rpm database (size)\n");
+      return 0;
+    }
+  if (dbdata.size > state->rpmheadsize)
+    {
+      state->rpmheadsize = dbdata.size + 128;
+      state->rpmhead = sat_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
+    }
+  rpmhead = state->rpmhead;
+  memcpy(buf, dbdata.data, 8);
+  rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
+  rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
+  if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
+    {
+      fprintf(stderr, "corrupt rpm database (data size)\n");
+      return 0;
+    }
+  memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
+  rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+  return rpmhead;
+}
+void *
+rpm_byfp(FILE *fp, const char *name, void **statep)
+{
+  struct rpm_by_state *state = *statep;
+  int headerstart, headerend;
+  RpmHead *rpmhead;
+  int sigdsize, sigcnt, l;
+  unsigned char lead[4096];
+
+  if (!fp)
+    return rpm_byrpmdbid(0, 0, statep);
+  if (!state)
+    {
+      state = sat_calloc(1, sizeof(*state));
+      *statep = state;
+    }
+  if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
+    {
+      fprintf(stderr, "%s: not a rpm\n", name);
+      return 0;
+    }
+  if (lead[78] != 0 || lead[79] != 5)
+    {
+      fprintf(stderr, "%s: not a V5 header\n", name);
+      return 0;
+    }
+  if (getu32(lead + 96) != 0x8eade801)
+    {
+      fprintf(stderr, "%s: bad signature header\n", name);
+      return 0;
+    }
+  sigcnt = getu32(lead + 96 + 8);
+  sigdsize = getu32(lead + 96 + 12);
+  if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
+    {
+      fprintf(stderr, "%s: bad signature header\n", name);
+      return 0;
+    }
+  sigdsize += sigcnt * 16;
+  sigdsize = (sigdsize + 7) & ~7;
+  headerstart = 96 + 16 + sigdsize;
+  while (sigdsize)
+    {
+      l = sigdsize > 4096 ? 4096 : sigdsize;
+      if (fread(lead, l, 1, fp) != 1)
+       {
+         fprintf(stderr, "%s: unexpected EOF\n", name);
+         return 0;
+       }
+      sigdsize -= l;
+    }
+  if (fread(lead, 16, 1, fp) != 1)
+    {
+      fprintf(stderr, "%s: unexpected EOF\n", name);
+      return 0;
+    }
+  if (getu32(lead) != 0x8eade801)
+    {
+      fprintf(stderr, "%s: bad header\n", name);
+      fclose(fp);
+      return 0;
+    }
+  sigcnt = getu32(lead + 8);
+  sigdsize = getu32(lead + 12);
+  if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
+    {
+      fprintf(stderr, "%s: bad header\n", name);
+      fclose(fp);
+      return 0;
+    }
+  l = sigdsize + sigcnt * 16;
+  headerend = headerstart + 16 + l;
+  if (l > state->rpmheadsize)
+    {
+      state->rpmheadsize = l + 128;
+      state->rpmhead = sat_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
+    }
+  rpmhead = state->rpmhead;
+  if (fread(rpmhead->data, l, 1, fp) != 1)
+    {
+      fprintf(stderr, "%s: unexpected EOF\n", name);
+      fclose(fp);
+      return 0;
+    }
+  rpmhead->cnt = sigcnt;
+  rpmhead->dcnt = sigdsize;
+  rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+  return rpmhead;
+}
+
index bd416c8..6c907c7 100644 (file)
@@ -9,3 +9,10 @@ extern void repo_add_rpmdb(Repo *repo, Repo *ref, const char *rootdir, int flags
 extern void repo_add_rpms(Repo *repo, const char **rpms, int nrpms, int flags);
 
 #define RPMDB_REPORT_PROGRESS (1 << 8)
+
+#define RPM_ITERATE_FILELIST_ONLYDIRS  (1 << 0)
+#define RPM_ITERATE_FILELIST_WITHMD5   (1 << 1)
+
+void *rpm_byrpmdbid(Id rpmdbid, const char *rootdir, void **statep);
+void *rpm_byfp(FILE *fp, const char *name, void **statep);
+void rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, char *, int, char *), void *cbdata);