rpm 5.3.7

rpmdb/rpmrepo.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 #if defined(WITH_DBSQL)
00008 #include <db51/dbsql.h>
00009 #elif defined(WITH_SQLITE)
00010 #include <sqlite3.h>
00011 #ifdef  __LCLINT__
00012 /*@-incondefs -redecl @*/
00013 extern const char *sqlite3_errmsg(sqlite3 *db)
00014         /*@*/;
00015 extern int sqlite3_open(
00016   const char *filename,            /* Database filename (UTF-8) */
00017   /*@out@*/ sqlite3 **ppDb         /* OUT: SQLite db handle */
00018 )
00019         /*@modifies *ppDb @*/;
00020 extern int sqlite3_exec(
00021   sqlite3 *db,                     /* An open database */
00022   const char *sql,                 /* SQL to be evaluted */
00023   int (*callback)(void*,int,char**,char**),  /* Callback function */
00024   void *,                          /* 1st argument to callback */
00025   /*@out@*/ char **errmsg          /* Error msg written here */
00026 )
00027         /*@modifies db, *errmsg @*/;
00028 extern int sqlite3_prepare(
00029   sqlite3 *db,                     /* Database handle */
00030   const char *zSql,                /* SQL statement, UTF-8 encoded */
00031   int nByte,                       /* Maximum length of zSql in bytes. */
00032   /*@out@*/ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
00033   /*@out@*/ const char **pzTail    /* OUT: Pointer to unused portion of zSql */
00034 )
00035         /*@modifies *ppStmt, *pzTail @*/;
00036 extern int sqlite3_reset(sqlite3_stmt *pStmt)
00037         /*@modifies pStmt @*/;
00038 extern int sqlite3_step(sqlite3_stmt *pStmt)
00039         /*@modifies pStmt @*/;
00040 extern int sqlite3_finalize(/*@only@*/ sqlite3_stmt *pStmt)
00041         /*@modifies pStmt @*/;
00042 extern int sqlite3_close(sqlite3 * db)
00043         /*@modifies db @*/;
00044 /*@=incondefs =redecl @*/
00045 #endif  /* __LCLINT__ */
00046 #endif  /* WITH_SQLITE */
00047 
00048 #include <rpmio_internal.h>     /* XXX fdInitDigest() et al */
00049 #include <rpmiotypes.h>
00050 #include <rpmio.h>      /* for *Pool methods */
00051 #include <rpmlog.h>
00052 #include <rpmurl.h>
00053 #include <poptIO.h>
00054 
00055 #define _RPMREPO_INTERNAL
00056 #include <rpmrepo.h>
00057 
00058 #include <rpmtypes.h>
00059 #include <rpmtag.h>
00060 #include <pkgio.h>
00061 #include <rpmts.h>
00062 
00063 #include "debug.h"
00064 
00065 /*@unchecked@*/
00066 int _rpmrepo_debug = 0;
00067 
00068 #define REPODBG(_l) if (_rpmrepo_debug) fprintf _l
00069 
00070 /*==============================================================*/
00071 
00072 /*@unchecked@*/ /*@observer@*/
00073 static const char primary_xml_init[] =
00074 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
00075 "<metadata xmlns=\"http://linux.duke.edu/metadata/common\" xmlns:rpm=\"http://linux.duke.edu/metadata/rpm\" packages=\"0\">\n";
00076 /*@unchecked@*/ /*@observer@*/
00077 static const char primary_xml_fini[] = "</metadata>\n";
00078 
00079 /*@unchecked@*/ /*@observer@*/
00080 static const char filelists_xml_init[] =
00081 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
00082 "<filelists xmlns=\"http://linux.duke.edu/metadata/filelists\" packages=\"0\">\n";
00083 /*@unchecked@*/ /*@observer@*/
00084 static const char filelists_xml_fini[] = "</filelists>\n";
00085 
00086 /*@unchecked@*/ /*@observer@*/
00087 static const char other_xml_init[] =
00088 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
00089 "<otherdata xmlns=\"http://linux.duke.edu/metadata/other\" packages=\"0\">\n";
00090 /*@unchecked@*/ /*@observer@*/
00091 static const char other_xml_fini[] = "</otherdata>\n";
00092 
00093 /*@unchecked@*/ /*@observer@*/
00094 static const char repomd_xml_init[] = "\
00095 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
00096 <repomd xmlns=\"http://linux.duke.edu/metadata/repo\">\n";
00097 /*@unchecked@*/ /*@observer@*/
00098 static const char repomd_xml_fini[] = "</repomd>\n";
00099 
00100 /* XXX todo: wire up popt aliases and bury the --queryformat glop externally. */
00101 /*@unchecked@*/ /*@observer@*/
00102 static const char primary_xml_qfmt[] =
00103 #include "yum_primary_xml"
00104 ;
00105 
00106 /*@unchecked@*/ /*@observer@*/
00107 static const char filelists_xml_qfmt[] =
00108 #include "yum_filelists_xml"
00109 ;
00110 
00111 /*@unchecked@*/ /*@observer@*/
00112 static const char other_xml_qfmt[] =
00113 #include "yum_other_xml"
00114 ;
00115 
00116 /*@unchecked@*/ /*@observer@*/
00117 static const char primary_yaml_qfmt[] =
00118 #include "wnh_primary_yaml"
00119 ;
00120 
00121 /*@unchecked@*/ /*@observer@*/
00122 static const char filelists_yaml_qfmt[] =
00123 #include "wnh_filelists_yaml"
00124 ;
00125 
00126 /*@unchecked@*/ /*@observer@*/
00127 static const char other_yaml_qfmt[] =
00128 #include "wnh_other_yaml"
00129 ;
00130 
00131 /*@unchecked@*/ /*@observer@*/
00132 static const char Packages_qfmt[] =
00133 #include "deb_Packages"
00134 ;
00135 
00136 /*@unchecked@*/ /*@observer@*/
00137 static const char Sources_qfmt[] =
00138 #include "deb_Sources"
00139 ;
00140 
00141 /*@-nullassign@*/
00142 /*@unchecked@*/ /*@observer@*/
00143 static const char *primary_sql_init[] = {
00144 "PRAGMA synchronous = \"OFF\";",
00145 "pragma locking_mode = \"EXCLUSIVE\";",
00146 "CREATE TABLE conflicts (  pkgKey INTEGER,  name TEXT,  flags TEXT,  epoch TEXT,  version TEXT,  release TEXT );",
00147 "CREATE TABLE db_info (dbversion INTEGER,  checksum TEXT);",
00148 "CREATE TABLE files (  pkgKey INTEGER,  name TEXT,  type TEXT );",
00149 "CREATE TABLE obsoletes (  pkgKey INTEGER,  name TEXT,  flags TEXT,  epoch TEXT,  version TEXT,  release TEXT );",
00150 "CREATE TABLE packages (  pkgKey INTEGER PRIMARY KEY,  pkgId TEXT,  name TEXT,  arch TEXT,  version TEXT,  epoch TEXT,  release TEXT,  summary TEXT,  description TEXT,  url TEXT,  time_file INTEGER,  time_build INTEGER,  rpm_license TEXT,  rpm_vendor TEXT,  rpm_group TEXT,  rpm_buildhost TEXT,  rpm_sourcerpm TEXT,  rpm_header_start INTEGER,  rpm_header_end INTEGER,  rpm_packager TEXT,  size_package INTEGER,  size_installed INTEGER,  size_archive INTEGER,  location_href TEXT,  location_base TEXT,  checksum_type TEXT);",
00151 "CREATE TABLE provides (  pkgKey INTEGER,  name TEXT,  flags TEXT,  epoch TEXT,  version TEXT,  release TEXT );",
00152 "CREATE TABLE requires (  pkgKey INTEGER,  name TEXT,  flags TEXT,  epoch TEXT,  version TEXT,  release TEXT  );",
00153 "CREATE INDEX filenames ON files (name);",
00154 "CREATE INDEX packageId ON packages (pkgId);",
00155 "CREATE INDEX packagename ON packages (name);",
00156 "CREATE INDEX pkgconflicts on conflicts (pkgKey);",
00157 "CREATE INDEX pkgobsoletes on obsoletes (pkgKey);",
00158 "CREATE INDEX pkgprovides on provides (pkgKey);",
00159 "CREATE INDEX pkgrequires on requires (pkgKey);",
00160 "CREATE INDEX providesname ON provides (name);",
00161 "CREATE INDEX requiresname ON requires (name);",
00162 "CREATE TRIGGER removals AFTER DELETE ON packages\
00163 \n    BEGIN\n\
00164 \n    DELETE FROM files WHERE pkgKey = old.pkgKey;\
00165 \n    DELETE FROM requires WHERE pkgKey = old.pkgKey;\
00166 \n    DELETE FROM provides WHERE pkgKey = old.pkgKey;\
00167 \n    DELETE FROM conflicts WHERE pkgKey = old.pkgKey;\
00168 \n    DELETE FROM obsoletes WHERE pkgKey = old.pkgKey;\
00169 \n    END;",
00170 "INSERT into db_info values (9, 'direct_create');",
00171     NULL
00172 };
00173 /*XXX todo: DBVERSION needs to be set */
00174 
00175 /*@unchecked@*/ /*@observer@*/
00176 static const char *filelists_sql_init[] = {
00177 "PRAGMA synchronous = \"OFF\";",
00178 "pragma locking_mode = \"EXCLUSIVE\";",
00179 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);",
00180 "CREATE TABLE filelist (  pkgKey INTEGER,  name TEXT,  type TEXT );",
00181 "CREATE TABLE packages (  pkgKey INTEGER PRIMARY KEY,  pkgId TEXT);",
00182 "CREATE INDEX filelistnames ON filelist (name);",
00183 "CREATE INDEX keyfile ON filelist (pkgKey);",
00184 "CREATE INDEX pkgId ON packages (pkgId);",
00185 "CREATE TRIGGER remove_filelist AFTER DELETE ON packages\
00186 \n    BEGIN\
00187 \n    DELETE FROM filelist WHERE pkgKey = old.pkgKey;\
00188 \n    END;",
00189 "INSERT into db_info values (9, 'direct_create');",
00190     NULL
00191 };
00192 /*XXX todo: DBVERSION needs to be set */
00193 
00194 /*@unchecked@*/ /*@observer@*/
00195 static const char *other_sql_init[] = {
00196 "PRAGMA synchronous = \"OFF\";",
00197 "pragma locking_mode = \"EXCLUSIVE\";",
00198 "CREATE TABLE changelog (  pkgKey INTEGER,  author TEXT,  date INTEGER,  changelog TEXT);",
00199 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);",
00200 "CREATE TABLE packages (  pkgKey INTEGER PRIMARY KEY,  pkgId TEXT);",
00201 "CREATE INDEX keychange ON changelog (pkgKey);",
00202 "CREATE INDEX pkgId ON packages (pkgId);",
00203 "CREATE TRIGGER remove_changelogs AFTER DELETE ON packages\
00204 \n    BEGIN\
00205 \n    DELETE FROM changelog WHERE pkgKey = old.pkgKey;\
00206 \n    END;",
00207 "INSERT into db_info values (9, 'direct_create');",
00208     NULL
00209 };
00210 /*XXX todo: DBVERSION needs to be set */
00211 /*@=nullassign@*/
00212 
00213 /* packages   1 pkgKey INTEGER PRIMARY KEY */
00214 /* packages   2 pkgId TEXT */
00215 /* packages   3 name TEXT */
00216 /* packages   4 arch TEXT */
00217 /* packages   5 version TEXT */
00218 /* packages   6 epoch TEXT */
00219 /* packages   7 release TEXT */
00220 /* packages   8 summary TEXT */
00221 /* packages   9 description TEXT */
00222 /* packages  10 url TEXT */
00223 /* packages  11 time_file INTEGER */
00224 /* packages  12 time_build INTEGER */
00225 /* packages  13 rpm_license TEXT */
00226 /* packages  14 rpm_vendor TEXT */
00227 /* packages  15 rpm_group TEXT */
00228 /* packages  16 rpm_buildhost TEXT */
00229 /* packages  17 rpm_sourcerpm TEXT */
00230 /* packages  18 rpm_header_start INTEGER */
00231 /* packages  19 rpm_header_end INTEGER */
00232 /* packages  20 rpm_packager TEXT */
00233 /* packages  21 size_package INTEGER */
00234 /* packages  22 size_installed INTEGER */
00235 /* packages  23 size_archive INTEGER */
00236 /* packages  24 location_href TEXT */
00237 /* packages  25 location_base TEXT */
00238 /* packages  26 checksum_type TEXT */
00239 /* obsoletes  1 pkgKey INTEGER */
00240 /* obsoletes  2 name TEXT */
00241 /* obsoletes  3 flags TEXT */
00242 /* obsoletes  4 epoch TEXT */
00243 /* obsoletes  5 version TEXT */
00244 /* obsoletes  6 release TEXT */
00245 /* provides   1 pkgKey INTEGER */
00246 /* provides   2 name TEXT */
00247 /* provides   3 flags TEXT */
00248 /* provides   4 epoch TEXT */
00249 /* provides   5 version TEXT */
00250 /* provides   6 release TEXT */
00251 /* conflicts  1 pkgKey INTEGER */
00252 /* conflicts  2 name TEXT */
00253 /* conflicts  3 flags TEXT */
00254 /* conflicts  4 epoch TEXT */
00255 /* conflicts  5 version TEXT */
00256 /* conflicts  6 release TEXT */
00257 /* requires   1 pkgKey INTEGER */
00258 /* requires   2 name TEXT */
00259 /* requires   3 flags TEXT */
00260 /* requires   4 epoch TEXT */
00261 /* requires   5 version TEXT */
00262 /* requires   6 release TEXT */
00263 /* files      1 pkgKey INTEGER */
00264 /* files      2 name TEXT */
00265 /* files      3 type TEXT */
00266 
00267 /*@unchecked@*/ /*@observer@*/
00268 static const char primary_sql_qfmt[] =
00269 #include "yum_primary_sqlite"
00270 ;
00271 
00272 /* packages  1 pkgKey INTEGER PRIMARY KEY */
00273 /* packages  2 pkgId TEXT */
00274 /* filelist  1 pkgKey INTEGER */
00275 /* filelist  2 name TEXT */
00276 /* filelist  3 type TEXT */
00277 
00278 /*@unchecked@*/ /*@observer@*/
00279 static const char filelists_sql_qfmt[] =
00280 #include "yum_filelists_sqlite"
00281 ;
00282 
00283 /* packages  1 pkgKey INTEGER PRIMARY KEY */
00284 /* packages  2 pkgId TEXT */
00285 /* changelog 1 pkgKey INTEGER */
00286 /* changelog 2 author TEXT */
00287 /* changelog 3 date INTEGER */
00288 /* changelog 4 changelog TEXT */
00289 
00290 /*@unchecked@*/ /*@observer@*/
00291 static const char other_sql_qfmt[] =
00292 #include "yum_other_sqlite"
00293 ;
00294 
00295 /* XXX static when ready. */
00296 /*@-fullinitblock@*/
00297 /*@unchecked@*/
00298 static struct rpmrepo_s __repo = {
00299     .flags      = REPO_FLAGS_PRETTY,
00300 
00301     .tempdir    = ".repodata",
00302     .finaldir   = "repodata",
00303     .olddir     = ".olddata",
00304     .markup     = ".xml",
00305     .pkgalgo    = PGPHASHALGO_SHA1,
00306     .algo       = PGPHASHALGO_SHA1,
00307     .primary    = {
00308         .type   = "primary",
00309         .xml_init= primary_xml_init,
00310         .xml_qfmt= primary_xml_qfmt,
00311         .xml_fini= primary_xml_fini,
00312         .sql_init= primary_sql_init,
00313         .sql_qfmt= primary_sql_qfmt,
00314 #ifdef  NOTYET  /* XXX char **?!? */
00315         .sql_fini= NULL,
00316 #endif
00317         .yaml_init= NULL,
00318         .yaml_qfmt= primary_yaml_qfmt,
00319         .yaml_fini= NULL,
00320         .Packages_init= NULL,
00321         .Packages_qfmt= NULL,
00322         .Packages_fini= NULL,
00323         .Sources_init= NULL,
00324         .Sources_qfmt= NULL,
00325         .Sources_fini= NULL
00326     },
00327     .filelists  = {
00328         .type   = "filelists",
00329         .xml_init= filelists_xml_init,
00330         .xml_qfmt= filelists_xml_qfmt,
00331         .xml_fini= filelists_xml_fini,
00332         .sql_init= filelists_sql_init,
00333         .sql_qfmt= filelists_sql_qfmt,
00334 #ifdef  NOTYET  /* XXX char **?!? */
00335         .sql_fini= NULL,
00336 #endif
00337         .yaml_init= NULL,
00338         .yaml_qfmt= filelists_yaml_qfmt,
00339         .yaml_fini= NULL,
00340         .Packages_init= NULL,
00341         .Packages_qfmt= NULL,
00342         .Packages_fini= NULL,
00343         .Sources_init= NULL,
00344         .Sources_qfmt= NULL,
00345         .Sources_fini= NULL
00346     },
00347     .other      = {
00348         .type   = "other",
00349         .xml_init= other_xml_init,
00350         .xml_qfmt= other_xml_qfmt,
00351         .xml_fini= other_xml_fini,
00352         .sql_init= other_sql_init,
00353         .sql_qfmt= other_sql_qfmt,
00354 #ifdef  NOTYET  /* XXX char **?!? */
00355         .sql_fini= NULL,
00356 #endif
00357         .yaml_init= NULL,
00358         .yaml_qfmt= other_yaml_qfmt,
00359         .yaml_fini= NULL,
00360         .Packages_init= NULL,
00361         .Packages_qfmt= NULL,
00362         .Packages_fini= NULL,
00363         .Sources_init= NULL,
00364         .Sources_qfmt= NULL,
00365         .Sources_fini= NULL
00366     },
00367     .repomd     = {
00368         .type   = "repomd",
00369         .xml_init= repomd_xml_init,
00370         .xml_qfmt= NULL,
00371         .xml_fini= repomd_xml_fini,
00372         .sql_init= NULL,
00373         .sql_qfmt= NULL,
00374 #ifdef  NOTYET  /* XXX char **?!? */
00375         .sql_fini= NULL,
00376 #endif
00377         .yaml_init= NULL,
00378         .yaml_qfmt= NULL,
00379         .yaml_fini= NULL,
00380         .Packages_init= NULL,
00381         .Packages_qfmt= Packages_qfmt,
00382         .Packages_fini= NULL,
00383         .Sources_init= NULL,
00384         .Sources_qfmt= Sources_qfmt,
00385         .Sources_fini= NULL
00386     }
00387 };
00388 /*@=fullinitblock@*/
00389 
00390 /*@unchecked@*/
00391 static rpmrepo _repo = &__repo;
00392 
00393 /*==============================================================*/
00394 
00400 static int rpmioExists(const char * fn, /*@out@*/ struct stat * st)
00401         /*@globals h_errno, fileSystem, internalState @*/
00402         /*@modifies st, fileSystem, internalState @*/
00403 {
00404     return (Stat(fn, st) == 0);
00405 }
00406 
00412 static time_t rpmioCtime(const char * fn)
00413         /*@globals h_errno, fileSystem, internalState @*/
00414         /*@modifies fileSystem, internalState @*/
00415 {
00416     struct stat sb;
00417     time_t stctime = 0;
00418 
00419     if (rpmioExists(fn, &sb))
00420         stctime = sb.st_ctime;
00421     return stctime;
00422 }
00423 
00424 /*==============================================================*/
00425 
00426 void
00427 rpmrepoError(int lvl, const char *fmt, ...)
00428 {
00429     va_list ap;
00430 
00431     va_start(ap, fmt);
00432     (void) fflush(NULL);
00433     (void) fprintf(stderr, "%s: ", __progname);
00434     (void) vfprintf(stderr, fmt, ap);
00435     va_end (ap);
00436     (void) fprintf(stderr, "\n");
00437     if (lvl)
00438         exit(EXIT_FAILURE);
00439 }
00440 
00448 static const char * rpmrepoGetPath(rpmrepo repo, const char * dir,
00449                 const char * type, int compress)
00450         /*@globals h_errno, rpmGlobalMacroContext, internalState @*/
00451         /*@modifies rpmGlobalMacroContext, internalState @*/
00452 {
00453     return rpmGetPath(repo->outputdir, "/", dir, "/", type,
00454                 (repo->markup != NULL ? repo->markup : ""),
00455                 (repo->suffix != NULL && compress ? repo->suffix : ""), NULL);
00456 }
00457 
00465 static void rpmrepoProgress(/*@unused@*/ rpmrepo repo,
00466                 /*@null@*/ const char * item, int current, int total)
00467         /*@globals fileSystem, internalState @*/
00468         /*@modifies fileSystem, internalState @*/
00469 {
00470     static size_t ncols = 80 - 1;       /* XXX TIOCGWINSIZ */
00471     const char * bn = (item != NULL ? strrchr(item, '/') : NULL);
00472     size_t nb;
00473 
00474     if (bn != NULL)
00475         bn++;
00476     else
00477         bn = item;
00478     nb = fprintf(stdout, "\r%s: %d/%d", __progname, current, total);
00479     if (bn != NULL)
00480         nb += fprintf(stdout, " - %s", bn);
00481     nb--;
00482     if (nb < ncols)
00483         fprintf(stdout, "%*s", (int)(ncols - nb), "");
00484     ncols = nb;
00485     (void) fflush(stdout);
00486 }
00487 
00494 static int rpmrepoMkdir(rpmrepo repo, const char * dn)
00495         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00496         /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
00497 {
00498     const char * dnurl = rpmGetPath(repo->outputdir, "/", dn, NULL);
00499 /*@-mods@*/
00500     int ut = urlPath(dnurl, &dn);
00501 /*@=mods@*/
00502     int rc = 0;;
00503 
00504     /* XXX todo: rpmioMkpath doesn't grok URI's */
00505     if (ut == URL_IS_UNKNOWN)
00506         rc = rpmioMkpath(dn, 0755, (uid_t)-1, (gid_t)-1);
00507     else
00508         rc = (Mkdir(dnurl, 0755) == 0 || errno == EEXIST ? 0 : -1);
00509     if (rc)
00510         rpmrepoError(0, _("Cannot create/verify %s: %s"), dnurl, strerror(errno));
00511     dnurl = _free(dnurl);
00512     return rc;
00513 }
00514 
00515 const char * rpmrepoRealpath(const char * lpath)
00516 {
00517     /* XXX GLIBC: realpath(path, NULL) return malloc'd */
00518     const char *rpath = Realpath(lpath, NULL);
00519     if (rpath == NULL) {
00520         char fullpath[MAXPATHLEN];
00521         rpath = Realpath(lpath, fullpath);
00522         if (rpath != NULL)
00523             rpath = xstrdup(rpath);
00524     }
00525     return rpath;
00526 }
00527 
00528 /*==============================================================*/
00529 
00530 int rpmrepoTestSetupDirs(rpmrepo repo)
00531 {
00532     const char ** directories = repo->directories;
00533     struct stat sb, *st = &sb;
00534     const char * dn;
00535     const char * fn;
00536     int rc = 0;
00537 
00538     /* XXX todo: check repo->pkglist existence? */
00539 
00540     if (directories != NULL)
00541     while ((dn = *directories++) != NULL) {
00542         if (!rpmioExists(dn, st) || !S_ISDIR(st->st_mode)) {
00543             rpmrepoError(0, _("Directory %s must exist"), dn);
00544             rc = 1;
00545         }
00546     }
00547 
00548     /* XXX todo create outputdir if it doesn't exist? */
00549     if (!rpmioExists(repo->outputdir, st)) {
00550         rpmrepoError(0, _("Directory %s does not exist."), repo->outputdir);
00551         rc = 1;
00552     }
00553     if (Access(repo->outputdir, W_OK)) {
00554         rpmrepoError(0, _("Directory %s must be writable."), repo->outputdir);
00555         rc = 1;
00556     }
00557 
00558     if (rpmrepoMkdir(repo, repo->tempdir)
00559      || rpmrepoMkdir(repo, repo->finaldir))
00560         rc = 1;
00561 
00562     dn = rpmGetPath(repo->outputdir, "/", repo->olddir, NULL);
00563     if (rpmioExists(dn, st)) {
00564         rpmrepoError(0, _("Old data directory exists, please remove: %s"), dn);
00565         rc = 1;
00566     }
00567     dn = _free(dn);
00568 
00569   { /*@observer@*/
00570     static const char * dirs[] = { ".repodata", "repodata", NULL };
00571     /*@observer@*/
00572     static const char * types[] =
00573         { "primary", "filelists", "other", "repomd", NULL };
00574     const char ** dirp, ** typep;
00575     for (dirp = dirs; *dirp != NULL; dirp++) {
00576         for (typep = types; *typep != NULL; typep++) {
00577             fn = rpmrepoGetPath(repo, *dirp, *typep, strcmp(*typep, "repomd"));
00578             if (rpmioExists(fn, st)) {
00579                 if (Access(fn, W_OK)) {
00580                     rpmrepoError(0, _("Path must be writable: %s"), fn);
00581                     rc = 1;
00582                 } else
00583                 if (REPO_ISSET(CHECKTS) && st->st_ctime > repo->mdtimestamp)
00584                     repo->mdtimestamp = st->st_ctime;
00585             }
00586             fn = _free(fn);
00587         }
00588     }
00589   }
00590 
00591 #ifdef  NOTYET          /* XXX repo->package_dir needs to go away. */
00592     if (repo->groupfile != NULL) {
00593         if (REPO_ISSET(SPLIT) || repo->groupfile[0] != '/') {
00594             fn = rpmGetPath(repo->package_dir, "/", repo->groupfile, NULL);
00595             repo->groupfile = _free(repo->groupfile);
00596             repo->groupfile = fn;
00597             fn = NULL;
00598         }
00599         if (!rpmioExists(repo->groupfile, st)) {
00600             rpmrepoError(0, _("groupfile %s cannot be found."), repo->groupfile);
00601             rc = 1;
00602         }
00603     }
00604 #endif
00605     return rc;
00606 }
00607 
00614 static int chkSuffix(const char * fn, const char * suffix)
00615         /*@*/
00616 {
00617     size_t flen = strlen(fn);
00618     size_t slen = strlen(suffix);
00619     return (flen > slen && !strcmp(fn + flen - slen, suffix));
00620 }
00621 
00622 const char ** rpmrepoGetFileList(rpmrepo repo, const char *roots[],
00623                 const char * ext)
00624 {
00625     const char ** pkglist = NULL;
00626     FTS * t;
00627     FTSENT * p;
00628     int xx;
00629 
00630     if ((t = Fts_open((char *const *)roots, repo->ftsoptions, NULL)) == NULL)
00631         rpmrepoError(1, _("Fts_open: %s"), strerror(errno));
00632 
00633     while ((p = Fts_read(t)) != NULL) {
00634 #ifdef  NOTYET
00635         const char * fts_name = p->fts_name;
00636         size_t fts_namelen = p->fts_namelen;
00637 
00638         /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
00639         if (p->fts_level == 0 && fts_namelen == 0) {
00640             fts_name = ".";
00641             fts_namelen = sizeof(".") - 1;
00642         }
00643 #endif
00644 
00645         /* Should this element be excluded/included? */
00646         /* XXX todo: apply globs to fts_path rather than fts_name? */
00647 /*@-onlytrans@*/
00648         if (mireApply(repo->excludeMire, repo->nexcludes, p->fts_name, 0, -1) >= 0)
00649             continue;
00650         if (mireApply(repo->includeMire, repo->nincludes, p->fts_name, 0, +1) < 0)
00651             continue;
00652 /*@=onlytrans@*/
00653 
00654         switch (p->fts_info) {
00655         case FTS_D:
00656         case FTS_DP:
00657         default:
00658             continue;
00659             /*@notreached@*/ /*@switchbreak@*/ break;
00660         case FTS_SL:
00661             if (REPO_ISSET(NOFOLLOW))
00662                 continue;
00663             /* XXX todo: fuss with symlinks */
00664             /*@notreached@*/ /*@switchbreak@*/ break;
00665         case FTS_F:
00666             /* Is this a *.rpm file? */
00667             if (chkSuffix(p->fts_name, ext))
00668                 xx = argvAdd(&pkglist, p->fts_path);
00669             /*@switchbreak@*/ break;
00670         }
00671     }
00672 
00673     (void) Fts_close(t);
00674 
00675 if (_rpmrepo_debug)
00676 argvPrint("pkglist", pkglist, NULL);
00677 
00678     return pkglist;
00679 }
00680 
00681 int rpmrepoCheckTimeStamps(rpmrepo repo)
00682 {
00683     int rc = 0;
00684 
00685     if (REPO_ISSET(CHECKTS)) {
00686         const char ** pkg;
00687 
00688         if (repo->pkglist != NULL)
00689         for (pkg = repo->pkglist; *pkg != NULL ; pkg++) {
00690             struct stat sb, *st = &sb;
00691             if (!rpmioExists(*pkg, st)) {
00692                 rpmrepoError(0, _("cannot get to file: %s"), *pkg);
00693                 rc = 1;
00694             } else if (st->st_ctime > repo->mdtimestamp)
00695                 rc = 1;
00696         }
00697     } else
00698         rc = 1;
00699 
00700     return rc;
00701 }
00702 
00709 static int rpmrfileXMLWrite(rpmrfile rfile, const char * spew)
00710         /*@globals fileSystem @*/
00711         /*@modifies rfile, fileSystem @*/
00712 {
00713     size_t nspew = (spew != NULL ? strlen(spew) : 0);
00714 /*@-nullpass@*/ /* XXX spew != NULL @*/
00715     size_t nb = (nspew > 0 ? Fwrite(spew, 1, nspew, rfile->fd) : 0);
00716 /*@=nullpass@*/
00717     int rc = 0;
00718     if (nspew != nb) {
00719         rpmrepoError(0, _("Fwrite failed: expected write %u != %u bytes: %s\n"),
00720                 (unsigned)nspew, (unsigned)nb, Fstrerror(rfile->fd));
00721         rc = 1;
00722     }
00723     spew = _free(spew);
00724     return rc;
00725 }
00726 
00733 static int rpmrepoFclose(rpmrepo repo, FD_t fd)
00734         /*@modifies repo, fd @*/
00735 {
00736     int rc = 0;
00737 
00738     if (fd != NULL) {
00739 #ifdef  NOTYET
00740         rpmts ts = repo->_ts;
00741         if (ts != NULL) {
00742             (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS),
00743                         fdstat_op(fd, FDSTAT_READ));
00744             (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST),
00745                         fdstat_op(fd, FDSTAT_DIGEST));
00746         }
00747 #endif
00748         rc = Fclose(fd);
00749     }
00750     return rc;
00751 }
00752 
00759 static int rpmrepoOpenMDFile(const rpmrepo repo, rpmrfile rfile)
00760         /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
00761         /*@modifies rfile, rpmGlobalMacroContext, fileSystem, internalState @*/
00762 {
00763     const char * spew = rfile->xml_init;
00764     size_t nspew = strlen(spew);
00765     const char * fn = rpmrepoGetPath(repo, repo->tempdir, rfile->type, 1);
00766     const char * tail;
00767     size_t nb;
00768     int rc = 0;
00769 
00770     rfile->fd = Fopen(fn, repo->wmode);
00771 assert(rfile->fd != NULL);
00772 
00773     if (repo->algo != PGPHASHALGO_NONE)
00774         fdInitDigest(rfile->fd, repo->algo, 0);
00775 
00776     if ((tail = strstr(spew, " packages=\"0\">\n")) != NULL)
00777         nspew -= strlen(tail);
00778 
00779     nb = Fwrite(spew, 1, nspew, rfile->fd);
00780 
00781     if (tail != NULL) {
00782         char buf[64];
00783         size_t tnb = snprintf(buf, sizeof(buf), " packages=\"%d\">\n",
00784                                 repo->pkgcount);
00785         nspew += tnb;
00786         nb += Fwrite(buf, 1, tnb, rfile->fd);
00787     }
00788     if (nspew != nb) {
00789         rpmrepoError(0, _("Fwrite failed: expected write %u != %u bytes: %s\n"),
00790                 (unsigned)nspew, (unsigned)nb, Fstrerror(rfile->fd));
00791         rc = 1;
00792     }
00793 
00794     fn = _free(fn);
00795 
00796 #if defined(WITH_SQLITE)
00797     if (REPO_ISSET(DATABASE)) {
00798         const char ** stmt;
00799         int xx;
00800         fn = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
00801                 rfile->type, ".sqlite", NULL);
00802         if ((xx = sqlite3_open(fn, &rfile->sqldb)) != SQLITE_OK)
00803             rpmrepoError(1, "sqlite3_open(%s): %s", fn, sqlite3_errmsg(rfile->sqldb));
00804         for (stmt = rfile->sql_init; *stmt != NULL; stmt++) {
00805             char * msg;
00806             xx = sqlite3_exec(rfile->sqldb, *stmt, NULL, NULL, &msg);
00807             if (xx != SQLITE_OK)
00808                 rpmrepoError(1, "sqlite3_exec(%s, \"%s\"): %s\n", fn, *stmt,
00809                         (msg != NULL ? msg : "failed"));
00810         }
00811         fn = _free(fn);
00812     }
00813 #endif
00814 
00815     return rc;
00816 }
00817 
00818 #if defined(WITH_SQLITE)
00819 
00824 static int rpmrfileSQL(rpmrfile rfile, const char * msg, int rc)
00825         /*@globals fileSystem @*/
00826         /*@modifies fileSystem @*/
00827 {
00828     if (rc != SQLITE_OK || _rpmrepo_debug)
00829         rpmrepoError(0, "sqlite3_%s(%s): %s", msg, rfile->type,
00830                 sqlite3_errmsg(rfile->sqldb));
00831     return rc;
00832 }
00833 
00839 static int rpmrfileSQLStep(rpmrfile rfile, sqlite3_stmt * stmt)
00840         /*@globals fileSystem @*/
00841         /*@modifies fileSystem @*/
00842 {
00843     int loop = 1;
00844     int rc = 0;
00845     int xx;
00846 
00847 /*@-infloops@*/
00848     while (loop) {
00849         rc = sqlite3_step(stmt);
00850         switch (rc) {
00851         default:
00852             rc = rpmrfileSQL(rfile, "step", rc);
00853             /*@fallthrough@*/
00854         case SQLITE_DONE:
00855             loop = 0;
00856             /*@switchbreak@*/ break;
00857         }
00858     }
00859 /*@=infloops@*/
00860 
00861     xx = rpmrfileSQL(rfile, "reset",
00862         sqlite3_reset(stmt));
00863 
00864     return rc;
00865 }
00866 
00873 static int rpmrfileSQLWrite(rpmrfile rfile, const char * cmd)
00874         /*@globals fileSystem @*/
00875         /*@modifies fileSystem @*/
00876 {
00877     sqlite3_stmt * stmt;
00878     const char * tail;
00879     int xx;
00880 
00881     xx = rpmrfileSQL(rfile, "prepare",
00882         sqlite3_prepare(rfile->sqldb, cmd, (int)strlen(cmd), &stmt, &tail));
00883 
00884     xx = rpmrfileSQL(rfile, "reset",
00885         sqlite3_reset(stmt));
00886 
00887     xx = rpmrfileSQLStep(rfile, stmt);
00888 
00889     xx = rpmrfileSQL(rfile, "finalize",
00890         sqlite3_finalize(stmt));
00891 
00892     cmd = _free(cmd);
00893 
00894     return 0;
00895 }
00896 #endif  /* WITH_SQLITE */
00897 
00902 static int rpmrepoRfileDigest(const rpmrepo repo, rpmrfile rfile,
00903                 const char ** digestp)
00904         /*@modifies *digestp @*/
00905 {
00906     static int asAscii = 1;
00907     struct stat sb, *st = &sb;
00908     const char * fn = rpmrepoGetPath(repo, repo->tempdir, rfile->type, 1);
00909     const char * path = NULL;
00910     int ut = urlPath(fn, &path);
00911     FD_t fd = NULL;
00912     int rc = 1;
00913     int xx;
00914 
00915     memset(st, 0, sizeof(*st));
00916     if (!rpmioExists(fn, st))
00917         goto exit;
00918     fd = Fopen(fn, "r.ufdio");
00919     if (fd == NULL || Ferror(fd))
00920         goto exit;
00921 
00922     switch (ut) {
00923     case URL_IS_PATH:
00924     case URL_IS_UNKNOWN:
00925 #if defined(HAVE_MMAP)
00926     {   void * mapped = (void *)-1;
00927 
00928         if (st->st_size > 0)
00929             mapped = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, Fileno(fd), 0);
00930         if (mapped != (void *)-1) {
00931 #ifdef  NOTYET
00932             rpmts ts = repo->_ts;
00933             rpmop op = rpmtsOp(ts, RPMTS_OP_DIGEST);
00934             rpmtime_t tstamp = rpmswEnter(op, 0);
00935 #endif
00936             DIGEST_CTX ctx = rpmDigestInit(repo->algo, RPMDIGEST_NONE);
00937             xx = rpmDigestUpdate(ctx, mapped, st->st_size);
00938             xx = rpmDigestFinal(ctx, digestp, NULL, asAscii);
00939 #ifdef  NOTYET
00940             tstamp = rpmswExit(op, st->st_size);
00941 #endif
00942             xx = munmap(mapped, st->st_size);
00943             break;
00944         }
00945     }   /*@fallthrough@*/
00946 #endif
00947     default:
00948     {   char buf[64 * BUFSIZ];
00949         size_t nb;
00950         size_t fsize = 0;
00951 
00952         fdInitDigest(fd, repo->algo, 0);
00953         while ((nb = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
00954             fsize += nb;
00955         if (Ferror(fd))
00956             goto exit;
00957         fdFiniDigest(fd, repo->algo, digestp, NULL, asAscii);
00958     }   break;
00959     }
00960 
00961     rc = 0;
00962 
00963 exit:
00964     if (fd)
00965         xx = rpmrepoFclose(repo, fd);
00966     fn = _free(fn);
00967     return rc;
00968 }
00969 
00976 static int rpmrepoCloseMDFile(const rpmrepo repo, rpmrfile rfile)
00977         /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
00978         /*@modifies rfile, rpmGlobalMacroContext, fileSystem, internalState @*/
00979 {
00980     static int asAscii = 1;
00981     char * xmlfn = xstrdup(fdGetOPath(rfile->fd));
00982     int rc = 0;
00983 
00984     if (!repo->quiet)
00985         rpmrepoError(0, _("Saving %s metadata"), basename(xmlfn));
00986 
00987     if (rpmrfileXMLWrite(rfile, xstrdup(rfile->xml_fini)))
00988         rc = 1;
00989 
00990     if (repo->algo > 0)
00991         fdFiniDigest(rfile->fd, repo->algo, &rfile->digest, NULL, asAscii);
00992     else
00993         rfile->digest = xstrdup("");
00994 
00995     (void) rpmrepoFclose(repo, rfile->fd);
00996     rfile->fd = NULL;
00997 
00998     /* Compute the (usually compressed) ouput file digest too. */
00999     rfile->Zdigest = NULL;
01000     (void) rpmrepoRfileDigest(repo, rfile, &rfile->Zdigest);
01001 
01002 #if defined(WITH_SQLITE)
01003     if (REPO_ISSET(DATABASE) && rfile->sqldb != NULL) {
01004         const char *dbfn = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
01005                 rfile->type, ".sqlite", NULL);
01006         int xx;
01007         if ((xx = sqlite3_close(rfile->sqldb)) != SQLITE_OK)
01008             rpmrepoError(1, "sqlite3_close(%s): %s", dbfn, sqlite3_errmsg(rfile->sqldb));
01009         rfile->sqldb = NULL;
01010         dbfn = _free(dbfn);
01011     }
01012 #endif
01013 
01014     rfile->ctime = rpmioCtime(xmlfn);
01015     xmlfn = _free(xmlfn);
01016 
01017     return rc;
01018 }
01019 
01022 static /*@observer@*/ /*@null@*/ const char *
01023 algo2tagname(uint32_t algo)
01024         /*@*/
01025 {
01026     const char * tagname = NULL;
01027 
01028     switch (algo) {
01029     case PGPHASHALGO_NONE:      tagname = "none";       break;
01030     case PGPHASHALGO_MD5:       tagname = "md5";        break;
01031     /* XXX todo: should be "sha1" */
01032     case PGPHASHALGO_SHA1:      tagname = "sha";        break;
01033     case PGPHASHALGO_RIPEMD160: tagname = "rmd160";     break;
01034     case PGPHASHALGO_MD2:       tagname = "md2";        break;
01035     case PGPHASHALGO_TIGER192:  tagname = "tiger192";   break;
01036     case PGPHASHALGO_HAVAL_5_160: tagname = "haval160"; break;
01037     case PGPHASHALGO_SHA256:    tagname = "sha256";     break;
01038     case PGPHASHALGO_SHA384:    tagname = "sha384";     break;
01039     case PGPHASHALGO_SHA512:    tagname = "sha512";     break;
01040     case PGPHASHALGO_MD4:       tagname = "md4";        break;
01041     case PGPHASHALGO_RIPEMD128: tagname = "rmd128";     break;
01042     case PGPHASHALGO_CRC32:     tagname = "crc32";      break;
01043     case PGPHASHALGO_ADLER32:   tagname = "adler32";    break;
01044     case PGPHASHALGO_CRC64:     tagname = "crc64";      break;
01045     case PGPHASHALGO_JLU32:     tagname = "jlu32";      break;
01046     case PGPHASHALGO_SHA224:    tagname = "sha224";     break;
01047     case PGPHASHALGO_RIPEMD256: tagname = "rmd256";     break;
01048     case PGPHASHALGO_RIPEMD320: tagname = "rmd320";     break;
01049     case PGPHASHALGO_SALSA10:   tagname = "salsa10";    break;
01050     case PGPHASHALGO_SALSA20:   tagname = "salsa20";    break;
01051     default:                    tagname = NULL;         break;
01052     }
01053     return tagname;
01054 }
01055 
01061 static const char * rpmrepoMDExpand(rpmrepo repo, rpmrfile rfile)
01062         /*@globals h_errno, rpmGlobalMacroContext, internalState @*/
01063         /*@modifies rpmGlobalMacroContext, internalState @*/
01064 {
01065     const char * spewalgo = algo2tagname(repo->algo);
01066     char spewtime[64];
01067 
01068     (void) snprintf(spewtime, sizeof(spewtime), "%u", (unsigned)rfile->ctime);
01069     return rpmExpand("\
01070   <data type=\"", rfile->type, "\">\n\
01071     <checksum type=\"", spewalgo, "\">", rfile->Zdigest, "</checksum>\n\
01072     <timestamp>", spewtime, "</timestamp>\n\
01073     <open-checksum type=\"",spewalgo,"\">", rfile->digest, "</open-checksum>\n\
01074     <location href=\"", repo->finaldir, "/", rfile->type, (repo->markup != NULL ? repo->markup : ""), (repo->suffix != NULL ? repo->suffix : ""), "\"/>\n\
01075   </data>\n", NULL);
01076 }
01077 
01078 int rpmrepoDoRepoMetadata(rpmrepo repo)
01079 {
01080     rpmrfile rfile = &repo->repomd;
01081     const char * fn = rpmrepoGetPath(repo, repo->tempdir, rfile->type, 0);
01082     int rc = 0;
01083 
01084     if ((rfile->fd = Fopen(fn, "w.ufdio")) != NULL) {   /* no compression */
01085         if (rpmrfileXMLWrite(rfile, xstrdup(rfile->xml_init))
01086          || rpmrfileXMLWrite(rfile, rpmrepoMDExpand(repo, &repo->other))
01087          || rpmrfileXMLWrite(rfile, rpmrepoMDExpand(repo, &repo->filelists))
01088          || rpmrfileXMLWrite(rfile, rpmrepoMDExpand(repo, &repo->primary))
01089          || rpmrfileXMLWrite(rfile, xstrdup(rfile->xml_fini)))
01090             rc = 1;
01091         (void) rpmrepoFclose(repo, rfile->fd);
01092         rfile->fd = NULL;
01093     }
01094 
01095     fn = _free(fn);
01096     if (rc) return rc;
01097 
01098 #ifdef  NOTYET
01099     def doRepoMetadata(self):
01100         """wrapper to generate the repomd.xml file that stores the info on the other files"""
01101     const char * repopath =
01102                 rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL);
01103         repodoc = libxml2.newDoc("1.0")
01104         reporoot = repodoc.newChild(None, "repomd", None)
01105         repons = reporoot.newNs("http://linux.duke.edu/metadata/repo", None)
01106         reporoot.setNs(repons)
01107         repopath = rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL);
01108         fn = rpmrepoGetPath(repo, repo->tempdir, repo->repomd.type, 1);
01109 
01110         repoid = "garbageid";
01111 
01112         if (REPO_ISSET(DATABASE)) {
01113             if (!repo->quiet) rpmrepoError(0, _("Generating sqlite DBs"));
01114             try:
01115                 dbversion = str(sqlitecachec.DBVERSION)
01116             except AttributeError:
01117                 dbversion = "9"
01118             rp = sqlitecachec.RepodataParserSqlite(repopath, repoid, None)
01119         }
01120 
01121   { static const char * types[] =
01122         { "primary", "filelists", "other", NULL };
01123     const char ** typep;
01124         for (typep = types; *typep != NULL; typep++) {
01125             complete_path = rpmrepoGetPath(repo, repo->tempdir, *typep, 1);
01126 
01127             zfo = _gzipOpen(complete_path)
01128             uncsum = misc.checksum(algo2tagname(repo->algo), zfo)
01129             zfo.close()
01130             csum = misc.checksum(algo2tagname(repo->algo), complete_path)
01131             (void) rpmioExists(complete_path, st)
01132             timestamp = os.stat(complete_path)[8]
01133 
01134             db_csums = {}
01135             db_compressed_sums = {}
01136 
01137             if (REPO_ISSET(repo)) {
01138                 if (repo->verbose) {
01139                     time_t now = time(NULL);
01140                     rpmrepoError(0, _("Starting %s db creation: %s"),
01141                         *typep, ctime(&now));
01142                 }
01143 
01144                 if (!strcmp(*typep, "primary"))
01145                     rp.getPrimary(complete_path, csum)
01146                 else if (!strcmp(*typep, "filelists"));
01147                     rp.getFilelists(complete_path, csum)
01148                 else if (!strcmp(*typep, "other"))
01149                     rp.getOtherdata(complete_path, csum)
01150 
01151                 {   const char *  tmp_result_path =
01152                         rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
01153                                 *typep, ".xml.gz.sqlite", NULL);
01154                     const char * resultpath =
01155                         rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
01156                                 *typep, ".sqlite", NULL);
01157 
01158                     /* rename from silly name to not silly name */
01159                     xx = Rename(tmp_result_path, resultpath);
01160                     tmp_result_path = _free(tmp_result_path);
01161                     result_compressed =
01162                         rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
01163                                 *typep, ".sqlite.bz2", NULL);
01164                     db_csums[*typep] = misc.checksum(algo2tagname(repo->algo), resultpath)
01165 
01166                     /* compress the files */
01167                     bzipFile(resultpath, result_compressed)
01168                     /* csum the compressed file */
01169                     db_compressed_sums[*typep] = misc.checksum(algo2tagname(repo->algo), result_compressed)
01170                     /* remove the uncompressed file */
01171                     xx = Unlink(resultpath);
01172                     resultpath = _free(resultpath);
01173                 }
01174 
01175                 if (REPO_ISSET(UNIQUEMDFN)) {
01176                     const char * csum_result_compressed =
01177                         rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
01178                                 db_compressed_sums[*typep], "-", *typep, ".sqlite.bz2", NULL);
01179                     xx = Rename(result_compressed, csum_result_compressed);
01180                     result_compressed = _free(result_compressed);
01181                     result_compressed = csum_result_compressed;
01182                 }
01183 
01184                 /* timestamp the compressed file */
01185                 (void) rpmioExists(result_compressed, st)
01186                 db_timestamp = os.stat(result_compressed)[8]
01187 
01188                 /* add this data as a section to the repomdxml */
01189                 db_data_type = rpmExpand(*typep, "_db", NULL);
01190                 data = reporoot.newChild(None, 'data', None)
01191                 data.newProp('type', db_data_type)
01192                 location = data.newChild(None, 'location', None)
01193                 if (repo->baseurl != NULL) {
01194                     location.newProp('xml:base', repo->baseurl)
01195                 }
01196 
01197                 location.newProp('href', rpmGetPath(repo->finaldir, "/", *typep, ".sqlite.bz2", NULL));
01198                 checksum = data.newChild(None, 'checksum', db_compressed_sums[*typep])
01199                 checksum.newProp('type', algo2tagname(repo->algo))
01200                 db_tstamp = data.newChild(None, 'timestamp', str(db_timestamp))
01201                 unchecksum = data.newChild(None, 'open-checksum', db_csums[*typep])
01202                 unchecksum.newProp('type', algo2tagname(repo->algo))
01203                 database_version = data.newChild(None, 'database_version', dbversion)
01204                 if (repo->verbose) {
01205                    time_t now = time(NULL);
01206                    rpmrepoError(0, _("Ending %s db creation: %s"),
01207                         *typep, ctime(&now));
01208                 }
01209             }
01210 
01211             data = reporoot.newChild(None, 'data', None)
01212             data.newProp('type', *typep)
01213 
01214             checksum = data.newChild(None, 'checksum', csum)
01215             checksum.newProp('type', algo2tagname(repo->algo))
01216             timestamp = data.newChild(None, 'timestamp', str(timestamp))
01217             unchecksum = data.newChild(None, 'open-checksum', uncsum)
01218             unchecksum.newProp('type', algo2tagname(repo->algo))
01219             location = data.newChild(None, 'location', None)
01220             if (repo->baseurl != NULL)
01221                 location.newProp('xml:base', repo->baseurl)
01222             if (REPO_ISSET(UNIQUEMDFN)) {
01223                 orig_file = rpmrepoGetPath(repo, repo->tempdir, *typep, strcmp(*typep, "repomd"));
01224                 res_file = rpmExpand(csum, "-", *typep,
01225                         (repo->markup ? repo->markup : ""),
01226                         (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""), NULL);
01227                 dest_file = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", res_file, NULL);
01228                 xx = Rename(orig_file, dest_file);
01229 
01230             } else
01231                 res_file = rpmExpand(*typep,
01232                         (repo->markup ? repo->markup : ""),
01233                         (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""), NULL);
01234 
01235             location.newProp('href', rpmGetPath(repo->finaldir, "/", res_file, NULL));
01236         }
01237   }
01238 
01239         if (!repo->quiet && REPO_ISSET(DATABASE))
01240             rpmrepoError(0, _("Sqlite DBs complete"));
01241 
01242         if (repo->groupfile != NULL) {
01243             self.addArbitraryMetadata(repo->groupfile, 'group_gz', reporoot)
01244             self.addArbitraryMetadata(repo->groupfile, 'group', reporoot, compress=False)
01245         }
01246 
01247         /* save it down */
01248         try:
01249             repodoc.saveFormatFileEnc(fn, 'UTF-8', 1)
01250         except:
01251             rpmrepoError(0, _("Error saving temp file for %s%s%s: %s"),
01252                 rfile->type,
01253                 (repo->markup ? repo->markup : ""),
01254                 (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""),
01255                 fn);
01256             rpmrepoError(1, _("Could not save temp file: %s"), fn);
01257 
01258         del repodoc
01259 #endif
01260 
01261     return rc;
01262 }
01263 
01264 int rpmrepoDoFinalMove(rpmrepo repo)
01265 {
01266     char * output_final_dir =
01267                 rpmGetPath(repo->outputdir, "/", repo->finaldir, NULL);
01268     char * output_old_dir =
01269                 rpmGetPath(repo->outputdir, "/", repo->olddir, NULL);
01270     struct stat sb, *st = &sb;
01271     int xx;
01272 
01273     if (rpmioExists(output_final_dir, st)) {
01274         if ((xx = Rename(output_final_dir, output_old_dir)) != 0)
01275             rpmrepoError(1, _("Error moving final %s to old dir %s"),
01276                         output_final_dir, output_old_dir);
01277     }
01278 
01279     {   const char * output_temp_dir =
01280                 rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL);
01281         if ((xx = Rename(output_temp_dir, output_final_dir)) != 0) {
01282             xx = Rename(output_old_dir, output_final_dir);
01283             rpmrepoError(1, _("Error moving final metadata into place"));
01284         }
01285         output_temp_dir = _free(output_temp_dir);
01286     }
01287 
01288     /* Merge old tree into new, unlink/rename as needed. */
01289   { 
01290     char *const _av[] = { output_old_dir, NULL };
01291     int _ftsoptions = FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV;
01292     int (*_compar)(const FTSENT **, const FTSENT **) = NULL;
01293     FTS * t = Fts_open(_av, _ftsoptions, _compar);
01294     FTSENT * p = NULL;
01295 
01296     if (t != NULL)
01297     while ((p = Fts_read(t)) != NULL) {
01298         const char * opath = p->fts_accpath;
01299         const char * ofn = p->fts_path;
01300         const char * obn = p->fts_name;
01301         const char * nfn = NULL;
01302         switch (p->fts_info) {
01303         default:
01304             break;
01305         case FTS_DP:
01306             /* Remove empty directories on post-traversal visit. */
01307             if ((xx = Rmdir(opath)) != 0)
01308                 rpmrepoError(1, _("Could not remove old metadata directory: %s: %s"),
01309                                 ofn, strerror(errno));
01310             break;
01311         case FTS_F:
01312             /* Remove all non-toplevel files. */
01313             if (p->fts_level > 0) {
01314                 if ((xx = Unlink(opath)) != 0)
01315                     rpmrepoError(1, _("Could not remove old metadata file: %s: %s"),
01316                                 ofn, strerror(errno));
01317                 break;
01318             }
01319 
01320             /* Remove/rename toplevel files, dependent on target existence. */
01321             nfn = rpmGetPath(output_final_dir, "/", obn, NULL);
01322             if (rpmioExists(nfn, st)) {
01323                 if ((xx = Unlink(opath)) != 0)
01324                     rpmrepoError(1, _("Could not remove old metadata file: %s: %s"),
01325                                 ofn, strerror(errno));
01326             } else {
01327                 if ((xx = Rename(opath, nfn)) != 0)
01328                     rpmrepoError(1, _("Could not restore old non-metadata file: %s -> %s: %s"),
01329                                 ofn, nfn, strerror(errno));
01330             }
01331             nfn = _free(nfn);
01332             break;
01333         case FTS_SL:
01334         case FTS_SLNONE:
01335             /* Remove all symlinks. */
01336             if ((xx = Unlink(opath)) != 0)
01337                 rpmrepoError(1, _("Could not remove old metadata symlink: %s: %s"),
01338                                 ofn, strerror(errno));
01339             break;
01340         }
01341     }
01342     if (t != NULL)
01343         Fts_close(t);
01344   }
01345 
01346     output_old_dir = _free(output_old_dir);
01347     output_final_dir = _free(output_final_dir);
01348 
01349     return 0;
01350 }
01351 
01352 /*==============================================================*/
01353 
01360 static Header rpmrepoReadHeader(rpmrepo repo, const char * path)
01361         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01362         /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/
01363 {
01364     /* XXX todo: read the payload and collect the blessed file digest. */
01365     FD_t fd = Fopen(path, "r.ufdio");
01366     Header h = NULL;
01367 
01368     if (fd != NULL) {
01369         rpmts ts = repo->_ts;
01370         uint32_t algo = repo->pkgalgo;
01371         rpmRC rpmrc;
01372 
01373         if (algo != PGPHASHALGO_NONE)
01374             fdInitDigest(fd, algo, 0);
01375 
01376         /* XXX what if path needs expansion? */
01377         rpmrc = rpmReadPackageFile(ts, fd, path, &h);
01378         if (algo != PGPHASHALGO_NONE) {
01379             char buffer[32 * BUFSIZ];
01380             size_t nb = sizeof(buffer);
01381             size_t nr;
01382             while ((nr = Fread(buffer, sizeof(buffer[0]), nb, fd)) == nb)
01383                 {};
01384             if (Ferror(fd)) {
01385                 fprintf(stderr, _("%s: Fread(%s) failed: %s\n"),
01386                         __progname, path, Fstrerror(fd));
01387                 rpmrc = RPMRC_FAIL;
01388             } else {
01389                 static int asAscii = 1;
01390                 const char *digest = NULL;
01391                 fdFiniDigest(fd, algo, &digest, NULL, asAscii);
01392                 (void) headerSetDigest(h, digest);
01393                 digest = _free(digest);
01394             }
01395         }
01396 
01397         (void) Fclose(fd);
01398 
01399         switch (rpmrc) {
01400         case RPMRC_NOTFOUND:
01401         case RPMRC_FAIL:
01402         default:
01403             (void) headerFree(h);
01404             h = NULL;
01405             break;
01406         case RPMRC_NOTTRUSTED:
01407         case RPMRC_NOKEY:
01408         case RPMRC_OK:
01409             if (repo->baseurl)
01410                 (void) headerSetBaseURL(h, repo->baseurl);
01411             (void) headerSetInstance(h, (uint32_t)repo->current+1);
01412             break;
01413         }
01414     }
01415     return h;
01416 }
01417 
01424 static const char * rfileHeaderSprintf(Header h, const char * qfmt)
01425         /*@globals fileSystem @*/
01426         /*@modifies h, fileSystem @*/
01427 {
01428     const char * msg = NULL;
01429     const char * s = headerSprintf(h, qfmt, NULL, NULL, &msg);
01430     if (s == NULL)
01431         rpmrepoError(1, _("headerSprintf(%s): %s"), qfmt, msg);
01432 assert(s != NULL);
01433     return s;
01434 }
01435 
01436 #if defined(WITH_SQLITE)
01437 
01443 static const char * rfileHeaderSprintfHack(Header h, const char * qfmt)
01444         /*@globals fileSystem @*/
01445         /*@modifies h, fileSystem @*/
01446 {
01447     static const char mark[] = "'XXX'";
01448     static size_t nmark = sizeof("'XXX'") - 1;
01449     const char * msg = NULL;
01450     char * s = (char *) headerSprintf(h, qfmt, NULL, NULL, &msg);
01451     char * f, * fe;
01452     int nsubs = 0;
01453 
01454     if (s == NULL)
01455         rpmrepoError(1, _("headerSprintf(%s): %s"), qfmt, msg);
01456 assert(s != NULL);
01457 
01458     /* XXX Find & replace 'XXX' with '%{DBINSTANCE}' the hard way. */
01459 /*@-nullptrarith@*/
01460     for (f = s; *f != '\0' && (fe = strstr(f, "'XXX'")) != NULL; fe += nmark, f = fe)
01461         nsubs++;
01462 /*@=nullptrarith@*/
01463 
01464     if (nsubs > 0) {
01465         char instance[64];
01466         int xx = snprintf(instance, sizeof(instance), "'%u'",
01467                 (uint32_t) headerGetInstance(h));
01468         size_t tlen = strlen(s) + nsubs * ((int)strlen(instance) - (int)nmark);
01469         char * t = xmalloc(tlen + 1);
01470         char * te = t;
01471 
01472         xx = xx;
01473 /*@-nullptrarith@*/
01474         for (f = s; *f != '\0' && (fe = strstr(f, mark)) != NULL; fe += nmark, f = fe) {
01475             *fe = '\0';
01476             te = stpcpy( stpcpy(te, f), instance);
01477         }
01478 /*@=nullptrarith@*/
01479         if (*f != '\0')
01480             te = stpcpy(te, f);
01481         s = _free(s);
01482         s = t;
01483     }
01484  
01485     return s;
01486 }
01487 #endif
01488 
01496 static int rpmrepoWriteMDFile(rpmrepo repo, rpmrfile rfile, Header h)
01497         /*@globals fileSystem @*/
01498         /*@modifies rfile, h, fileSystem @*/
01499 {
01500     int rc = 0;
01501 
01502     if (rfile->xml_qfmt != NULL) {
01503         if (rpmrfileXMLWrite(rfile, rfileHeaderSprintf(h, rfile->xml_qfmt)))
01504             rc = 1;
01505     }
01506 
01507 #if defined(WITH_SQLITE)
01508     if (REPO_ISSET(DATABASE)) {
01509         if (rpmrfileSQLWrite(rfile, rfileHeaderSprintfHack(h, rfile->sql_qfmt)))
01510             rc = 1;
01511     }
01512 #endif
01513 
01514     return rc;
01515 }
01516 
01522 static int repoWriteMetadataDocs(rpmrepo repo)
01523         /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
01524         /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/
01525 {
01526     const char ** pkglist = repo->pkglist;
01527     const char * pkg;
01528     int rc = 0;
01529 
01530     if (pkglist)
01531     while ((pkg = *pkglist++) != NULL) {
01532         Header h = rpmrepoReadHeader(repo, pkg);
01533 
01534         repo->current++;
01535 
01536         /* XXX repoReadHeader() displays error. Continuing is foolish */
01537         if (h == NULL) {
01538             rc = 1;
01539             break;
01540         }
01541 
01542 #ifdef  REFERENCE
01543         /* XXX todo: rpmGetPath(mydir, "/", filematrix[mydir], NULL); */
01544         reldir = (pkgpath != NULL ? pkgpath : rpmGetPath(repo->basedir, "/", repo->directories[0], NULL));
01545         self.primaryfile.write(po.do_primary_xml_dump(reldir, baseurl=repo->baseurl))
01546         self.flfile.write(po.do_filelists_xml_dump())
01547         self.otherfile.write(po.do_other_xml_dump())
01548 #endif
01549 
01550         if (rpmrepoWriteMDFile(repo, &repo->primary, h)
01551          || rpmrepoWriteMDFile(repo, &repo->filelists, h)
01552          || rpmrepoWriteMDFile(repo, &repo->other, h))
01553             rc = 1;
01554 
01555         (void) headerFree(h);
01556         h = NULL;
01557         if (rc) break;
01558 
01559         if (!repo->quiet) {
01560             if (repo->verbose)
01561                 rpmrepoError(0, "%d/%d - %s", repo->current, repo->pkgcount, pkg);
01562             else
01563                 rpmrepoProgress(repo, pkg, repo->current, repo->pkgcount);
01564         }
01565     }
01566     return rc;
01567 }
01568 
01569 int rpmrepoDoPkgMetadata(rpmrepo repo)
01570 {
01571     int rc = 0;
01572 
01573     repo->current = 0;
01574 
01575 #ifdef  REFERENCE
01576     def _getFragmentUrl(self, url, fragment):
01577         import urlparse
01578         urlparse.uses_fragment.append('media')
01579         if not url:
01580             return url
01581         (scheme, netloc, path, query, fragid) = urlparse.urlsplit(url)
01582         return urlparse.urlunsplit((scheme, netloc, path, query, str(fragment)))
01583 
01584     def doPkgMetadata(self):
01585         """all the heavy lifting for the package metadata"""
01586         if (argvCount(repo->directories) == 1) {
01587             MetaDataGenerator.doPkgMetadata(self)
01588             return
01589         }
01590 
01591     ARGV_t roots = NULL;
01592     filematrix = {}
01593     for mydir in repo->directories {
01594         if (mydir[0] == '/')
01595             thisdir = xstrdup(mydir);
01596         else if (mydir[0] == '.' && mydir[1] == '.' && mydir[2] == '/')
01597             thisdir = Realpath(mydir, NULL);
01598         else
01599             thisdir = rpmGetPath(repo->basedir, "/", mydir, NULL);
01600 
01601         xx = argvAdd(&roots, thisdir);
01602         thisdir = _free(thisdir);
01603 
01604         filematrix[mydir] = rpmrepoGetFileList(repo, roots, '.rpm')
01605         self.trimRpms(filematrix[mydir])
01606         repo->pkgcount = argvCount(filematrix[mydir]);
01607         roots = argvFree(roots);
01608     }
01609 
01610     mediano = 1;
01611     repo->baseurl = self._getFragmentUrl(repo->baseurl, mediano)
01612 #endif
01613 
01614     if (rpmrepoOpenMDFile(repo, &repo->primary)
01615      || rpmrepoOpenMDFile(repo, &repo->filelists)
01616      || rpmrepoOpenMDFile(repo, &repo->other))
01617         rc = 1;
01618     if (rc) return rc;
01619 
01620 #ifdef  REFERENCE
01621     for mydir in repo->directories {
01622         repo->baseurl = self._getFragmentUrl(repo->baseurl, mediano)
01623         /* XXX todo: rpmGetPath(mydir, "/", filematrix[mydir], NULL); */
01624         if (repoWriteMetadataDocs(repo, filematrix[mydir]))
01625             rc = 1;
01626         mediano++;
01627     }
01628     repo->baseurl = self._getFragmentUrl(repo->baseurl, 1)
01629 #endif
01630 
01631     if (repoWriteMetadataDocs(repo))
01632         rc = 1;
01633 
01634     if (!repo->quiet)
01635         fprintf(stderr, "\n");
01636     if (rpmrepoCloseMDFile(repo, &repo->primary)
01637      || rpmrepoCloseMDFile(repo, &repo->filelists)
01638      || rpmrepoCloseMDFile(repo, &repo->other))
01639         rc = 1;
01640 
01641     return rc;
01642 }
01643 
01644 /*==============================================================*/
01645 
01646 /*@unchecked@*/
01647 static int compression = -1;
01648 
01649 /*@unchecked@*/ /*@observer@*/
01650 static struct poptOption repoCompressionPoptTable[] = {
01651  { "uncompressed", '\0', POPT_ARG_VAL,          &compression, 0,
01652         N_("don't compress"), NULL },
01653  { "gzip", 'Z', POPT_ARG_VAL,                   &compression, 1,
01654         N_("use gzip compression"), NULL },
01655  { "bzip2", '\0', POPT_ARG_VAL,                 &compression, 2,
01656         N_("use bzip2 compression"), NULL },
01657  { "lzma", '\0', POPT_ARG_VAL,                  &compression, 3,
01658         N_("use lzma compression"), NULL },
01659  { "xz", '\0', POPT_ARG_VAL,                    &compression, 4,
01660         N_("use xz compression"), NULL },
01661   POPT_TABLEEND
01662 };
01663 
01664 /*@unchecked@*/ /*@observer@*/
01665 static struct poptOption _rpmrepoOptions[] = {
01666 
01667  { "quiet", 'q', POPT_ARG_VAL,                  &__repo.quiet, 0,
01668         N_("output nothing except for serious errors"), NULL },
01669  { "verbose", 'v', 0,                           NULL, (int)'v',
01670         N_("output more debugging info."), NULL },
01671  { "dryrun", '\0', POPT_BIT_SET,        &__repo.flags, REPO_FLAGS_DRYRUN,
01672         N_("sanity check arguments, don't create metadata"), NULL },
01673  { "excludes", 'x', POPT_ARG_ARGV,              &__repo.exclude_patterns, 0,
01674         N_("glob PATTERN(s) to exclude"), N_("PATTERN") },
01675  { "includes", 'i', POPT_ARG_ARGV,              &__repo.include_patterns, 0,
01676         N_("glob PATTERN(s) to include"), N_("PATTERN") },
01677  { "basedir", '\0', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,    &__repo.basedir, 0,
01678         N_("top level directory"), N_("DIR") },
01679  { "baseurl", 'u', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,     &__repo.baseurl, 0,
01680         N_("baseurl to append on all files"), N_("BASEURL") },
01681 #ifdef  NOTYET
01682  { "groupfile", 'g', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,   &__repo.groupfile, 0,
01683         N_("path to groupfile to include in metadata"), N_("FILE") },
01684 #endif
01685  { "pretty", 'p', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,         &__repo.flags, REPO_FLAGS_PRETTY,
01686         N_("make sure all xml generated is formatted"), NULL },
01687  { "checkts", 'C', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,        &__repo.flags, REPO_FLAGS_CHECKTS,
01688         N_("check timestamps on files vs the metadata to see if we need to update"), NULL },
01689  { "database", 'd', POPT_BIT_SET,               &__repo.flags, REPO_FLAGS_DATABASE,
01690         N_("create sqlite3 database files"), NULL },
01691  { "split", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,         &__repo.flags, REPO_FLAGS_SPLIT,
01692         N_("generate split media"), NULL },
01693  { "pkglist", 'l', POPT_ARG_ARGV|POPT_ARGFLAG_DOC_HIDDEN,       &__repo.manifests, 0,
01694         N_("use only the files listed in this file from the directory specified"), N_("FILE") },
01695  { "outputdir", 'o', POPT_ARG_STRING,           &__repo.outputdir, 0,
01696         N_("<dir> = optional directory to output to"), N_("DIR") },
01697  { "skip-symlinks", 'S', POPT_BIT_SET,          &__repo.flags, REPO_FLAGS_NOFOLLOW,
01698         N_("ignore symlinks of packages"), NULL },
01699  { "unique-md-filenames", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &__repo.flags, REPO_FLAGS_UNIQUEMDFN,
01700         N_("include the file's checksum in the filename, helps with proxies"), NULL },
01701 
01702   POPT_TABLEEND
01703 
01704 };
01705 
01706 /*@unchecked@*/ /*@observer@*/
01707 static struct poptOption rpmrepoOptionsTable[] = {
01708 
01709  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, _rpmrepoOptions, 0,
01710         N_("Repository options:"), NULL },
01711 
01712  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0,
01713         N_("Fts(3) traversal options:"), NULL },
01714 
01715  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, repoCompressionPoptTable, 0,
01716         N_("Available compressions:"), NULL },
01717 
01718  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
01719         N_("Available digests:"), NULL },
01720 
01721  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
01722         N_("Common options for all rpmio executables:"),
01723         NULL },
01724 
01725   POPT_AUTOALIAS
01726   POPT_AUTOHELP
01727   POPT_TABLEEND
01728 };
01729 
01730 static int rpmrepoInitPopt(rpmrepo repo, char ** av)
01731         /*@modifies repo @*/
01732 {
01733     void *use =  repo->_item.use;
01734     void *pool = repo->_item.pool;
01735     int ac = argvCount((ARGV_t)av);
01736     poptContext con = rpmioInit(ac, av, rpmrepoOptionsTable);
01737     int rc = 0;         /* XXX assume success */
01738     int xx;
01739     int i;
01740 
01741     *repo = *_repo;     /* structure assignment */
01742     repo->_item.use = use;
01743     repo->_item.pool = pool;
01744 
01745     repo->con = con;
01746 
01747     /* XXX Impedanace match against poptIO common code. */
01748     if (rpmIsVerbose())
01749         repo->verbose++;
01750     if (rpmIsDebug())
01751         repo->verbose++;
01752 
01753     repo->ftsoptions = (rpmioFtsOpts ? rpmioFtsOpts : FTS_PHYSICAL);
01754     switch (repo->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)) {
01755     case (FTS_LOGICAL|FTS_PHYSICAL):
01756         rpmrepoError(1, "FTS_LOGICAL and FTS_PYSICAL are mutually exclusive");
01757         /*@notreached@*/ break;
01758     case 0:
01759         repo->ftsoptions |= FTS_PHYSICAL;
01760         break;
01761     }
01762 
01763     repo->algo = (rpmioDigestHashAlgo >= 0
01764                 ? (rpmioDigestHashAlgo & 0xff)  : PGPHASHALGO_SHA1);
01765 
01766     repo->compression = (compression >= 0 ? compression : 1);
01767     switch (repo->compression) {
01768     case 0:
01769         repo->suffix = NULL;
01770         repo->wmode = "w.ufdio";
01771         break;
01772     default:
01773         /*@fallthrough@*/
01774     case 1:
01775         repo->suffix = ".gz";
01776         repo->wmode = "w9.gzdio";
01777         break;
01778     case 2:
01779         repo->suffix = ".bz2";
01780         repo->wmode = "w9.bzdio";
01781         break;
01782     case 3:
01783         repo->suffix = ".lzma";
01784         repo->wmode = "w.lzdio";
01785         break;
01786     case 4:
01787         repo->suffix = ".xz";
01788         repo->wmode = "w.xzdio";
01789         break;
01790     }
01791 
01792     repo->av = poptGetArgs(repo->con);
01793 
01794     if (repo->av == NULL || repo->av[0] == NULL)
01795         rpmrepoError(1, _("Must specify path(s) to index."));
01796 
01797     if (repo->av != NULL)
01798     for (i = 0; repo->av[i] != NULL; i++) {
01799         char fullpath[MAXPATHLEN];
01800         struct stat sb;
01801         const char * rpath;
01802         const char * lpath = NULL;
01803         int ut = urlPath(repo->av[i], &lpath);
01804         size_t nb = (size_t)(lpath - repo->av[i]);
01805         int isdir = (lpath[strlen(lpath)-1] == '/');
01806         
01807         /* Convert to absolute/clean/malloc'd path. */
01808         if (lpath[0] != '/') {
01809             if ((rpath = rpmrepoRealpath(lpath)) == NULL)
01810                 rpmrepoError(1, _("Realpath(%s): %s"), lpath, strerror(errno));
01811             lpath = rpmGetPath(rpath, NULL);
01812             rpath = _free(rpath);
01813         } else
01814             lpath = rpmGetPath(lpath, NULL);
01815 
01816         /* Reattach the URI to the absolute/clean path. */
01817         /* XXX todo: rpmGenPath was confused by file:///path/file URI's. */
01818         switch (ut) {
01819         case URL_IS_DASH:
01820         case URL_IS_UNKNOWN:
01821             rpath = lpath;
01822             lpath = NULL;
01823             /*@switchbreak@*/ break;
01824         default:
01825 assert(nb < sizeof(fullpath));
01826             strncpy(fullpath, repo->av[i], nb);
01827             fullpath[nb] = '\0';
01828             rpath = rpmGenPath(fullpath, lpath, NULL);
01829             lpath = _free(lpath);
01830             /*@switchbreak@*/ break;
01831         }
01832 
01833         /* Add a trailing '/' on directories. */
01834         lpath = (isdir || (!Stat(rpath, &sb) && S_ISDIR(sb.st_mode))
01835                 ? "/" : NULL);
01836         if (lpath != NULL) {
01837             lpath = rpmExpand(rpath, lpath, NULL);
01838             xx = argvAdd(&repo->directories, lpath);
01839             lpath = _free(lpath);
01840         } else {
01841             xx = argvAdd(&repo->pkglist, rpath);
01842         }
01843         rpath = _free(rpath);
01844     }
01845 
01846     return rc;
01847 }
01848 
01849 static void rpmrepoFini(void * _repo)
01850         /*@globals fileSystem @*/
01851         /*@modifies *_repo, fileSystem @*/
01852 {
01853     rpmrepo repo = _repo;
01854 
01855     repo->primary.digest = _free(repo->primary.digest);
01856     repo->primary.Zdigest = _free(repo->primary.Zdigest);
01857     repo->filelists.digest = _free(repo->filelists.digest);
01858     repo->filelists.Zdigest = _free(repo->filelists.Zdigest);
01859     repo->other.digest = _free(repo->other.digest);
01860     repo->other.Zdigest = _free(repo->other.Zdigest);
01861     repo->repomd.digest = _free(repo->repomd.digest);
01862     repo->repomd.Zdigest = _free(repo->repomd.Zdigest);
01863     repo->outputdir = _free(repo->outputdir);
01864     repo->pkglist = argvFree(repo->pkglist);
01865     repo->directories = argvFree(repo->directories);
01866     repo->manifests = argvFree(repo->manifests);
01867 /*@-onlytrans -refcounttrans @*/
01868     repo->excludeMire = mireFreeAll(repo->excludeMire, repo->nexcludes);
01869     repo->includeMire = mireFreeAll(repo->includeMire, repo->nincludes);
01870 /*@=onlytrans =refcounttrans @*/
01871     repo->exclude_patterns = argvFree(repo->exclude_patterns);
01872     repo->include_patterns = argvFree(repo->include_patterns);
01873 
01874     repo->con = poptFreeContext(repo->con);
01875 
01876 }
01877 
01878 /*@unchecked@*/ /*@only@*/ /*@null@*/
01879 rpmioPool _rpmrepoPool = NULL;
01880 
01881 static rpmrepo rpmrepoGetPool(/*@null@*/ rpmioPool pool)
01882         /*@globals _rpmrepoPool, fileSystem @*/
01883         /*@modifies pool, _rpmrepoPool, fileSystem @*/
01884 {
01885     rpmrepo repo;
01886 
01887     if (_rpmrepoPool == NULL) {
01888         _rpmrepoPool = rpmioNewPool("repo", sizeof(*repo), -1, _rpmrepo_debug,
01889                         NULL, NULL, rpmrepoFini);
01890         pool = _rpmrepoPool;
01891     }
01892     repo = (rpmrepo) rpmioGetPool(pool, sizeof(*repo));
01893     memset(((char *)repo)+sizeof(repo->_item), 0, sizeof(*repo)-sizeof(repo->_item));
01894     return repo;
01895 }
01896 
01897 rpmrepo rpmrepoNew(char ** av, int flags)
01898 {
01899     rpmrepo repo = rpmrepoGetPool(_rpmrepoPool);
01900     int xx;
01901 
01902     xx = rpmrepoInitPopt(repo, av);
01903 
01904     return rpmrepoLink(repo);
01905 }