rpm
5.2.1
|
00001 00005 #include "system.h" 00006 00007 #if defined(WITH_SQLITE) 00008 #include <sqlite3.h> 00009 #ifdef __LCLINT__ 00010 /*@-incondefs -redecl @*/ 00011 extern const char *sqlite3_errmsg(sqlite3 *db) 00012 /*@*/; 00013 extern int sqlite3_open( 00014 const char *filename, /* Database filename (UTF-8) */ 00015 /*@out@*/ sqlite3 **ppDb /* OUT: SQLite db handle */ 00016 ) 00017 /*@modifies *ppDb @*/; 00018 extern int sqlite3_exec( 00019 sqlite3 *db, /* An open database */ 00020 const char *sql, /* SQL to be evaluted */ 00021 int (*callback)(void*,int,char**,char**), /* Callback function */ 00022 void *, /* 1st argument to callback */ 00023 /*@out@*/ char **errmsg /* Error msg written here */ 00024 ) 00025 /*@modifies db, *errmsg @*/; 00026 extern int sqlite3_prepare( 00027 sqlite3 *db, /* Database handle */ 00028 const char *zSql, /* SQL statement, UTF-8 encoded */ 00029 int nByte, /* Maximum length of zSql in bytes. */ 00030 /*@out@*/ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ 00031 /*@out@*/ const char **pzTail /* OUT: Pointer to unused portion of zSql */ 00032 ) 00033 /*@modifies *ppStmt, *pzTail @*/; 00034 extern int sqlite3_reset(sqlite3_stmt *pStmt) 00035 /*@modifies pStmt @*/; 00036 extern int sqlite3_step(sqlite3_stmt *pStmt) 00037 /*@modifies pStmt @*/; 00038 extern int sqlite3_finalize(/*@only@*/ sqlite3_stmt *pStmt) 00039 /*@modifies pStmt @*/; 00040 extern int sqlite3_close(sqlite3 * db) 00041 /*@modifies db @*/; 00042 /*@=incondefs =redecl @*/ 00043 #endif 00044 #endif 00045 00046 #include <rpmio_internal.h> /* XXX fdInitDigest() et al */ 00047 #include <fts.h> 00048 #include <argv.h> 00049 #include <mire.h> 00050 #include <poptIO.h> 00051 00052 #include <rpmtypes.h> 00053 #include <rpmtag.h> 00054 #include <pkgio.h> 00055 #include <rpmts.h> 00056 00057 #include "debug.h" 00058 00059 /*@access FD_t @*/ 00060 /*@access miRE @*/ 00061 00062 /*==============================================================*/ 00063 00064 /*@unchecked@*/ 00065 static int _repo_debug; 00066 00067 typedef struct rpmrepo_s * rpmrepo; 00068 typedef struct rpmrfile_s * rpmrfile; 00069 00073 struct rpmrfile_s { 00074 /*@observer@*/ 00075 const char * type; 00076 /*@observer@*/ 00077 const char * xml_init; 00078 /*@observer@*/ /*@relnull@*/ 00079 const char * xml_qfmt; 00080 /*@observer@*/ 00081 const char * xml_fini; 00082 /*@observer@*/ 00083 const char ** sql_init; 00084 /*@observer@*/ 00085 const char * sql_qfmt; 00086 #ifdef NOTYET /* XXX char **?!? */ 00087 /*@observer@*/ 00088 const char ** sql_fini; 00089 #endif 00090 /*@observer@*/ 00091 const char * yaml_init; 00092 /*@observer@*/ 00093 const char * yaml_qfmt; 00094 /*@observer@*/ 00095 const char * yaml_fini; 00096 /*@observer@*/ 00097 const char * Packages_init; 00098 /*@observer@*/ 00099 const char * Packages_qfmt; 00100 /*@observer@*/ 00101 const char * Packages_fini; 00102 /*@observer@*/ 00103 const char * Sources_init; 00104 /*@observer@*/ 00105 const char * Sources_qfmt; 00106 /*@observer@*/ 00107 const char * Sources_fini; 00108 /*@relnull@*/ 00109 FD_t fd; 00110 #if defined(WITH_SQLITE) 00111 sqlite3 * sqldb; 00112 #endif 00113 /*@null@*/ 00114 const char * digest; 00115 /*@null@*/ 00116 const char * Zdigest; 00117 time_t ctime; 00118 }; 00119 00123 struct rpmrepo_s { 00124 int quiet; 00125 int verbose; 00126 int dryrun; 00127 /*@null@*/ 00128 ARGV_t exclude_patterns; 00129 /*@relnull@*/ 00130 miRE excludeMire; 00131 int nexcludes; 00132 /*@null@*/ 00133 ARGV_t include_patterns; 00134 /*@relnull@*/ 00135 miRE includeMire; 00136 int nincludes; 00137 /*@null@*/ 00138 const char * basedir; 00139 /*@null@*/ 00140 const char * baseurl; 00141 #ifdef NOTYET 00142 /*@null@*/ 00143 const char * groupfile; 00144 #endif 00145 int split; 00146 #if defined(WITH_SQLITE) 00147 int database; 00148 #endif 00149 int pretty; 00150 int checkts; 00151 /*@relnull@*/ 00152 const char * outputdir; 00153 00154 int nofollow; 00155 /*@null@*/ 00156 ARGV_t manifests; 00157 00158 /*@observer@*/ /*@relnull@*/ 00159 const char * tempdir; 00160 /*@observer@*/ /*@relnull@*/ 00161 const char * finaldir; 00162 /*@observer@*/ /*@relnull@*/ 00163 const char * olddir; 00164 00165 time_t mdtimestamp; 00166 00167 int uniquemdfilenames; 00168 00169 /*@null@*/ 00170 rpmts ts; 00171 /*@null@*/ 00172 ARGV_t pkglist; 00173 unsigned current; 00174 unsigned pkgcount; 00175 00176 /*@null@*/ 00177 ARGV_t directories; 00178 int ftsoptions; 00179 uint32_t pkgalgo; 00180 uint32_t algo; 00181 int compression; 00182 /*@observer@*/ 00183 const char * markup; 00184 /*@observer@*/ /*@null@*/ 00185 const char * suffix; 00186 /*@observer@*/ 00187 const char * wmode; 00188 00189 struct rpmrfile_s primary; 00190 struct rpmrfile_s filelists; 00191 struct rpmrfile_s other; 00192 struct rpmrfile_s repomd; 00193 00194 }; 00195 00196 /*@unchecked@*/ /*@observer@*/ 00197 static const char primary_xml_init[] = 00198 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 00199 "<metadata xmlns=\"http://linux.duke.edu/metadata/common\" xmlns:rpm=\"http://linux.duke.edu/metadata/rpm\" packages=\"0\">\n"; 00200 /*@unchecked@*/ /*@observer@*/ 00201 static const char primary_xml_fini[] = "</metadata>\n"; 00202 00203 /*@unchecked@*/ /*@observer@*/ 00204 static const char filelists_xml_init[] = 00205 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 00206 "<filelists xmlns=\"http://linux.duke.edu/metadata/filelists\" packages=\"0\">\n"; 00207 /*@unchecked@*/ /*@observer@*/ 00208 static const char filelists_xml_fini[] = "</filelists>\n"; 00209 00210 /*@unchecked@*/ /*@observer@*/ 00211 static const char other_xml_init[] = 00212 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 00213 "<otherdata xmlns=\"http://linux.duke.edu/metadata/other\" packages=\"0\">\n"; 00214 /*@unchecked@*/ /*@observer@*/ 00215 static const char other_xml_fini[] = "</otherdata>\n"; 00216 00217 /*@unchecked@*/ /*@observer@*/ 00218 static const char repomd_xml_init[] = "\ 00219 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\ 00220 <repomd xmlns=\"http://linux.duke.edu/metadata/repo\">\n"; 00221 /*@unchecked@*/ /*@observer@*/ 00222 static const char repomd_xml_fini[] = "</repomd>\n"; 00223 00224 /* XXX todo: wire up popt aliases and bury the --queryformat glop externally. */ 00225 /*@unchecked@*/ /*@observer@*/ 00226 static const char primary_xml_qfmt[] = 00227 #include "yum_primary_xml" 00228 ; 00229 00230 /*@unchecked@*/ /*@observer@*/ 00231 static const char filelists_xml_qfmt[] = 00232 #include "yum_filelists_xml" 00233 ; 00234 00235 /*@unchecked@*/ /*@observer@*/ 00236 static const char other_xml_qfmt[] = 00237 #include "yum_other_xml" 00238 ; 00239 00240 /*@unchecked@*/ /*@observer@*/ 00241 static const char primary_yaml_qfmt[] = 00242 #include "wnh_primary_yaml" 00243 ; 00244 00245 /*@unchecked@*/ /*@observer@*/ 00246 static const char filelists_yaml_qfmt[] = 00247 #include "wnh_filelists_yaml" 00248 ; 00249 00250 /*@unchecked@*/ /*@observer@*/ 00251 static const char other_yaml_qfmt[] = 00252 #include "wnh_other_yaml" 00253 ; 00254 00255 /*@unchecked@*/ /*@observer@*/ 00256 static const char Packages_qfmt[] = 00257 #include "deb_Packages" 00258 ; 00259 00260 /*@unchecked@*/ /*@observer@*/ 00261 static const char Sources_qfmt[] = 00262 #include "deb_Sources" 00263 ; 00264 00265 /*@-nullassign@*/ 00266 /*@unchecked@*/ /*@observer@*/ 00267 static const char *primary_sql_init[] = { 00268 "PRAGMA synchronous = \"OFF\";", 00269 "pragma locking_mode = \"EXCLUSIVE\";", 00270 "CREATE TABLE conflicts ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );", 00271 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);", 00272 "CREATE TABLE files ( pkgKey INTEGER, name TEXT, type TEXT );", 00273 "CREATE TABLE obsoletes ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );", 00274 "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);", 00275 "CREATE TABLE provides ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );", 00276 "CREATE TABLE requires ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );", 00277 "CREATE INDEX filenames ON files (name);", 00278 "CREATE INDEX packageId ON packages (pkgId);", 00279 "CREATE INDEX packagename ON packages (name);", 00280 "CREATE INDEX pkgconflicts on conflicts (pkgKey);", 00281 "CREATE INDEX pkgobsoletes on obsoletes (pkgKey);", 00282 "CREATE INDEX pkgprovides on provides (pkgKey);", 00283 "CREATE INDEX pkgrequires on requires (pkgKey);", 00284 "CREATE INDEX providesname ON provides (name);", 00285 "CREATE INDEX requiresname ON requires (name);", 00286 "CREATE TRIGGER removals AFTER DELETE ON packages\ 00287 \n BEGIN\n\ 00288 \n DELETE FROM files WHERE pkgKey = old.pkgKey;\ 00289 \n DELETE FROM requires WHERE pkgKey = old.pkgKey;\ 00290 \n DELETE FROM provides WHERE pkgKey = old.pkgKey;\ 00291 \n DELETE FROM conflicts WHERE pkgKey = old.pkgKey;\ 00292 \n DELETE FROM obsoletes WHERE pkgKey = old.pkgKey;\ 00293 \n END;", 00294 "INSERT into db_info values (9, 'direct_create');", 00295 NULL 00296 }; 00297 /*XXX todo: DBVERSION needs to be set */ 00298 00299 /*@unchecked@*/ /*@observer@*/ 00300 static const char *filelists_sql_init[] = { 00301 "PRAGMA synchronous = \"OFF\";", 00302 "pragma locking_mode = \"EXCLUSIVE\";", 00303 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);", 00304 "CREATE TABLE filelist ( pkgKey INTEGER, name TEXT, type TEXT );", 00305 "CREATE TABLE packages ( pkgKey INTEGER PRIMARY KEY, pkgId TEXT);", 00306 "CREATE INDEX filelistnames ON filelist (name);", 00307 "CREATE INDEX keyfile ON filelist (pkgKey);", 00308 "CREATE INDEX pkgId ON packages (pkgId);", 00309 "CREATE TRIGGER remove_filelist AFTER DELETE ON packages\ 00310 \n BEGIN\ 00311 \n DELETE FROM filelist WHERE pkgKey = old.pkgKey;\ 00312 \n END;", 00313 "INSERT into db_info values (9, 'direct_create');", 00314 NULL 00315 }; 00316 /*XXX todo: DBVERSION needs to be set */ 00317 00318 /*@unchecked@*/ /*@observer@*/ 00319 static const char *other_sql_init[] = { 00320 "PRAGMA synchronous = \"OFF\";", 00321 "pragma locking_mode = \"EXCLUSIVE\";", 00322 "CREATE TABLE changelog ( pkgKey INTEGER, author TEXT, date INTEGER, changelog TEXT);", 00323 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);", 00324 "CREATE TABLE packages ( pkgKey INTEGER PRIMARY KEY, pkgId TEXT);", 00325 "CREATE INDEX keychange ON changelog (pkgKey);", 00326 "CREATE INDEX pkgId ON packages (pkgId);", 00327 "CREATE TRIGGER remove_changelogs AFTER DELETE ON packages\ 00328 \n BEGIN\ 00329 \n DELETE FROM changelog WHERE pkgKey = old.pkgKey;\ 00330 \n END;", 00331 "INSERT into db_info values (9, 'direct_create');", 00332 NULL 00333 }; 00334 /*XXX todo: DBVERSION needs to be set */ 00335 /*@=nullassign@*/ 00336 00337 /* packages 1 pkgKey INTEGER PRIMARY KEY */ 00338 /* packages 2 pkgId TEXT */ 00339 /* packages 3 name TEXT */ 00340 /* packages 4 arch TEXT */ 00341 /* packages 5 version TEXT */ 00342 /* packages 6 epoch TEXT */ 00343 /* packages 7 release TEXT */ 00344 /* packages 8 summary TEXT */ 00345 /* packages 9 description TEXT */ 00346 /* packages 10 url TEXT */ 00347 /* packages 11 time_file INTEGER */ 00348 /* packages 12 time_build INTEGER */ 00349 /* packages 13 rpm_license TEXT */ 00350 /* packages 14 rpm_vendor TEXT */ 00351 /* packages 15 rpm_group TEXT */ 00352 /* packages 16 rpm_buildhost TEXT */ 00353 /* packages 17 rpm_sourcerpm TEXT */ 00354 /* packages 18 rpm_header_start INTEGER */ 00355 /* packages 19 rpm_header_end INTEGER */ 00356 /* packages 20 rpm_packager TEXT */ 00357 /* packages 21 size_package INTEGER */ 00358 /* packages 22 size_installed INTEGER */ 00359 /* packages 23 size_archive INTEGER */ 00360 /* packages 24 location_href TEXT */ 00361 /* packages 25 location_base TEXT */ 00362 /* packages 26 checksum_type TEXT */ 00363 /* obsoletes 1 pkgKey INTEGER */ 00364 /* obsoletes 2 name TEXT */ 00365 /* obsoletes 3 flags TEXT */ 00366 /* obsoletes 4 epoch TEXT */ 00367 /* obsoletes 5 version TEXT */ 00368 /* obsoletes 6 release TEXT */ 00369 /* provides 1 pkgKey INTEGER */ 00370 /* provides 2 name TEXT */ 00371 /* provides 3 flags TEXT */ 00372 /* provides 4 epoch TEXT */ 00373 /* provides 5 version TEXT */ 00374 /* provides 6 release TEXT */ 00375 /* conflicts 1 pkgKey INTEGER */ 00376 /* conflicts 2 name TEXT */ 00377 /* conflicts 3 flags TEXT */ 00378 /* conflicts 4 epoch TEXT */ 00379 /* conflicts 5 version TEXT */ 00380 /* conflicts 6 release TEXT */ 00381 /* requires 1 pkgKey INTEGER */ 00382 /* requires 2 name TEXT */ 00383 /* requires 3 flags TEXT */ 00384 /* requires 4 epoch TEXT */ 00385 /* requires 5 version TEXT */ 00386 /* requires 6 release TEXT */ 00387 /* files 1 pkgKey INTEGER */ 00388 /* files 2 name TEXT */ 00389 /* files 3 type TEXT */ 00390 00391 /*@unchecked@*/ /*@observer@*/ 00392 static const char primary_sql_qfmt[] = 00393 #include "yum_primary_sqlite" 00394 ; 00395 00396 /* packages 1 pkgKey INTEGER PRIMARY KEY */ 00397 /* packages 2 pkgId TEXT */ 00398 /* filelist 1 pkgKey INTEGER */ 00399 /* filelist 2 name TEXT */ 00400 /* filelist 3 type TEXT */ 00401 00402 /*@unchecked@*/ /*@observer@*/ 00403 static const char filelists_sql_qfmt[] = 00404 #include "yum_filelists_sqlite" 00405 ; 00406 00407 /* packages 1 pkgKey INTEGER PRIMARY KEY */ 00408 /* packages 2 pkgId TEXT */ 00409 /* changelog 1 pkgKey INTEGER */ 00410 /* changelog 2 author TEXT */ 00411 /* changelog 3 date INTEGER */ 00412 /* changelog 4 changelog TEXT */ 00413 00414 /*@unchecked@*/ /*@observer@*/ 00415 static const char other_sql_qfmt[] = 00416 #include "yum_other_sqlite" 00417 ; 00418 00419 /*@-fullinitblock@*/ 00420 /*@unchecked@*/ 00421 static struct rpmrepo_s __rpmrepo = { 00422 .pretty = 1, 00423 #if defined(WITH_SQLITE) 00424 .database = 0, 00425 #endif 00426 .tempdir = ".repodata", 00427 .finaldir = "repodata", 00428 .olddir = ".olddata", 00429 .markup = ".xml", 00430 .pkgalgo = PGPHASHALGO_SHA1, 00431 .algo = PGPHASHALGO_SHA1, 00432 .primary = { 00433 .type = "primary", 00434 .xml_init= primary_xml_init, 00435 .xml_qfmt= primary_xml_qfmt, 00436 .xml_fini= primary_xml_fini, 00437 .sql_init= primary_sql_init, 00438 .sql_qfmt= primary_sql_qfmt, 00439 #ifdef NOTYET /* XXX char **?!? */ 00440 .sql_fini= NULL, 00441 #endif 00442 .yaml_init= NULL, 00443 .yaml_qfmt= primary_yaml_qfmt, 00444 .yaml_fini= NULL, 00445 .Packages_init= NULL, 00446 .Packages_qfmt= NULL, 00447 .Packages_fini= NULL, 00448 .Sources_init= NULL, 00449 .Sources_qfmt= NULL, 00450 .Sources_fini= NULL 00451 }, 00452 .filelists = { 00453 .type = "filelists", 00454 .xml_init= filelists_xml_init, 00455 .xml_qfmt= filelists_xml_qfmt, 00456 .xml_fini= filelists_xml_fini, 00457 .sql_init= filelists_sql_init, 00458 .sql_qfmt= filelists_sql_qfmt, 00459 #ifdef NOTYET /* XXX char **?!? */ 00460 .sql_fini= NULL, 00461 #endif 00462 .yaml_init= NULL, 00463 .yaml_qfmt= filelists_yaml_qfmt, 00464 .yaml_fini= NULL, 00465 .Packages_init= NULL, 00466 .Packages_qfmt= NULL, 00467 .Packages_fini= NULL, 00468 .Sources_init= NULL, 00469 .Sources_qfmt= NULL, 00470 .Sources_fini= NULL 00471 }, 00472 .other = { 00473 .type = "other", 00474 .xml_init= other_xml_init, 00475 .xml_qfmt= other_xml_qfmt, 00476 .xml_fini= other_xml_fini, 00477 .sql_init= other_sql_init, 00478 .sql_qfmt= other_sql_qfmt, 00479 #ifdef NOTYET /* XXX char **?!? */ 00480 .sql_fini= NULL, 00481 #endif 00482 .yaml_init= NULL, 00483 .yaml_qfmt= other_yaml_qfmt, 00484 .yaml_fini= NULL, 00485 .Packages_init= NULL, 00486 .Packages_qfmt= NULL, 00487 .Packages_fini= NULL, 00488 .Sources_init= NULL, 00489 .Sources_qfmt= NULL, 00490 .Sources_fini= NULL 00491 }, 00492 .repomd = { 00493 .type = "repomd", 00494 .xml_init= repomd_xml_init, 00495 .xml_qfmt= NULL, 00496 .xml_fini= repomd_xml_fini, 00497 .sql_init= NULL, 00498 .sql_qfmt= NULL, 00499 #ifdef NOTYET /* XXX char **?!? */ 00500 .sql_fini= NULL, 00501 #endif 00502 .yaml_init= NULL, 00503 .yaml_qfmt= NULL, 00504 .yaml_fini= NULL, 00505 .Packages_init= NULL, 00506 .Packages_qfmt= Packages_qfmt, 00507 .Packages_fini= NULL, 00508 .Sources_init= NULL, 00509 .Sources_qfmt= Sources_qfmt, 00510 .Sources_fini= NULL 00511 } 00512 }; 00513 /*@=fullinitblock@*/ 00514 00515 /*@unchecked@*/ 00516 static rpmrepo _rpmrepo = &__rpmrepo; 00517 00518 /*==============================================================*/ 00524 /*@mayexit@*/ 00525 static void 00526 repo_error(int lvl, const char *fmt, ...) 00527 /*@globals fileSystem @*/ 00528 /*@modifies fileSystem @*/ 00529 { 00530 va_list ap; 00531 00532 va_start(ap, fmt); 00533 (void) fflush(NULL); 00534 (void) fprintf(stderr, "%s: ", __progname); 00535 (void) vfprintf(stderr, fmt, ap); 00536 va_end (ap); 00537 (void) fprintf(stderr, "\n"); 00538 if (lvl) 00539 exit(EXIT_FAILURE); 00540 } 00541 00549 static void repoProgress(/*@unused@*/ rpmrepo repo, 00550 /*@null@*/ const char * item, int current, int total) 00551 /*@globals fileSystem, internalState @*/ 00552 /*@modifies fileSystem, internalState @*/ 00553 { 00554 static size_t ncols = 80 - 1; /* XXX TIOCGWINSIZ */ 00555 const char * bn = (item != NULL ? strrchr(item, '/') : NULL); 00556 size_t nb; 00557 00558 if (bn != NULL) 00559 bn++; 00560 else 00561 bn = item; 00562 nb = fprintf(stdout, "\r%s: %d/%d", __progname, current, total); 00563 if (bn != NULL) 00564 nb += fprintf(stdout, " - %s", bn); 00565 nb--; 00566 if (nb < ncols) 00567 fprintf(stdout, "%*s", (int)(ncols - nb), ""); 00568 ncols = nb; 00569 (void) fflush(stdout); 00570 } 00571 00577 static int rpmioExists(const char * fn, /*@out@*/ struct stat * st) 00578 /*@globals h_errno, fileSystem, internalState @*/ 00579 /*@modifies st, fileSystem, internalState @*/ 00580 { 00581 return (Stat(fn, st) == 0); 00582 } 00583 00589 static time_t rpmioCtime(const char * fn) 00590 /*@globals h_errno, fileSystem, internalState @*/ 00591 /*@modifies fileSystem, internalState @*/ 00592 { 00593 struct stat sb; 00594 time_t stctime = 0; 00595 00596 if (rpmioExists(fn, &sb)) 00597 stctime = sb.st_ctime; 00598 return stctime; 00599 } 00600 00606 /*@null@*/ 00607 static const char * repoRealpath(const char * lpath) 00608 /*@globals fileSystem, internalState @*/ 00609 /*@modifies fileSystem, internalState @*/ 00610 { 00611 /* XXX GLIBC: realpath(path, NULL) return malloc'd */ 00612 const char *rpath = Realpath(lpath, NULL); 00613 if (rpath == NULL) { 00614 char fullpath[MAXPATHLEN]; 00615 rpath = Realpath(lpath, fullpath); 00616 if (rpath != NULL) 00617 rpath = xstrdup(rpath); 00618 } 00619 return rpath; 00620 } 00621 00622 /*==============================================================*/ 00629 static int repoMkdir(rpmrepo repo, const char * dn) 00630 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00631 /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/ 00632 { 00633 const char * dnurl = rpmGetPath(repo->outputdir, "/", dn, NULL); 00634 /*@-mods@*/ 00635 int ut = urlPath(dnurl, &dn); 00636 /*@=mods@*/ 00637 int rc = 0;; 00638 00639 /* XXX todo: rpmioMkpath doesn't grok URI's */ 00640 if (ut == URL_IS_UNKNOWN) 00641 rc = rpmioMkpath(dn, 0755, (uid_t)-1, (gid_t)-1); 00642 else 00643 rc = (Mkdir(dnurl, 0755) == 0 || errno == EEXIST ? 0 : -1); 00644 if (rc) 00645 repo_error(0, _("Cannot create/verify %s: %s"), dnurl, strerror(errno)); 00646 dnurl = _free(dnurl); 00647 return rc; 00648 } 00649 00657 static const char * repoGetPath(rpmrepo repo, const char * dir, 00658 const char * type, int compress) 00659 /*@globals h_errno, rpmGlobalMacroContext, internalState @*/ 00660 /*@modifies rpmGlobalMacroContext, internalState @*/ 00661 { 00662 return rpmGetPath(repo->outputdir, "/", dir, "/", type, 00663 (repo->markup != NULL ? repo->markup : ""), 00664 (repo->suffix != NULL && compress ? repo->suffix : ""), NULL); 00665 } 00666 00672 static int repoTestSetupDirs(rpmrepo repo) 00673 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/ 00674 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/ 00675 { 00676 const char ** directories = repo->directories; 00677 struct stat sb, *st = &sb; 00678 const char * dn; 00679 const char * fn; 00680 int rc = 0; 00681 00682 /* XXX todo: check repo->pkglist existence? */ 00683 00684 if (directories != NULL) 00685 while ((dn = *directories++) != NULL) { 00686 if (!rpmioExists(dn, st) || !S_ISDIR(st->st_mode)) { 00687 repo_error(0, _("Directory %s must exist"), dn); 00688 rc = 1; 00689 } 00690 } 00691 00692 /* XXX todo create outputdir if it doesn't exist? */ 00693 if (!rpmioExists(repo->outputdir, st)) { 00694 repo_error(0, _("Directory %s does not exist."), repo->outputdir); 00695 rc = 1; 00696 } 00697 if (Access(repo->outputdir, W_OK)) { 00698 repo_error(0, _("Directory %s must be writable."), repo->outputdir); 00699 rc = 1; 00700 } 00701 00702 if (repoMkdir(repo, repo->tempdir) 00703 || repoMkdir(repo, repo->finaldir)) 00704 rc = 1; 00705 00706 dn = rpmGetPath(repo->outputdir, "/", repo->olddir, NULL); 00707 if (rpmioExists(dn, st)) { 00708 repo_error(0, _("Old data directory exists, please remove: %s"), dn); 00709 rc = 1; 00710 } 00711 dn = _free(dn); 00712 00713 { /*@observer@*/ 00714 static const char * dirs[] = { ".repodata", "repodata", NULL }; 00715 /*@observer@*/ 00716 static const char * types[] = 00717 { "primary", "filelists", "other", "repomd", NULL }; 00718 const char ** dirp, ** typep; 00719 for (dirp = dirs; *dirp != NULL; dirp++) { 00720 for (typep = types; *typep != NULL; typep++) { 00721 fn = repoGetPath(repo, *dirp, *typep, strcmp(*typep, "repomd")); 00722 if (rpmioExists(fn, st)) { 00723 if (Access(fn, W_OK)) { 00724 repo_error(0, _("Path must be writable: %s"), fn); 00725 rc = 1; 00726 } else 00727 if (repo->checkts && st->st_ctime > repo->mdtimestamp) 00728 repo->mdtimestamp = st->st_ctime; 00729 } 00730 fn = _free(fn); 00731 } 00732 } 00733 } 00734 00735 #ifdef NOTYET /* XXX repo->package_dir needs to go away. */ 00736 if (repo->groupfile != NULL) { 00737 if (repo->split || repo->groupfile[0] != '/') { 00738 fn = rpmGetPath(repo->package_dir, "/", repo->groupfile, NULL); 00739 repo->groupfile = _free(repo->groupfile); 00740 repo->groupfile = fn; 00741 fn = NULL; 00742 } 00743 if (!rpmioExists(repo->groupfile, st)) { 00744 repo_error(0, _("groupfile %s cannot be found."), repo->groupfile); 00745 rc = 1; 00746 } 00747 } 00748 #endif 00749 return rc; 00750 } 00751 00758 static int chkSuffix(const char * fn, const char * suffix) 00759 /*@*/ 00760 { 00761 size_t flen = strlen(fn); 00762 size_t slen = strlen(suffix); 00763 return (flen > slen && !strcmp(fn + flen - slen, suffix)); 00764 } 00765 00773 /*@null@*/ 00774 static const char ** repoGetFileList(rpmrepo repo, const char *roots[], 00775 const char * ext) 00776 /*@globals fileSystem, internalState @*/ 00777 /*@modifies repo, fileSystem, internalState @*/ 00778 { 00779 const char ** pkglist = NULL; 00780 FTS * t; 00781 FTSENT * p; 00782 int xx; 00783 00784 if ((t = Fts_open((char *const *)roots, repo->ftsoptions, NULL)) == NULL) 00785 repo_error(1, _("Fts_open: %s"), strerror(errno)); 00786 00787 while ((p = Fts_read(t)) != NULL) { 00788 #ifdef NOTYET 00789 const char * fts_name = p->fts_name; 00790 size_t fts_namelen = p->fts_namelen; 00791 00792 /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */ 00793 if (p->fts_level == 0 && fts_namelen == 0) { 00794 fts_name = "."; 00795 fts_namelen = sizeof(".") - 1; 00796 } 00797 #endif 00798 00799 /* Should this element be excluded/included? */ 00800 /* XXX todo: apply globs to fts_path rather than fts_name? */ 00801 /*@-onlytrans@*/ 00802 if (mireApply(repo->excludeMire, repo->nexcludes, p->fts_name, 0, -1) >= 0) 00803 continue; 00804 if (mireApply(repo->includeMire, repo->nincludes, p->fts_name, 0, +1) < 0) 00805 continue; 00806 /*@=onlytrans@*/ 00807 00808 switch (p->fts_info) { 00809 case FTS_D: 00810 case FTS_DP: 00811 default: 00812 continue; 00813 /*@notreached@*/ /*@switchbreak@*/ break; 00814 case FTS_SL: 00815 if (repo->nofollow) 00816 continue; 00817 /* XXX todo: fuss with symlinks */ 00818 /*@notreached@*/ /*@switchbreak@*/ break; 00819 case FTS_F: 00820 /* Is this a *.rpm file? */ 00821 if (chkSuffix(p->fts_name, ext)) 00822 xx = argvAdd(&pkglist, p->fts_path); 00823 /*@switchbreak@*/ break; 00824 } 00825 } 00826 00827 (void) Fts_close(t); 00828 00829 if (_repo_debug) 00830 argvPrint("pkglist", pkglist, NULL); 00831 00832 return pkglist; 00833 } 00834 00840 static int repoCheckTimeStamps(rpmrepo repo) 00841 /*@globals h_errno, fileSystem, internalState @*/ 00842 /*@modifies fileSystem, internalState @*/ 00843 { 00844 int rc = 0; 00845 00846 if (repo->checkts) { 00847 const char ** pkg; 00848 00849 if (repo->pkglist != NULL) 00850 for (pkg = repo->pkglist; *pkg != NULL ; pkg++) { 00851 struct stat sb, *st = &sb; 00852 if (!rpmioExists(*pkg, st)) { 00853 repo_error(0, _("cannot get to file: %s"), *pkg); 00854 rc = 1; 00855 } else if (st->st_ctime > repo->mdtimestamp) 00856 rc = 1; 00857 } 00858 } else 00859 rc = 1; 00860 00861 return rc; 00862 } 00863 00870 static int rfileXMLWrite(rpmrfile rfile, /*@only@*/ /*@null@*/ const char * spew) 00871 /*@globals fileSystem @*/ 00872 /*@modifies rfile, fileSystem @*/ 00873 { 00874 size_t nspew = (spew != NULL ? strlen(spew) : 0); 00875 /*@-nullpass@*/ /* XXX spew != NULL @*/ 00876 size_t nb = (nspew > 0 ? Fwrite(spew, 1, nspew, rfile->fd) : 0); 00877 /*@=nullpass@*/ 00878 int rc = 0; 00879 if (nspew != nb) { 00880 repo_error(0, _("Fwrite failed: expected write %u != %u bytes: %s\n"), 00881 (unsigned)nspew, (unsigned)nb, Fstrerror(rfile->fd)); 00882 rc = 1; 00883 } 00884 spew = _free(spew); 00885 return rc; 00886 } 00887 00894 static int repoFclose(rpmrepo repo, FD_t fd) 00895 /*@modifies repo, fd @*/ 00896 { 00897 int rc = 0; 00898 00899 if (fd != NULL) { 00900 if (repo->ts != NULL) { 00901 (void) rpmswAdd(rpmtsOp(repo->ts, RPMTS_OP_UNCOMPRESS), 00902 fdstat_op(fd, FDSTAT_READ)); 00903 (void) rpmswAdd(rpmtsOp(repo->ts, RPMTS_OP_DIGEST), 00904 fdstat_op(fd, FDSTAT_DIGEST)); 00905 } 00906 rc = Fclose(fd); 00907 } 00908 return rc; 00909 } 00910 00917 static int repoOpenMDFile(const rpmrepo repo, rpmrfile rfile) 00918 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/ 00919 /*@modifies rfile, rpmGlobalMacroContext, fileSystem, internalState @*/ 00920 { 00921 const char * spew = rfile->xml_init; 00922 size_t nspew = strlen(spew); 00923 const char * fn = repoGetPath(repo, repo->tempdir, rfile->type, 1); 00924 const char * tail; 00925 size_t nb; 00926 int rc = 0; 00927 00928 rfile->fd = Fopen(fn, repo->wmode); 00929 assert(rfile->fd != NULL); 00930 00931 if (repo->algo != PGPHASHALGO_NONE) 00932 fdInitDigest(rfile->fd, repo->algo, 0); 00933 00934 if ((tail = strstr(spew, " packages=\"0\">\n")) != NULL) 00935 nspew -= strlen(tail); 00936 00937 nb = Fwrite(spew, 1, nspew, rfile->fd); 00938 00939 if (tail != NULL) { 00940 char buf[64]; 00941 size_t tnb = snprintf(buf, sizeof(buf), " packages=\"%d\">\n", 00942 repo->pkgcount); 00943 nspew += tnb; 00944 nb += Fwrite(buf, 1, tnb, rfile->fd); 00945 } 00946 if (nspew != nb) { 00947 repo_error(0, _("Fwrite failed: expected write %u != %u bytes: %s\n"), 00948 (unsigned)nspew, (unsigned)nb, Fstrerror(rfile->fd)); 00949 rc = 1; 00950 } 00951 00952 fn = _free(fn); 00953 00954 #if defined(WITH_SQLITE) 00955 if (repo->database) { 00956 const char ** stmt; 00957 int xx; 00958 fn = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", 00959 rfile->type, ".sqlite", NULL); 00960 if ((xx = sqlite3_open(fn, &rfile->sqldb)) != SQLITE_OK) 00961 repo_error(1, "sqlite3_open(%s): %s", fn, sqlite3_errmsg(rfile->sqldb)); 00962 for (stmt = rfile->sql_init; *stmt != NULL; stmt++) { 00963 char * msg; 00964 xx = sqlite3_exec(rfile->sqldb, *stmt, NULL, NULL, &msg); 00965 if (xx != SQLITE_OK) 00966 repo_error(1, "sqlite3_exec(%s, \"%s\"): %s\n", fn, *stmt, 00967 (msg != NULL ? msg : "failed")); 00968 } 00969 fn = _free(fn); 00970 } 00971 #endif 00972 00973 return rc; 00974 } 00975 00982 static Header repoReadHeader(rpmrepo repo, const char * path) 00983 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00984 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/ 00985 { 00986 /* XXX todo: read the payload and collect the blessed file digest. */ 00987 FD_t fd = Fopen(path, "r.ufdio"); 00988 Header h = NULL; 00989 00990 if (fd != NULL) { 00991 uint32_t algo = repo->pkgalgo; 00992 rpmRC rpmrc; 00993 00994 if (algo != PGPHASHALGO_NONE) 00995 fdInitDigest(fd, algo, 0); 00996 00997 /* XXX what if path needs expansion? */ 00998 rpmrc = rpmReadPackageFile(repo->ts, fd, path, &h); 00999 if (algo != PGPHASHALGO_NONE) { 01000 char buffer[32 * BUFSIZ]; 01001 size_t nb = sizeof(buffer); 01002 size_t nr; 01003 while ((nr = Fread(buffer, sizeof(buffer[0]), nb, fd)) == nb) 01004 {}; 01005 if (Ferror(fd)) { 01006 fprintf(stderr, _("%s: Fread(%s) failed: %s\n"), 01007 __progname, path, Fstrerror(fd)); 01008 rpmrc = RPMRC_FAIL; 01009 } else { 01010 static int asAscii = 1; 01011 const char *digest = NULL; 01012 fdFiniDigest(fd, algo, &digest, NULL, asAscii); 01013 (void) headerSetDigest(h, digest); 01014 digest = _free(digest); 01015 } 01016 } 01017 01018 (void) Fclose(fd); 01019 01020 switch (rpmrc) { 01021 case RPMRC_NOTFOUND: 01022 case RPMRC_FAIL: 01023 default: 01024 (void)headerFree(h); 01025 h = NULL; 01026 break; 01027 case RPMRC_NOTTRUSTED: 01028 case RPMRC_NOKEY: 01029 case RPMRC_OK: 01030 if (repo->baseurl) 01031 (void) headerSetBaseURL(h, repo->baseurl); 01032 break; 01033 } 01034 } 01035 return h; 01036 } 01037 01044 static const char * rfileHeaderSprintf(Header h, const char * qfmt) 01045 /*@globals fileSystem @*/ 01046 /*@modifies h, fileSystem @*/ 01047 { 01048 const char * msg = NULL; 01049 const char * s = headerSprintf(h, qfmt, NULL, NULL, &msg); 01050 if (s == NULL) 01051 repo_error(1, _("headerSprintf(%s): %s"), qfmt, msg); 01052 assert(s != NULL); 01053 return s; 01054 } 01055 01056 #if defined(WITH_SQLITE) 01057 01062 static int rfileSQL(rpmrfile rfile, const char * msg, int rc) 01063 /*@globals fileSystem @*/ 01064 /*@modifies fileSystem @*/ 01065 { 01066 if (rc != SQLITE_OK || _repo_debug) 01067 repo_error(0, "sqlite3_%s(%s): %s", msg, rfile->type, 01068 sqlite3_errmsg(rfile->sqldb)); 01069 return rc; 01070 } 01071 01077 static int rfileSQLStep(rpmrfile rfile, sqlite3_stmt * stmt) 01078 /*@globals fileSystem @*/ 01079 /*@modifies fileSystem @*/ 01080 { 01081 int loop = 1; 01082 int rc = 0; 01083 int xx; 01084 01085 /*@-infloops@*/ 01086 while (loop) { 01087 rc = sqlite3_step(stmt); 01088 switch (rc) { 01089 default: 01090 rc = rfileSQL(rfile, "step", rc); 01091 /*@fallthrough@*/ 01092 case SQLITE_DONE: 01093 loop = 0; 01094 /*@switchbreak@*/ break; 01095 } 01096 } 01097 /*@=infloops@*/ 01098 01099 xx = rfileSQL(rfile, "reset", 01100 sqlite3_reset(stmt)); 01101 01102 return rc; 01103 } 01104 01111 static const char * rfileHeaderSprintfHack(Header h, const char * qfmt) 01112 /*@globals fileSystem @*/ 01113 /*@modifies h, fileSystem @*/ 01114 { 01115 static const char mark[] = "'XXX'"; 01116 static size_t nmark = sizeof("'XXX'") - 1; 01117 const char * msg = NULL; 01118 char * s = (char *) headerSprintf(h, qfmt, NULL, NULL, &msg); 01119 char * f, * fe; 01120 int nsubs = 0; 01121 01122 if (s == NULL) 01123 repo_error(1, _("headerSprintf(%s): %s"), qfmt, msg); 01124 assert(s != NULL); 01125 01126 /* XXX Find & replace 'XXX' with '%{DBINSTANCE}' the hard way. */ 01127 /*@-nullptrarith@*/ 01128 for (f = s; *f != '\0' && (fe = strstr(f, "'XXX'")) != NULL; fe += nmark, f = fe) 01129 nsubs++; 01130 /*@=nullptrarith@*/ 01131 01132 if (nsubs > 0) { 01133 char instance[64]; 01134 int xx = snprintf(instance, sizeof(instance), "'%u'", 01135 (unsigned) headerGetInstance(h)); 01136 size_t tlen = strlen(s) + nsubs * ((int)strlen(instance) - (int)nmark); 01137 char * t = xmalloc(tlen + 1); 01138 char * te = t; 01139 01140 xx = xx; 01141 /*@-nullptrarith@*/ 01142 for (f = s; *f != '\0' && (fe = strstr(f, mark)) != NULL; fe += nmark, f = fe) { 01143 *fe = '\0'; 01144 te = stpcpy( stpcpy(te, f), instance); 01145 } 01146 /*@=nullptrarith@*/ 01147 if (*f != '\0') 01148 te = stpcpy(te, f); 01149 s = _free(s); 01150 s = t; 01151 } 01152 01153 return s; 01154 } 01155 01162 static int rfileSQLWrite(rpmrfile rfile, /*@only@*/ const char * cmd) 01163 /*@globals fileSystem @*/ 01164 /*@modifies fileSystem @*/ 01165 { 01166 sqlite3_stmt * stmt; 01167 const char * tail; 01168 int xx; 01169 01170 xx = rfileSQL(rfile, "prepare", 01171 sqlite3_prepare(rfile->sqldb, cmd, (int)strlen(cmd), &stmt, &tail)); 01172 01173 xx = rfileSQL(rfile, "reset", 01174 sqlite3_reset(stmt)); 01175 01176 xx = rfileSQLStep(rfile, stmt); 01177 01178 xx = rfileSQL(rfile, "finalize", 01179 sqlite3_finalize(stmt)); 01180 01181 cmd = _free(cmd); 01182 01183 return 0; 01184 } 01185 #endif 01186 01194 static int repoWriteMDFile(rpmrepo repo, rpmrfile rfile, Header h) 01195 /*@globals fileSystem @*/ 01196 /*@modifies rfile, h, fileSystem @*/ 01197 { 01198 int rc = 0; 01199 01200 if (rfile->xml_qfmt != NULL) { 01201 if (rfileXMLWrite(rfile, rfileHeaderSprintf(h, rfile->xml_qfmt))) 01202 rc = 1; 01203 } 01204 01205 #if defined(WITH_SQLITE) 01206 if (repo->database) { 01207 if (rfileSQLWrite(rfile, rfileHeaderSprintfHack(h, rfile->sql_qfmt))) 01208 rc = 1; 01209 } 01210 #endif 01211 01212 return rc; 01213 } 01214 01221 static int repoWriteMetadataDocs(rpmrepo repo, /*@null@*/ const char ** pkglist) 01222 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/ 01223 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/ 01224 { 01225 const char * pkg; 01226 int rc = 0; 01227 01228 while ((pkg = *pkglist++) != NULL) { 01229 Header h = repoReadHeader(repo, pkg); 01230 01231 repo->current++; 01232 if (h == NULL) { 01233 #ifdef DYING /* XXX repoReadHeader() displays error. Continuing is foolish */ 01234 repo_error(0, _("\nError %s: %s\n"), pkg, strerror(errno)); 01235 continue; 01236 #else 01237 rc = 1; 01238 break; 01239 #endif 01240 } 01241 (void) headerSetInstance(h, (uint32_t)repo->current); 01242 01243 #ifdef NOTYET 01244 /* XXX todo: rpmGetPath(mydir, "/", filematrix[mydir], NULL); */ 01245 reldir = (pkgpath != NULL ? pkgpath : rpmGetPath(repo->basedir, "/", repo->directories[0], NULL)); 01246 self.primaryfile.write(po.do_primary_xml_dump(reldir, baseurl=repo->baseurl)) 01247 self.flfile.write(po.do_filelists_xml_dump()) 01248 self.otherfile.write(po.do_other_xml_dump()) 01249 #endif 01250 if (repoWriteMDFile(repo, &repo->primary, h) 01251 || repoWriteMDFile(repo, &repo->filelists, h) 01252 || repoWriteMDFile(repo, &repo->other, h)) 01253 rc = 1; 01254 01255 (void)headerFree(h); 01256 h = NULL; 01257 if (rc) break; 01258 01259 if (!repo->quiet) { 01260 if (repo->verbose) 01261 repo_error(0, "%d/%d - %s", repo->current, repo->pkgcount, pkg); 01262 else 01263 repoProgress(repo, pkg, repo->current, repo->pkgcount); 01264 } 01265 } 01266 return rc; 01267 } 01268 01273 static int repoRfileDigest(const rpmrepo repo, rpmrfile rfile, 01274 const char ** digestp) 01275 /*@modifies *digestp @*/ 01276 { 01277 static int asAscii = 1; 01278 struct stat sb, *st = &sb; 01279 const char * fn = repoGetPath(repo, repo->tempdir, rfile->type, 1); 01280 const char * path = NULL; 01281 int ut = urlPath(fn, &path); 01282 FD_t fd = NULL; 01283 int rc = 1; 01284 int xx; 01285 01286 memset(st, 0, sizeof(*st)); 01287 if (!rpmioExists(fn, st)) 01288 goto exit; 01289 fd = Fopen(fn, "r.ufdio"); 01290 if (fd == NULL || Ferror(fd)) 01291 goto exit; 01292 01293 switch (ut) { 01294 case URL_IS_PATH: 01295 case URL_IS_UNKNOWN: 01296 #if defined(HAVE_MMAP) 01297 { void * mapped = (void *)-1; 01298 01299 if (st->st_size > 0) 01300 mapped = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, Fileno(fd), 0); 01301 if (mapped != (void *)-1) { 01302 rpmop op = rpmtsOp(repo->ts, RPMTS_OP_DIGEST); 01303 rpmtime_t tstamp = rpmswEnter(op, 0); 01304 DIGEST_CTX ctx = rpmDigestInit(repo->algo, RPMDIGEST_NONE); 01305 xx = rpmDigestUpdate(ctx, mapped, st->st_size); 01306 xx = rpmDigestFinal(ctx, digestp, NULL, asAscii); 01307 tstamp = rpmswExit(op, st->st_size); 01308 xx = munmap(mapped, st->st_size); 01309 break; 01310 } 01311 } /*@fallthrough@*/ 01312 #endif 01313 default: 01314 { char buf[64 * BUFSIZ]; 01315 size_t nb; 01316 size_t fsize = 0; 01317 01318 fdInitDigest(fd, repo->algo, 0); 01319 while ((nb = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) 01320 fsize += nb; 01321 if (Ferror(fd)) 01322 goto exit; 01323 fdFiniDigest(fd, repo->algo, digestp, NULL, asAscii); 01324 } break; 01325 } 01326 01327 rc = 0; 01328 01329 exit: 01330 if (fd) 01331 xx = repoFclose(repo, fd); 01332 fn = _free(fn); 01333 return rc; 01334 } 01335 01342 static int repoCloseMDFile(const rpmrepo repo, rpmrfile rfile) 01343 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/ 01344 /*@modifies rfile, rpmGlobalMacroContext, fileSystem, internalState @*/ 01345 { 01346 static int asAscii = 1; 01347 char * xmlfn = xstrdup(fdGetOPath(rfile->fd)); 01348 int rc = 0; 01349 01350 if (!repo->quiet) 01351 repo_error(0, _("Saving %s metadata"), basename(xmlfn)); 01352 01353 if (rfileXMLWrite(rfile, xstrdup(rfile->xml_fini))) 01354 rc = 1; 01355 01356 if (repo->algo > 0) 01357 fdFiniDigest(rfile->fd, repo->algo, &rfile->digest, NULL, asAscii); 01358 else 01359 rfile->digest = xstrdup(""); 01360 01361 (void) repoFclose(repo, rfile->fd); 01362 rfile->fd = NULL; 01363 01364 /* Compute the (usually compressed) ouput file digest too. */ 01365 rfile->Zdigest = NULL; 01366 (void) repoRfileDigest(repo, rfile, &rfile->Zdigest); 01367 01368 #if defined(WITH_SQLITE) 01369 if (repo->database && rfile->sqldb != NULL) { 01370 const char *dbfn = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", 01371 rfile->type, ".sqlite", NULL); 01372 int xx; 01373 if ((xx = sqlite3_close(rfile->sqldb)) != SQLITE_OK) 01374 repo_error(1, "sqlite3_close(%s): %s", dbfn, sqlite3_errmsg(rfile->sqldb)); 01375 rfile->sqldb = NULL; 01376 dbfn = _free(dbfn); 01377 } 01378 #endif 01379 01380 rfile->ctime = rpmioCtime(xmlfn); 01381 xmlfn = _free(xmlfn); 01382 01383 return rc; 01384 } 01385 01391 static int repoDoPkgMetadata(rpmrepo repo) 01392 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/ 01393 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/ 01394 { 01395 int rc = 0; 01396 01397 repo->current = 0; 01398 01399 #ifdef NOTYET 01400 def _getFragmentUrl(self, url, fragment): 01401 import urlparse 01402 urlparse.uses_fragment.append('media') 01403 if not url: 01404 return url 01405 (scheme, netloc, path, query, fragid) = urlparse.urlsplit(url) 01406 return urlparse.urlunsplit((scheme, netloc, path, query, str(fragment))) 01407 01408 def doPkgMetadata(self): 01409 """all the heavy lifting for the package metadata""" 01410 if (argvCount(repo->directories) == 1) { 01411 MetaDataGenerator.doPkgMetadata(self) 01412 return 01413 } 01414 01415 ARGV_t roots = NULL; 01416 filematrix = {} 01417 for mydir in repo->directories { 01418 if (mydir[0] == '/') 01419 thisdir = xstrdup(mydir); 01420 else if (mydir[0] == '.' && mydir[1] == '.' && mydir[2] == '/') 01421 thisdir = Realpath(mydir, NULL); 01422 else 01423 thisdir = rpmGetPath(repo->basedir, "/", mydir, NULL); 01424 01425 xx = argvAdd(&roots, thisdir); 01426 thisdir = _free(thisdir); 01427 01428 filematrix[mydir] = repoGetFileList(repo, roots, '.rpm') 01429 self.trimRpms(filematrix[mydir]) 01430 repo->pkgcount = argvCount(filematrix[mydir]); 01431 roots = argvFree(roots); 01432 } 01433 01434 mediano = 1; 01435 repo->baseurl = self._getFragmentUrl(repo->baseurl, mediano) 01436 #endif 01437 01438 if (repoOpenMDFile(repo, &repo->primary) 01439 || repoOpenMDFile(repo, &repo->filelists) 01440 || repoOpenMDFile(repo, &repo->other)) 01441 rc = 1; 01442 if (rc) return rc; 01443 01444 #ifdef NOTYET 01445 for mydir in repo->directories { 01446 repo->baseurl = self._getFragmentUrl(repo->baseurl, mediano) 01447 /* XXX todo: rpmGetPath(mydir, "/", filematrix[mydir], NULL); */ 01448 if (repoWriteMetadataDocs(repo, filematrix[mydir])) 01449 rc = 1; 01450 mediano++; 01451 } 01452 repo->baseurl = self._getFragmentUrl(repo->baseurl, 1) 01453 #else 01454 if (repoWriteMetadataDocs(repo, repo->pkglist)) 01455 rc = 1; 01456 #endif 01457 01458 if (!repo->quiet) 01459 fprintf(stderr, "\n"); 01460 if (repoCloseMDFile(repo, &repo->primary) 01461 || repoCloseMDFile(repo, &repo->filelists) 01462 || repoCloseMDFile(repo, &repo->other)) 01463 rc = 1; 01464 01465 return rc; 01466 } 01467 01470 static /*@observer@*/ /*@null@*/ const char * 01471 algo2tagname(uint32_t algo) 01472 /*@*/ 01473 { 01474 const char * tagname = NULL; 01475 01476 switch (algo) { 01477 case PGPHASHALGO_NONE: tagname = "none"; break; 01478 case PGPHASHALGO_MD5: tagname = "md5"; break; 01479 /* XXX todo: should be "sha1" */ 01480 case PGPHASHALGO_SHA1: tagname = "sha"; break; 01481 case PGPHASHALGO_RIPEMD160: tagname = "rmd160"; break; 01482 case PGPHASHALGO_MD2: tagname = "md2"; break; 01483 case PGPHASHALGO_TIGER192: tagname = "tiger192"; break; 01484 case PGPHASHALGO_HAVAL_5_160: tagname = "haval160"; break; 01485 case PGPHASHALGO_SHA256: tagname = "sha256"; break; 01486 case PGPHASHALGO_SHA384: tagname = "sha384"; break; 01487 case PGPHASHALGO_SHA512: tagname = "sha512"; break; 01488 case PGPHASHALGO_MD4: tagname = "md4"; break; 01489 case PGPHASHALGO_RIPEMD128: tagname = "rmd128"; break; 01490 case PGPHASHALGO_CRC32: tagname = "crc32"; break; 01491 case PGPHASHALGO_ADLER32: tagname = "adler32"; break; 01492 case PGPHASHALGO_CRC64: tagname = "crc64"; break; 01493 case PGPHASHALGO_JLU32: tagname = "jlu32"; break; 01494 case PGPHASHALGO_SHA224: tagname = "sha224"; break; 01495 case PGPHASHALGO_RIPEMD256: tagname = "rmd256"; break; 01496 case PGPHASHALGO_RIPEMD320: tagname = "rmd320"; break; 01497 case PGPHASHALGO_SALSA10: tagname = "salsa10"; break; 01498 case PGPHASHALGO_SALSA20: tagname = "salsa20"; break; 01499 default: tagname = NULL; break; 01500 } 01501 return tagname; 01502 } 01503 01509 static const char * repoMDExpand(rpmrepo repo, rpmrfile rfile) 01510 /*@globals h_errno, rpmGlobalMacroContext, internalState @*/ 01511 /*@modifies rpmGlobalMacroContext, internalState @*/ 01512 { 01513 const char * spewalgo = algo2tagname(repo->algo); 01514 char spewtime[64]; 01515 01516 (void) snprintf(spewtime, sizeof(spewtime), "%u", (unsigned)rfile->ctime); 01517 return rpmExpand("\ 01518 <data type=\"", rfile->type, "\">\n\ 01519 <checksum type=\"", spewalgo, "\">", rfile->Zdigest, "</checksum>\n\ 01520 <timestamp>", spewtime, "</timestamp>\n\ 01521 <open-checksum type=\"",spewalgo,"\">", rfile->digest, "</open-checksum>\n\ 01522 <location href=\"", repo->finaldir, "/", rfile->type, (repo->markup != NULL ? repo->markup : ""), (repo->suffix != NULL ? repo->suffix : ""), "\"/>\n\ 01523 </data>\n", NULL); 01524 } 01525 01531 static int repoDoRepoMetadata(rpmrepo repo) 01532 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/ 01533 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/ 01534 { 01535 rpmrfile rfile = &repo->repomd; 01536 const char * fn = repoGetPath(repo, repo->tempdir, rfile->type, 0); 01537 int rc = 0; 01538 01539 if ((rfile->fd = Fopen(fn, "w.ufdio")) != NULL) { /* no compression */ 01540 if (rfileXMLWrite(rfile, xstrdup(rfile->xml_init)) 01541 || rfileXMLWrite(rfile, repoMDExpand(repo, &repo->other)) 01542 || rfileXMLWrite(rfile, repoMDExpand(repo, &repo->filelists)) 01543 || rfileXMLWrite(rfile, repoMDExpand(repo, &repo->primary)) 01544 || rfileXMLWrite(rfile, xstrdup(rfile->xml_fini))) 01545 rc = 1; 01546 (void) repoFclose(repo, rfile->fd); 01547 rfile->fd = NULL; 01548 } 01549 01550 fn = _free(fn); 01551 if (rc) return rc; 01552 01553 #ifdef NOTYET 01554 def doRepoMetadata(self): 01555 """wrapper to generate the repomd.xml file that stores the info on the other files""" 01556 const char * repopath = 01557 rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL); 01558 repodoc = libxml2.newDoc("1.0") 01559 reporoot = repodoc.newChild(None, "repomd", None) 01560 repons = reporoot.newNs("http://linux.duke.edu/metadata/repo", None) 01561 reporoot.setNs(repons) 01562 repopath = rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL); 01563 fn = repoGetPath(repo, repo->tempdir, repo->repomd.type, 1); 01564 01565 repoid = "garbageid"; 01566 01567 if (repo->database) { 01568 if (!repo->quiet) repo_error(0, _("Generating sqlite DBs")); 01569 try: 01570 dbversion = str(sqlitecachec.DBVERSION) 01571 except AttributeError: 01572 dbversion = "9" 01573 rp = sqlitecachec.RepodataParserSqlite(repopath, repoid, None) 01574 } 01575 01576 { static const char * types[] = 01577 { "primary", "filelists", "other", NULL }; 01578 const char ** typep; 01579 for (typep = types; *typep != NULL; typep++) { 01580 complete_path = repoGetPath(repo, repo->tempdir, *typep, 1); 01581 01582 zfo = _gzipOpen(complete_path) 01583 uncsum = misc.checksum(algo2tagname(repo->algo), zfo) 01584 zfo.close() 01585 csum = misc.checksum(algo2tagname(repo->algo), complete_path) 01586 (void) rpmioExists(complete_path, st) 01587 timestamp = os.stat(complete_path)[8] 01588 01589 db_csums = {} 01590 db_compressed_sums = {} 01591 01592 if (repo->database) { 01593 if (repo->verbose) { 01594 time_t now = time(NULL); 01595 repo_error(0, _("Starting %s db creation: %s"), 01596 *typep, ctime(&now)); 01597 } 01598 01599 if (!strcmp(*typep, "primary")) 01600 rp.getPrimary(complete_path, csum) 01601 else if (!strcmp(*typep, "filelists")); 01602 rp.getFilelists(complete_path, csum) 01603 else if (!strcmp(*typep, "other")) 01604 rp.getOtherdata(complete_path, csum) 01605 01606 { const char * tmp_result_path = 01607 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", 01608 *typep, ".xml.gz.sqlite", NULL); 01609 const char * resultpath = 01610 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", 01611 *typep, ".sqlite", NULL); 01612 01613 /* rename from silly name to not silly name */ 01614 xx = Rename(tmp_result_path, resultpath); 01615 tmp_result_path = _free(tmp_result_path); 01616 result_compressed = 01617 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", 01618 *typep, ".sqlite.bz2", NULL); 01619 db_csums[*typep] = misc.checksum(algo2tagname(repo->algo), resultpath) 01620 01621 /* compress the files */ 01622 bzipFile(resultpath, result_compressed) 01623 /* csum the compressed file */ 01624 db_compressed_sums[*typep] = misc.checksum(algo2tagname(repo->algo), result_compressed) 01625 /* remove the uncompressed file */ 01626 xx = Unlink(resultpath); 01627 resultpath = _free(resultpath); 01628 } 01629 01630 if (repo->uniquemdfilenames) { 01631 const char * csum_result_compressed = 01632 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", 01633 db_compressed_sums[*typep], "-", *typep, ".sqlite.bz2", NULL); 01634 xx = Rename(result_compressed, csum_result_compressed); 01635 result_compressed = _free(result_compressed); 01636 result_compressed = csum_result_compressed; 01637 } 01638 01639 /* timestamp the compressed file */ 01640 (void) rpmioExists(result_compressed, st) 01641 db_timestamp = os.stat(result_compressed)[8] 01642 01643 /* add this data as a section to the repomdxml */ 01644 db_data_type = rpmExpand(*typep, "_db", NULL); 01645 data = reporoot.newChild(None, 'data', None) 01646 data.newProp('type', db_data_type) 01647 location = data.newChild(None, 'location', None) 01648 if (repo->baseurl != NULL) { 01649 location.newProp('xml:base', repo->baseurl) 01650 } 01651 01652 location.newProp('href', rpmGetPath(repo->finaldir, "/", *typep, ".sqlite.bz2", NULL)); 01653 checksum = data.newChild(None, 'checksum', db_compressed_sums[*typep]) 01654 checksum.newProp('type', algo2tagname(repo->algo)) 01655 db_tstamp = data.newChild(None, 'timestamp', str(db_timestamp)) 01656 unchecksum = data.newChild(None, 'open-checksum', db_csums[*typep]) 01657 unchecksum.newProp('type', algo2tagname(repo->algo)) 01658 database_version = data.newChild(None, 'database_version', dbversion) 01659 if (repo->verbose) { 01660 time_t now = time(NULL); 01661 repo_error(0, _("Ending %s db creation: %s"), 01662 *typep, ctime(&now)); 01663 } 01664 } 01665 01666 data = reporoot.newChild(None, 'data', None) 01667 data.newProp('type', *typep) 01668 01669 checksum = data.newChild(None, 'checksum', csum) 01670 checksum.newProp('type', algo2tagname(repo->algo)) 01671 timestamp = data.newChild(None, 'timestamp', str(timestamp)) 01672 unchecksum = data.newChild(None, 'open-checksum', uncsum) 01673 unchecksum.newProp('type', algo2tagname(repo->algo)) 01674 location = data.newChild(None, 'location', None) 01675 if (repo->baseurl != NULL) 01676 location.newProp('xml:base', repo->baseurl) 01677 if (repo->uniquemdfilenames) { 01678 orig_file = repoGetPath(repo, repo->tempdir, *typep, strcmp(*typep, "repomd")); 01679 res_file = rpmExpand(csum, "-", *typep, 01680 (repo->markup ? repo->markup : ""), 01681 (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""), NULL); 01682 dest_file = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", res_file, NULL); 01683 xx = Rename(orig_file, dest_file); 01684 01685 } else 01686 res_file = rpmExpand(*typep, 01687 (repo->markup ? repo->markup : ""), 01688 (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""), NULL); 01689 01690 location.newProp('href', rpmGetPath(repo->finaldir, "/", res_file, NULL)); 01691 } 01692 } 01693 01694 if (!repo->quiet && repo->database) 01695 repo_error(0, _("Sqlite DBs complete")); 01696 01697 if (repo->groupfile != NULL) { 01698 self.addArbitraryMetadata(repo->groupfile, 'group_gz', reporoot) 01699 self.addArbitraryMetadata(repo->groupfile, 'group', reporoot, compress=False) 01700 } 01701 01702 /* save it down */ 01703 try: 01704 repodoc.saveFormatFileEnc(fn, 'UTF-8', 1) 01705 except: 01706 repo_error(0, _("Error saving temp file for %s%s%s: %s"), 01707 rfile->type, 01708 (repo->markup ? repo->markup : ""), 01709 (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""), 01710 fn); 01711 repo_error(1, _("Could not save temp file: %s"), fn); 01712 01713 del repodoc 01714 #endif 01715 01716 return rc; 01717 } 01718 01724 static int repoDoFinalMove(rpmrepo repo) 01725 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/ 01726 /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/ 01727 { 01728 const char * output_final_dir = 01729 rpmGetPath(repo->outputdir, "/", repo->finaldir, NULL); 01730 const char * output_old_dir = 01731 rpmGetPath(repo->outputdir, "/", repo->olddir, NULL); 01732 const char * oldfile; 01733 struct stat sb, *st = &sb; 01734 int xx; 01735 01736 if (rpmioExists(output_final_dir, st)) { 01737 if ((xx = Rename(output_final_dir, output_old_dir)) != 0) 01738 repo_error(1, _("Error moving final %s to old dir %s"), 01739 output_final_dir, output_old_dir); 01740 } 01741 01742 { const char * output_temp_dir = 01743 rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL); 01744 if ((xx = Rename(output_temp_dir, output_final_dir)) != 0) { 01745 xx = Rename(output_old_dir, output_final_dir); 01746 repo_error(1, _("Error moving final metadata into place")); 01747 } 01748 output_temp_dir = _free(output_temp_dir); 01749 } 01750 01751 { /*@observer@*/ 01752 static const char * types[] = 01753 { "primary", "filelists", "other", "repomd", "group", NULL }; 01754 const char ** typep; 01755 01756 for (typep = types; *typep != NULL; typep++) { 01757 oldfile = rpmGetPath(output_old_dir, "/", *typep, 01758 (repo->markup != NULL ? repo->markup : ""), 01759 (repo->suffix != NULL && strcmp(*typep, "repomd") 01760 ? repo->suffix : ""), NULL); 01761 if (rpmioExists(oldfile, st)) { 01762 if (Unlink(oldfile)) 01763 repo_error(1, _("Could not remove old metadata file: %s: %s"), 01764 oldfile, strerror(errno)); 01765 } 01766 oldfile = _free(oldfile); 01767 } 01768 } 01769 01770 { DIR * dir = Opendir(output_old_dir); 01771 struct dirent * dp; 01772 01773 if (dir != NULL) { 01774 while ((dp = Readdir(dir)) != NULL) { 01775 const char * finalfile; 01776 01777 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 01778 continue; 01779 01780 finalfile = rpmGetPath(output_final_dir, "/", dp->d_name, NULL); 01781 oldfile = rpmGetPath(output_old_dir, "/", dp->d_name, NULL); 01782 01783 if (!strcmp(dp->d_name, "filelists.sqlite.bz2") 01784 || !strcmp(dp->d_name, "other.sqlite.bz2") 01785 || !strcmp(dp->d_name, "primary.sqlite.bz2")) 01786 { 01787 xx = Unlink(oldfile); 01788 oldfile = _free(oldfile); 01789 continue; 01790 } 01791 01792 if (rpmioExists(finalfile, st)) { 01793 if (!S_ISDIR(st->st_mode)) { 01794 if ((xx = Unlink(oldfile)) != 0) 01795 repo_error(1, _("Could not remove old metadata file: %s: %s"), 01796 oldfile, strerror(errno)); 01797 } 01798 #ifdef NOTYET 01799 else { 01800 shutil.rmtree(oldfile) 01801 } 01802 #endif 01803 } else { 01804 if ((xx = Rename(oldfile, finalfile)) != 0) { 01805 repo_error(1, _("Could not restore old non-metadata file: %s -> %s: %s"), 01806 oldfile, finalfile, strerror(errno)); 01807 } 01808 } 01809 oldfile = _free(oldfile); 01810 finalfile = _free(finalfile); 01811 } 01812 xx = Closedir(dir); 01813 } 01814 } 01815 01816 if ((xx = Rmdir(output_old_dir)) != 0) { 01817 repo_error(1, _("Could not remove old metadata dir: %s: %s"), 01818 repo->olddir, strerror(errno)); 01819 } 01820 output_old_dir = _free(output_old_dir); 01821 output_final_dir = _free(output_final_dir); 01822 01823 return 0; 01824 } 01825 01826 /*==============================================================*/ 01827 01830 static void repoArgCallback(poptContext con, 01831 /*@unused@*/ enum poptCallbackReason reason, 01832 const struct poptOption * opt, /*@unused@*/ const char * arg, 01833 /*@unused@*/ void * data) 01834 /*@globals _rpmrepo, fileSystem, internalState @*/ 01835 /*@modifies _rpmrepo, fileSystem, internalState @*/ 01836 { 01837 rpmrepo repo = _rpmrepo; 01838 01839 /* XXX avoid accidental collisions with POPT_BIT_SET for flags */ 01840 if (opt->arg == NULL) 01841 switch (opt->val) { 01842 01843 case 'v': /* --verbose */ 01844 repo->verbose++; 01845 break; 01846 case '?': 01847 default: 01848 fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val); 01849 poptPrintUsage(con, stderr, 0); 01850 exit(EXIT_FAILURE); 01851 /*@notreached@*/ break; 01852 } 01853 } 01854 01855 /*@unchecked@*/ 01856 static int compression = -1; 01857 01858 /*@unchecked@*/ /*@observer@*/ 01859 static struct poptOption repoCompressionPoptTable[] = { 01860 { "uncompressed", '\0', POPT_ARG_VAL, &compression, 0, 01861 N_("don't compress"), NULL }, 01862 { "gzip", 'Z', POPT_ARG_VAL, &compression, 1, 01863 N_("use gzip compression"), NULL }, 01864 { "bzip2", '\0', POPT_ARG_VAL, &compression, 2, 01865 N_("use bzip2 compression"), NULL }, 01866 { "lzma", '\0', POPT_ARG_VAL, &compression, 3, 01867 N_("use lzma compression"), NULL }, 01868 { "xz", '\0', POPT_ARG_VAL, &compression, 4, 01869 N_("use xz compression"), NULL }, 01870 POPT_TABLEEND 01871 }; 01872 01873 /*@unchecked@*/ /*@observer@*/ 01874 static struct poptOption optionsTable[] = { 01875 /*@-type@*/ /* FIX: cast? */ 01876 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE, 01877 repoArgCallback, 0, NULL, NULL }, 01878 /*@=type@*/ 01879 01880 { "repodebug", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &_repo_debug, -1, 01881 N_("debug repo handling"), NULL }, 01882 01883 { "quiet", 'q', POPT_ARG_VAL, &__rpmrepo.quiet, 0, 01884 N_("output nothing except for serious errors"), NULL }, 01885 { "verbose", 'v', 0, NULL, (int)'v', 01886 N_("output more debugging info."), NULL }, 01887 { "dryrun", '\0', POPT_ARG_VAL, &__rpmrepo.dryrun, 1, 01888 N_("sanity check arguments, don't create metadata"), NULL }, 01889 { "excludes", 'x', POPT_ARG_ARGV, &__rpmrepo.exclude_patterns, 0, 01890 N_("glob PATTERN(s) to exclude"), N_("PATTERN") }, 01891 { "includes", 'i', POPT_ARG_ARGV, &__rpmrepo.include_patterns, 0, 01892 N_("glob PATTERN(s) to include"), N_("PATTERN") }, 01893 { "basedir", '\0', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.basedir, 0, 01894 N_("top level directory"), N_("DIR") }, 01895 { "baseurl", 'u', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.baseurl, 0, 01896 N_("baseurl to append on all files"), N_("BASEURL") }, 01897 #ifdef NOTYET 01898 { "groupfile", 'g', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.groupfile, 0, 01899 N_("path to groupfile to include in metadata"), N_("FILE") }, 01900 #endif 01901 { "pretty", 'p', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.pretty, 1, 01902 N_("make sure all xml generated is formatted"), NULL }, 01903 { "checkts", 'C', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.checkts, 1, 01904 N_("check timestamps on files vs the metadata to see if we need to update"), NULL }, 01905 #if defined(WITH_SQLITE) 01906 { "database", 'd', POPT_ARG_VAL, &__rpmrepo.database, 1, 01907 N_("create sqlite3 database files"), NULL }, 01908 #endif 01909 { "split", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.split, 1, 01910 N_("generate split media"), NULL }, 01911 { "pkglist", 'l', POPT_ARG_ARGV|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.manifests, 0, 01912 N_("use only the files listed in this file from the directory specified"), N_("FILE") }, 01913 { "outputdir", 'o', POPT_ARG_STRING, &__rpmrepo.outputdir, 0, 01914 N_("<dir> = optional directory to output to"), N_("DIR") }, 01915 { "skip-symlinks", 'S', POPT_ARG_VAL, &__rpmrepo.nofollow, 1, 01916 N_("ignore symlinks of packages"), NULL }, 01917 { "unique-md-filenames", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.uniquemdfilenames, 1, 01918 N_("include the file's checksum in the filename, helps with proxies"), NULL }, 01919 01920 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0, 01921 N_("Fts(3) traversal options:"), NULL }, 01922 01923 #ifdef NOTYET 01924 { "version", '\0', 0, NULL, POPT_SHOWVERSION, 01925 N_("print the version"), NULL }, 01926 #endif 01927 01928 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, repoCompressionPoptTable, 0, 01929 N_("Available compressions:"), NULL }, 01930 01931 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0, 01932 N_("Available digests:"), NULL }, 01933 01934 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0, 01935 N_("Common options for all rpmio executables:"), 01936 NULL }, 01937 01938 POPT_AUTOALIAS 01939 POPT_AUTOHELP 01940 POPT_TABLEEND 01941 01942 }; 01943 01944 int 01945 main(int argc, char *argv[]) 01946 /*@globals _rpmrepo, 01947 rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 01948 /*@modifies _rpmrepo, 01949 rpmGlobalMacroContext, fileSystem, internalState @*/ 01950 { 01951 rpmrepo repo = _rpmrepo; 01952 poptContext optCon; 01953 const char ** av = NULL; 01954 int ndirs = 0; 01955 int nfiles = 0; 01956 int rc = 1; /* assume failure. */ 01957 int xx; 01958 int i; 01959 01960 #if !defined(__LCLINT__) /* XXX force "rpmrepo" name. */ 01961 __progname = "rpmrepo"; 01962 #endif 01963 01964 /* Process options. */ 01965 optCon = rpmioInit(argc, argv, optionsTable); 01966 01967 repo->ftsoptions = (rpmioFtsOpts ? rpmioFtsOpts : FTS_PHYSICAL); 01968 switch (repo->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)) { 01969 case (FTS_LOGICAL|FTS_PHYSICAL): 01970 repo_error(1, "FTS_LOGICAL and FTS_PYSICAL are mutually exclusive"); 01971 /*@notreached@*/ break; 01972 case 0: 01973 repo->ftsoptions |= FTS_PHYSICAL; 01974 break; 01975 } 01976 01977 repo->algo = (rpmioDigestHashAlgo >= 0 ? (rpmioDigestHashAlgo & 0xff) : PGPHASHALGO_SHA1); 01978 01979 repo->compression = (compression >= 0 ? compression : 1); 01980 switch (repo->compression) { 01981 case 0: 01982 repo->suffix = NULL; 01983 repo->wmode = "w.ufdio"; 01984 break; 01985 default: 01986 /*@fallthrough@*/ 01987 case 1: 01988 repo->suffix = ".gz"; 01989 repo->wmode = "w9.gzdio"; 01990 break; 01991 case 2: 01992 repo->suffix = ".bz2"; 01993 repo->wmode = "w9.bzdio"; 01994 break; 01995 case 3: 01996 repo->suffix = ".lzma"; 01997 repo->wmode = "w.lzdio"; 01998 break; 01999 case 4: 02000 repo->suffix = ".xz"; 02001 repo->wmode = "w.xzdio"; 02002 break; 02003 } 02004 02005 av = poptGetArgs(optCon); 02006 if (av == NULL || av[0] == NULL) { 02007 repo_error(0, _("Must specify path(s) to index.")); 02008 poptPrintUsage(optCon, stderr, 0); 02009 goto exit; 02010 } 02011 02012 if (av != NULL) 02013 for (i = 0; av[i] != NULL; i++) { 02014 char fullpath[MAXPATHLEN]; 02015 struct stat sb; 02016 const char * rpath; 02017 const char * lpath = NULL; 02018 int ut = urlPath(av[i], &lpath); 02019 size_t nb = (size_t)(lpath - av[i]); 02020 int isdir = (lpath[strlen(lpath)-1] == '/'); 02021 02022 /* Convert to absolute/clean/malloc'd path. */ 02023 if (lpath[0] != '/') { 02024 if ((rpath = repoRealpath(lpath)) == NULL) 02025 repo_error(1, _("Realpath(%s): %s"), lpath, strerror(errno)); 02026 lpath = rpmGetPath(rpath, NULL); 02027 rpath = _free(rpath); 02028 } else 02029 lpath = rpmGetPath(lpath, NULL); 02030 02031 /* Reattach the URI to the absolute/clean path. */ 02032 /* XXX todo: rpmGenPath was confused by file:///path/file URI's. */ 02033 switch (ut) { 02034 case URL_IS_DASH: 02035 case URL_IS_UNKNOWN: 02036 rpath = lpath; 02037 lpath = NULL; 02038 /*@switchbreak@*/ break; 02039 default: 02040 assert(nb < sizeof(fullpath)); 02041 strncpy(fullpath, av[i], nb); 02042 fullpath[nb] = '\0'; 02043 rpath = rpmGenPath(fullpath, lpath, NULL); 02044 lpath = _free(lpath); 02045 /*@switchbreak@*/ break; 02046 } 02047 02048 /* Add a trailing '/' on directories. */ 02049 lpath = (isdir || (!Stat(rpath, &sb) && S_ISDIR(sb.st_mode)) 02050 ? "/" : NULL); 02051 if (lpath != NULL) { 02052 lpath = rpmExpand(rpath, lpath, NULL); 02053 xx = argvAdd(&repo->directories, lpath); 02054 lpath = _free(lpath); 02055 ndirs++; 02056 } else { 02057 xx = argvAdd(&repo->pkglist, rpath); 02058 nfiles++; 02059 } 02060 rpath = _free(rpath); 02061 } 02062 02063 if (_repo_debug || repo->dryrun) 02064 argvPrint("repo->directories", repo->directories, NULL); 02065 02066 #ifdef NOTYET 02067 if (repo->basedir == NULL) 02068 repo->basedir = xstrdup(repo->directories[0]); 02069 #endif 02070 02071 if (repo->outputdir == NULL) { 02072 if (repo->directories != NULL && repo->directories[0] != NULL) 02073 repo->outputdir = xstrdup(repo->directories[0]); 02074 else { 02075 repo->outputdir = repoRealpath("."); 02076 if (repo->outputdir == NULL) 02077 repo_error(1, _("Realpath(%s): %s"), ".", strerror(errno)); 02078 } 02079 } 02080 02081 if (repo->split && repo->checkts) 02082 repo_error(1, _("--split and --checkts options are mutually exclusive")); 02083 02084 #ifdef NOTYET 02085 /* Add manifest(s) contents to rpm list. */ 02086 if (repo->manifests != NULL) { 02087 const char ** av = repo->manifests; 02088 const char * fn; 02089 /* Load the rpm list from manifest(s). */ 02090 while ((fn = *av++) != NULL) { 02091 /* XXX todo: parse paths from files. */ 02092 /* XXX todo: convert to absolute paths. */ 02093 /* XXX todo: check for existence. */ 02094 xx = argvAdd(&repo->pkglist, fn); 02095 } 02096 } 02097 #endif 02098 02099 /* Set up mire patterns (no error returns with globs, easy pie). */ 02100 if (mireLoadPatterns(RPMMIRE_GLOB, 0, repo->exclude_patterns, NULL, 02101 &repo->excludeMire, &repo->nexcludes)) 02102 repo_error(1, _("Error loading exclude glob patterns.")); 02103 if (mireLoadPatterns(RPMMIRE_GLOB, 0, repo->include_patterns, NULL, 02104 &repo->includeMire, &repo->nincludes)) 02105 repo_error(1, _("Error loading include glob patterns.")); 02106 02107 /* Load the rpm list from a multi-rooted directory traversal. */ 02108 if (repo->directories != NULL) { 02109 ARGV_t pkglist = repoGetFileList(repo, repo->directories, ".rpm"); 02110 xx = argvAppend(&repo->pkglist, pkglist); 02111 pkglist = argvFree(pkglist); 02112 } 02113 02114 /* XXX todo: check for duplicates in repo->pkglist? */ 02115 xx = argvSort(repo->pkglist, NULL); 02116 02117 if (_repo_debug || repo->dryrun) 02118 argvPrint("repo->pkglist", repo->pkglist, NULL); 02119 02120 repo->pkgcount = argvCount(repo->pkglist); 02121 02122 /* XXX enable --stats using transaction set. */ 02123 _rpmts_stats = _rpmsw_stats; 02124 repo->ts = rpmtsCreate(); 02125 02126 /* XXX todo wire up usual rpm CLI options. hotwire --nosignature for now */ 02127 (void) rpmtsSetVSFlags(repo->ts, _RPMVSF_NOSIGNATURES); 02128 02129 rc = repoTestSetupDirs(repo); 02130 02131 if (rc || repo->dryrun) 02132 goto exit; 02133 02134 if (!repo->split) { 02135 rc = repoCheckTimeStamps(repo); 02136 if (rc == 0) { 02137 fprintf(stdout, _("repo is up to date\n")); 02138 goto exit; 02139 } 02140 } 02141 02142 if ((rc = repoDoPkgMetadata(repo)) != 0) 02143 goto exit; 02144 if ((rc = repoDoRepoMetadata(repo)) != 0) 02145 goto exit; 02146 if ((rc = repoDoFinalMove(repo)) != 0) 02147 goto exit; 02148 02149 exit: 02150 (void)rpmtsFree(repo->ts); 02151 repo->ts = NULL; 02152 repo->primary.digest = _free(repo->primary.digest); 02153 repo->primary.Zdigest = _free(repo->primary.Zdigest); 02154 repo->filelists.digest = _free(repo->filelists.digest); 02155 repo->filelists.Zdigest = _free(repo->filelists.Zdigest); 02156 repo->other.digest = _free(repo->other.digest); 02157 repo->other.Zdigest = _free(repo->other.Zdigest); 02158 repo->repomd.digest = _free(repo->repomd.digest); 02159 repo->repomd.Zdigest = _free(repo->repomd.Zdigest); 02160 repo->outputdir = _free(repo->outputdir); 02161 repo->pkglist = argvFree(repo->pkglist); 02162 repo->directories = argvFree(repo->directories); 02163 repo->manifests = argvFree(repo->manifests); 02164 /*@-onlytrans -refcounttrans @*/ 02165 repo->excludeMire = mireFreeAll(repo->excludeMire, repo->nexcludes); 02166 repo->includeMire = mireFreeAll(repo->includeMire, repo->nincludes); 02167 /*@=onlytrans =refcounttrans @*/ 02168 repo->exclude_patterns = argvFree(repo->exclude_patterns); 02169 repo->include_patterns = argvFree(repo->include_patterns); 02170 02171 tagClean(NULL); 02172 optCon = rpmioFini(optCon); 02173 02174 return rc; 02175 }