rpm
5.2.1
|
00001 #include "system.h" 00002 /*@unchecked@*/ 00003 extern const char * __progname; 00004 00005 #define _RPMIOB_INTERNAL 00006 #include <rpmiotypes.h> 00007 #include <rpmio_internal.h> /* XXX fdGetFILE */ 00008 #include <poptIO.h> 00009 #include "debug.h" 00010 00011 static int _rpmdc_debug = 0; 00012 00013 /* XXX older 0install manifest format. */ 00014 static int _old_0install = 0; 00015 00016 #define _KFB(n) (1U << (n)) 00017 #define _DFB(n) (_KFB(n) | 0x40000000) 00018 00019 #define F_ISSET(_dc, _FLAG) ((_dc)->flags & ((RPMDC_FLAGS_##_FLAG) & ~0x40000000)) 00020 00024 enum dcFlags_e { 00025 RPMDC_FLAGS_NONE = 0, 00026 /* 0 reserved */ 00027 RPMDC_FLAGS_WARN = _DFB( 1), 00028 RPMDC_FLAGS_CREATE = _DFB( 2), 00029 RPMDC_FLAGS_DIRSONLY = _DFB( 3), 00030 /* 4-13 reserved */ 00031 RPMDC_FLAGS_BINARY = _DFB(14), 00032 RPMDC_FLAGS_STATUS = _DFB(15), 00033 RPMDC_FLAGS_0INSTALL = _DFB(16) 00034 /* 17-31 unused */ 00035 }; 00036 00039 typedef struct rpmdc_s * rpmdc; 00040 00043 struct rpmdc_s { 00044 int ftsoptions; 00045 FTS * t; 00046 FTSENT * p; 00047 struct stat sb; 00049 enum dcFlags_e flags; 00050 uint32_t algo; 00051 uint32_t dalgo; 00052 /*@observer@*/ /*@null@*/ 00053 const char * dalgoName; 00054 const char * digest; 00055 size_t digestlen; 00056 const char * fn; 00057 FD_t fd; 00058 int (*parse) (rpmdc dc); 00059 const char * (*print) (rpmdc dc, int rc); 00060 const char * ofn; 00061 FD_t ofd; 00062 uint32_t oalgo; 00063 const char * oalgoName; 00064 ARGV_t manifests; 00065 ARGI_t algos; 00066 ARGV_t digests; 00067 ARGV_t paths; 00068 unsigned char buf[BUFSIZ]; 00069 ssize_t nb; 00070 int ix; 00071 00072 size_t ncomputed; 00073 size_t nchecked; 00074 size_t nmatched; 00075 size_t nfailed; 00076 struct rpmop_s totalops; 00077 struct rpmop_s readops; 00078 struct rpmop_s digestops; 00079 }; 00080 00083 static struct rpmdc_s _dc = { 00084 .ftsoptions = FTS_PHYSICAL, 00085 .flags = RPMDC_FLAGS_CREATE 00086 }; 00087 00090 static rpmdc dc = &_dc; 00091 00092 /*==============================================================*/ 00093 static uint32_t rpmdcName2Algo(const char * dname) 00094 /*@*/ 00095 { 00096 struct poptOption * opt = rpmioDigestPoptTable; 00097 uint32_t dalgo = 0xffffffff; 00098 00099 /* XXX compatible with 0install legacy derangement. bug imho. */ 00100 if (!strcmp(dname, "sha1new")) 00101 dname = "sha1"; 00102 00103 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) { 00104 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) 00105 continue; 00106 if (opt->longName == NULL) 00107 continue; 00108 if (!(opt->val > 0 && opt->val < 256)) 00109 continue; 00110 if (strcmp(opt->longName, dname)) 00111 continue; 00112 dalgo = (uint32_t) opt->val; 00113 break; 00114 } 00115 return dalgo; 00116 } 00117 00118 /*@null@*/ 00119 static const char * rpmdcAlgo2Name(uint32_t dalgo) 00120 /*@*/ 00121 { 00122 struct poptOption * opt = rpmioDigestPoptTable; 00123 const char * dalgoName = NULL; 00124 00125 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) { 00126 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) 00127 continue; 00128 if (opt->longName == NULL) 00129 continue; 00130 if (!(opt->val > 0 && opt->val < 256)) 00131 continue; 00132 if ((uint32_t)opt->val != dalgo) 00133 continue; 00134 dalgoName = opt->longName; 00135 break; 00136 } 00137 return dalgoName; 00138 } 00139 00140 /*==============================================================*/ 00141 00142 static int rpmdcParseCoreutils(rpmdc dc) 00143 /*@globals h_errno, fileSystem, internalState @*/ 00144 /*@modifies h_errno, fileSystem, internalState @*/ 00145 { 00146 int rc = -1; /* assume failure */ 00147 00148 if (dc->manifests != NULL) /* note rc=0 return with no files to load. */ 00149 while ((dc->fn = *dc->manifests++) != NULL) { 00150 char buf[BUFSIZ]; 00151 unsigned lineno; 00152 FILE *fp; 00153 00154 if (strcmp(dc->fn, "-") == 0) { 00155 dc->fd = NULL; 00156 fp = stdin; 00157 } else { 00158 /* XXX .fpio is needed because of fgets(3) usage. */ 00159 dc->fd = Fopen(dc->fn, "r.fpio"); 00160 if (dc->fd == NULL || Ferror(dc->fd) || (fp = fdGetFILE(dc->fd)) == NULL) { 00161 fprintf(stderr, _("%s: Failed to open %s: %s\n"), 00162 __progname, dc->fn, Fstrerror(dc->fd)); 00163 if (dc->fd != NULL) (void) Fclose(dc->fd); 00164 dc->fd = NULL; 00165 fp = NULL; 00166 goto exit; 00167 } 00168 } 00169 00170 lineno = 0; 00171 while (fgets(buf, sizeof(buf), fp) != NULL) { 00172 const char * dname, * digest, * path; 00173 char *se = buf + (int)strlen(buf); 00174 int c, xx; 00175 00176 lineno++; 00177 while (se > buf && xisspace((int)se[-1])) 00178 se--; 00179 *se = '\0'; 00180 00181 /* Skip blank lines */ 00182 if (buf[0] == '\0') /*@innercontinue@*/ continue; 00183 /* Skip comment lines */ 00184 if (buf[0] == '#') /*@innercontinue@*/ continue; 00185 00186 /* Parse "[algo:]digest [* ]path" line. */ 00187 dname = NULL; path = NULL; 00188 for (digest = se = buf; (c = (int)*se) != 0; se++) 00189 switch (c) { 00190 default: 00191 /*@switchbreak@*/ break; 00192 case ':': 00193 *se++ = '\0'; 00194 dname = digest; 00195 digest = se; 00196 /*@switchbreak@*/ break; 00197 case ' ': 00198 se[0] = '\0'; /* loop will terminate */ 00199 if (se[1] == ' ' || se[1] == '*') 00200 se[1] = '\0'; 00201 path = se + 2; 00202 /*@switchbreak@*/ break; 00203 } 00204 if (path == NULL) { 00205 fprintf(stderr, _("%s: %s line %u: No file path found.\n"), 00206 __progname, dc->fn, lineno); 00207 rc = 2; 00208 goto exit; 00209 } 00210 00211 /* Map name to algorithm number. */ 00212 if (dname) { 00213 if ((dc->dalgo = rpmdcName2Algo(dname)) != 0xffffffff) 00214 dc->dalgoName = xstrdup(dname); 00215 if (dc->dalgo == 0xffffffff) { 00216 fprintf(stderr, _("%s: %s line %u: Unknown digest name \"%s\"\n"), 00217 __progname, dc->fn, lineno, dname); 00218 rc = 2; 00219 goto exit; 00220 } 00221 } else 00222 dc->dalgo = dc->algo; 00223 00224 /* Save {algo, digest, path} for processing. */ 00225 xx = argiAdd(&dc->algos, -1, dc->dalgo); 00226 xx = argvAdd(&dc->digests, digest); 00227 xx = argvAdd(&dc->paths, path); 00228 } 00229 00230 if (dc->fd != NULL) { 00231 (void) Fclose(dc->fd); 00232 dc->fd = NULL; 00233 } 00234 } 00235 rc = 0; 00236 00237 exit: 00238 return rc; 00239 } 00240 00241 /*@null@*/ 00242 static const char * rpmdcPrintCoreutils(rpmdc dc, int rc) 00243 { 00244 const char *msg = (rc ? "FAILED" : "OK"); 00245 char * t, * te; 00246 size_t nb = 0; 00247 00248 /* Don't bother formatting if noone cares. */ 00249 if (rc == 0 && F_ISSET(dc, STATUS)) 00250 return NULL; 00251 00252 /* Calculate size of message. */ 00253 if (dc->dalgoName != NULL) 00254 nb += strlen(dc->dalgoName) + sizeof(":") - 1; 00255 assert(dc->digest != NULL); 00256 if (dc->digest != NULL && dc->digestlen > 0) 00257 nb += dc->digestlen; 00258 nb += sizeof(" *") - 1; 00259 if (dc->fn != NULL) 00260 nb += strlen(dc->fn); 00261 nb += strlen(msg); 00262 nb += sizeof("\n"); /* XXX trailing NUL */ 00263 00264 /* Compose the message. */ 00265 te = t = xmalloc(nb); 00266 *te = '\0'; 00267 00268 if (dc->manifests) { 00269 if (rc || !F_ISSET(dc, STATUS)) { 00270 if (dc->fn) 00271 te = stpcpy( stpcpy(te, dc->fn), ": "); 00272 te = stpcpy(te, msg); 00273 *te++ = '\n'; 00274 } 00275 } else { 00276 if (dc->dalgoName) 00277 te = stpcpy( stpcpy(te, dc->dalgoName), ":"); 00278 te = stpcpy(te, dc->digest); 00279 *te++ = ' '; 00280 *te++ = (F_ISSET(dc, BINARY) ? '*' : ' '); 00281 te = stpcpy(te, dc->fn); 00282 *te++ = '\n'; 00283 } 00284 *te = '\0'; 00285 00286 return t; 00287 } 00288 00289 /*==============================================================*/ 00290 00291 static int rpmdcParseZeroInstall(rpmdc dc) 00292 /*@globals h_errno, fileSystem, internalState @*/ 00293 /*@modifies h_errno, fileSystem, internalState @*/ 00294 { 00295 int rc = 0; /* assume success */ 00296 00297 if (dc->manifests != NULL) /* note rc=0 return with no files to load. */ 00298 while ((dc->fn = *dc->manifests++) != NULL) { 00299 unsigned lineno; 00300 char * be; 00301 rpmiob iob = NULL; 00302 int xx = rpmiobSlurp(dc->fn, &iob); 00303 const char * digest; 00304 char * f; 00305 char * fe; 00306 00307 if (!(xx == 0 && iob != NULL)) { 00308 fprintf(stderr, _("%s: Failed to open %s\n"), __progname, dc->fn); 00309 rc = -1; 00310 goto bottom; 00311 } 00312 00313 be = (char *)(iob->b + iob->blen); 00314 while (be > (char *)iob->b && (be[-1] == '\n' || be[-1] == '\r')) { 00315 be--; 00316 *be = '\0'; 00317 } 00318 00319 /* Parse "algo=digest" from last line. */ 00320 be = strrchr((char *)iob->b, '='); 00321 if (be == NULL) { 00322 fprintf(stderr, 00323 _("%s: %s: Manifest needs \"algo=digest\" as last line\n"), 00324 __progname, dc->fn); 00325 rc = 2; 00326 goto bottom; 00327 } 00328 *be = '\0'; 00329 dc->digest = be + 1; 00330 while (be > (char *)iob->b && !(be[-1] == '\n' || be[-1] == '\r')) 00331 be--; 00332 if (be <= (char *)iob->b) { 00333 fprintf(stderr, _("%s: %s: Manifest is empty\n"), 00334 __progname, dc->fn); 00335 rc = 2; 00336 goto bottom; 00337 } 00338 00339 /* Map name to algorithm number. */ 00340 if ((dc->dalgo = rpmdcName2Algo(be)) == 0xffffffff) { 00341 fprintf(stderr, _("%s: %s: Unknown digest algo name \"%s\"\n"), 00342 __progname, dc->fn, be); 00343 rc = 2; 00344 goto bottom; 00345 } 00346 *be = '\0'; 00347 00348 /* Verify the manifest digest. */ 00349 { DIGEST_CTX ctx = rpmDigestInit(dc->dalgo, 0); 00350 00351 (void) rpmDigestUpdate(ctx, (char *)iob->b, (be - (char *)iob->b)); 00352 digest = NULL; 00353 (void) rpmDigestFinal(ctx, &digest, NULL, 1); 00354 if (strcmp(dc->digest, digest)) { 00355 fprintf(stderr, 00356 _("%s: %s: Manifest digest check: Expected(%s) != (%s)\n"), 00357 __progname, dc->fn, dc->digest, digest); 00358 rc = 2; 00359 goto bottom; 00360 } 00361 digest = _free(digest); 00362 } 00363 00364 /* Parse and save manifest items. */ 00365 lineno = 0; 00366 for (f = (char *)iob->b; *f; f = fe) { 00367 static const char hexdigits[] = "0123456789ABCDEFabcdef"; 00368 const char * _dn = NULL; 00369 const char * path; 00370 00371 lineno++; 00372 fe = f; 00373 while (*fe && !(*fe == '\n' || *fe == '\r')) 00374 fe++; 00375 while (*fe && (*fe == '\n' || *fe == '\r')) 00376 *fe++ = '\0'; 00377 switch ((int)*f) { 00378 case 'D': 00379 _dn = f + 2; 00380 continue; 00381 /*@notreached@*/ break; 00382 case 'F': 00383 case 'S': 00384 case 'X': 00385 digest = f + 2; 00386 f += 2; 00387 while (*f && strchr(hexdigits, *f) != NULL) 00388 f++; 00389 if (*f != ' ') { 00390 fprintf(stderr, _("%s: %s line %u: Malformed digest field.\n"), 00391 __progname, dc->fn, lineno); 00392 rc = 2; 00393 goto bottom; 00394 } 00395 *f++ = '\0'; 00396 while (*f && xisdigit(*f)) 00397 f++; 00398 if (*f != ' ') { 00399 fprintf(stderr, _("%s: %s line %u: Malformed mtime field.\n"), 00400 __progname, dc->fn, lineno); 00401 rc = 2; 00402 goto bottom; 00403 } 00404 *f++ = '\0'; 00405 while (*f && xisdigit(*f)) 00406 f++; 00407 if (*f != ' ') { 00408 fprintf(stderr, _("%s: %s line %u: Malformed size field.\n"), 00409 __progname, dc->fn, lineno); 00410 rc = 2; 00411 goto bottom; 00412 } 00413 *f++ = '\0'; 00414 if (*f == '\0') { 00415 fprintf(stderr, _("%s: %s line %u: No file path.\n"), 00416 __progname, dc->fn, lineno); 00417 rc = 2; 00418 goto bottom; 00419 } 00420 00421 if (_dn && *_dn == '/') 00422 path = rpmExpand(_dn+1, "/", f, NULL); 00423 else 00424 path = xstrdup(f); 00425 00426 /* Save {algo, digest, path} for processing. */ 00427 xx = argiAdd(&dc->algos, -1, dc->dalgo); 00428 xx = argvAdd(&dc->digests, digest); 00429 xx = argvAdd(&dc->paths, path); 00430 path = _free(path); 00431 break; 00432 } 00433 } 00434 00435 bottom: 00436 iob = rpmiobFree(iob); 00437 if (rc != 0) 00438 goto exit; 00439 } 00440 00441 exit: 00442 return rc; 00443 } 00444 00445 /*@null@*/ 00446 static const char * rpmdcPrintZeroInstall(rpmdc dc, int rc) 00447 { 00448 char * t, * te; 00449 size_t nb = 0; 00450 char _mtime[32]; 00451 char _size[32]; 00452 const struct stat * st = &dc->sb; 00453 const char * _bn; 00454 00455 /* Don't bother formatting if noone cares. */ 00456 if (rc == 0 && F_ISSET(dc, STATUS)) 00457 return NULL; 00458 00459 snprintf(_mtime, sizeof(_mtime), "%llu", 00460 (unsigned long long) st->st_mtime); 00461 _mtime[sizeof(_mtime)-1] = '\0'; 00462 snprintf(_size, sizeof(_size), "%llu", 00463 (unsigned long long) st->st_size); 00464 _size[sizeof(_size)-1] = '\0'; 00465 if ((_bn = strrchr(dc->fn, '/')) != NULL) 00466 _bn++; 00467 else 00468 _bn = dc->fn; 00469 00470 /* Calculate size of message. */ 00471 nb += sizeof("F"); 00472 if (dc->digest != NULL && dc->digestlen > 0) 00473 nb += 1 + dc->digestlen; 00474 nb += 1 + strlen(_mtime); 00475 nb += 1 + strlen(_size); 00476 nb += 1 + strlen(_bn); 00477 nb += sizeof("\n"); /* XXX trailing NUL */ 00478 00479 /* Compose the message. */ 00480 te = t = xmalloc(nb); 00481 *te = '\0'; 00482 00483 if (dc->manifests) { 00484 const char *msg = (rc ? "FAILED" : "OK"); 00485 if (rc || !F_ISSET(dc, STATUS)) { 00486 if (dc->fn) 00487 te = stpcpy( stpcpy(te, dc->fn), ": "); 00488 te = stpcpy(te, msg); 00489 *te++ = '\n'; 00490 } 00491 } else { 00492 if (S_ISDIR(st->st_mode)) { 00493 *te++ = 'D'; 00494 if (_old_0install) { 00495 *te++ = ' '; 00496 te = stpcpy(te, _mtime); 00497 } 00498 *te++ = ' '; 00499 *te++ = '/'; 00500 te = stpcpy(te, _bn); 00501 *te++ = '\n'; 00502 } else if (S_ISREG(st->st_mode) || S_ISLNK(st->st_mode)) { 00503 if (S_ISLNK(st->st_mode)) 00504 *te++ = 'S'; 00505 else 00506 *te++ = (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ? 'X' : 'F'; 00507 *te++ = ' '; 00508 te = stpcpy(te, dc->digest); 00509 *te++ = ' '; 00510 te = stpcpy(te, _mtime); 00511 *te++ = ' '; 00512 te = stpcpy(te, _size); 00513 *te++ = ' '; 00514 te = stpcpy(te, _bn); 00515 *te++ = '\n'; 00516 } 00517 } 00518 *te = '\0'; 00519 00520 return t; 00521 } 00522 00523 /*==============================================================*/ 00524 00525 static int rpmdcPrintFile(rpmdc dc) 00526 { 00527 static int asAscii = 1; 00528 int rc = 0; 00529 00530 if (_rpmdc_debug) 00531 fprintf(stderr, "\trpmdcPrintFile(%p) fd %p fn %s\n", dc, dc->fd, dc->fn); 00532 00533 assert(dc->fd != NULL); 00534 fdFiniDigest(dc->fd, dc->dalgo, &dc->digest, &dc->digestlen, asAscii); 00535 assert(dc->digest != NULL); 00536 dc->ncomputed++; 00537 00538 if (dc->manifests) { 00539 dc->nchecked++; 00540 if ((rc = strcmp(dc->digest, dc->digests[dc->ix])) != 0) 00541 dc->nfailed++; 00542 else 00543 dc->nmatched++; 00544 } 00545 00546 { const char * t = (*dc->print) (dc, rc); 00547 if (dc->ofd && t && *t) { 00548 size_t nb = strlen(t); 00549 nb = Fwrite(t, nb, sizeof(*t), dc->ofd); 00550 (void) Fflush(dc->ofd); 00551 } 00552 t = _free(t); 00553 } 00554 00555 dc->digest = _free(dc->digest); 00556 dc->digestlen = 0; 00557 return rc; 00558 } 00559 00560 static int rpmdcFiniFile(rpmdc dc) 00561 { 00562 uint32_t dalgo = (dc->manifests ? dc->algos->vals[dc->ix] : dc->algo); 00563 int rc = 0; 00564 int xx; 00565 00566 /* Only regular files have dc->fd != NULL here. Skip all other paths. */ 00567 if (dc->fd == NULL) 00568 return rc; 00569 00570 if (_rpmdc_debug) 00571 fprintf(stderr, "\trpmdcFiniFile(%p) fn %s\n", dc, dc->fn); 00572 switch (dalgo) { 00573 default: 00574 dc->dalgo = dalgo; 00575 dc->dalgoName = NULL; 00576 xx = rpmdcPrintFile(dc); 00577 if (xx) rc = xx; 00578 break; 00579 case 256: /* --all digests requested. */ 00580 { struct poptOption * opt = rpmioDigestPoptTable; 00581 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) { 00582 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) 00583 continue; 00584 if (opt->arg != (void *)&rpmioDigestHashAlgo) 00585 continue; 00586 dc->dalgo = opt->val; 00587 if (!(dc->dalgo > 0 && dc->dalgo < 256)) 00588 continue; 00589 dc->dalgoName = opt->longName; 00590 xx = rpmdcPrintFile(dc); 00591 if (xx) rc = xx; 00592 } 00593 } break; 00594 } 00595 00596 (void) rpmswAdd(&dc->readops, fdstat_op(dc->fd, FDSTAT_READ)); 00597 (void) rpmswAdd(&dc->digestops, fdstat_op(dc->fd, FDSTAT_DIGEST)); 00598 Fclose(dc->fd); 00599 dc->fd = NULL; 00600 00601 return rc; 00602 } 00603 00604 static int rpmdcCalcFile(rpmdc dc) 00605 { 00606 int rc = 0; 00607 00608 if (_rpmdc_debug) 00609 fprintf(stderr, "\trpmdcCalcFile(%p) fn %s\n", dc, dc->fn); 00610 /* Skip (unopened) non-files. */ 00611 if (dc->fd != NULL) 00612 do { 00613 dc->nb = Fread(dc->buf, sizeof(dc->buf[0]), sizeof(dc->buf), dc->fd); 00614 if (Ferror(dc->fd)) { 00615 rc = 2; 00616 break; 00617 } 00618 } while (dc->nb > 0); 00619 00620 return rc; 00621 } 00622 00623 static int rpmdcInitFile(rpmdc dc) 00624 { 00625 int rc = 0; 00626 00627 if (_rpmdc_debug) 00628 fprintf(stderr, "\trpmdcInitFile(%p) fn %s\n", dc, dc->fn); 00629 /* Skip non-files. */ 00630 if (!S_ISREG(dc->sb.st_mode)) { 00631 /* XXX not found return code? */ 00632 goto exit; 00633 } 00634 00635 dc->fd = Fopen(dc->fn, "r.ufdio"); 00636 if (dc->fd == NULL || Ferror(dc->fd)) { 00637 fprintf(stderr, _("open of %s failed: %s\n"), dc->fn, Fstrerror(dc->fd)); 00638 if (dc->fd != NULL) Fclose(dc->fd); 00639 dc->fd = NULL; 00640 rc = 2; 00641 goto exit; 00642 } 00643 00644 switch (dc->algo) { 00645 default: 00646 /* XXX TODO: instantiate verify digests for all identical paths. */ 00647 dc->dalgo = dc->algo; 00648 fdInitDigest(dc->fd, dc->dalgo, 0); 00649 break; 00650 case 256: /* --all digests requested. */ 00651 { struct poptOption * opt = rpmioDigestPoptTable; 00652 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) { 00653 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) 00654 continue; 00655 if (opt->longName == NULL) 00656 continue; 00657 if (!(opt->val > 0 && opt->val < 256)) 00658 continue; 00659 dc->dalgo = opt->val; 00660 dc->dalgoName = opt->longName; 00661 fdInitDigest(dc->fd, dc->dalgo, 0); 00662 } 00663 } break; 00664 } 00665 00666 exit: 00667 return rc; 00668 } 00669 00670 static int 00671 rpmdcVisitF(rpmdc dc) 00672 /*@modifies dc @*/ 00673 { 00674 int rc = 0; 00675 int xx; 00676 00677 if (_rpmdc_debug) 00678 fprintf(stderr, "*** rpmdcVisitF(%p) fn %s\n", dc, dc->fn); 00679 if ((xx = rpmdcInitFile(dc)) != 0) 00680 rc = xx; 00681 else { 00682 if ((xx = rpmdcCalcFile(dc)) != 0) 00683 rc = xx; 00684 if ((xx = rpmdcFiniFile(dc)) != 0) 00685 rc = xx; 00686 } 00687 return rc; 00688 } 00689 00690 static int 00691 rpmdcSortLexical(const FTSENT ** a, const FTSENT ** b) 00692 /*@*/ 00693 { 00694 return strcmp((*a)->fts_name, (*b)->fts_name); 00695 } 00696 00697 static int 00698 rpmdcSortDirsLast(const FTSENT ** a, const FTSENT ** b) 00699 /*@*/ 00700 { 00701 if (S_ISDIR((*a)->fts_statp->st_mode)) { 00702 if (!S_ISDIR((*b)->fts_statp->st_mode)) 00703 return 1; 00704 } else if (S_ISDIR((*b)->fts_statp->st_mode)) 00705 return -1; 00706 return strcmp((*a)->fts_name, (*b)->fts_name); 00707 } 00708 00709 static int 00710 rpmdcCWalk(rpmdc dc) 00711 { 00712 char *const * paths = (char * const *) dc->paths; 00713 int ftsoptions = dc->ftsoptions; 00714 int rval = 0; 00715 00716 dc->t = Fts_open(paths, ftsoptions, 00717 (F_ISSET(dc, 0INSTALL) && _old_0install ? rpmdcSortLexical : rpmdcSortDirsLast)); 00718 if (dc->t == NULL) { 00719 fprintf(stderr, "Fts_open: %s", strerror(errno)); 00720 return -1; 00721 } 00722 00723 while ((dc->p = Fts_read(dc->t)) != NULL) { 00724 #ifdef NOTYET 00725 int indent = 0; 00726 if (F_ISSET(dc, INDENT)) 00727 indent = dc->p->fts_level * 4; 00728 if (rpmdcCheckExcludes(dc->p->fts_name, dc->p->fts_path)) { 00729 (void) Fts_set(dc->t, dc->p, FTS_SKIP); 00730 continue; 00731 } 00732 #endif 00733 00734 dc->fn = dc->p->fts_path; /* XXX eliminate dc->fn */ 00735 memcpy(&dc->sb, dc->p->fts_statp, sizeof(dc->sb)); 00736 00737 switch(dc->p->fts_info) { 00738 case FTS_D: 00739 #ifdef NOTYET 00740 if (!F_ISSET(dc, DIRSONLY)) 00741 (void) printf("\n"); 00742 if (!F_ISSET(dc, NOCOMMENT)) 00743 (void) printf("# %s\n", dc->p->fts_path); 00744 (void) rpmdcVisitD(dc); 00745 #endif 00746 /* XXX don't visit topdirs for 0install. */ 00747 if (F_ISSET(dc, 0INSTALL) && dc->p->fts_level > 0) 00748 rpmdcVisitF(dc); 00749 /*@switchbreak@*/ break; 00750 case FTS_DP: 00751 #ifdef NOTYET 00752 if (!F_ISSET(dc, NOCOMMENT) && (dc->p->fts_level > 0)) 00753 (void) printf("%*s# %s\n", indent, "", dc->p->fts_path); 00754 (void) printf("%*s..\n", indent, ""); 00755 if (!F_ISSET(dc, DIRSONLY)) 00756 (void) printf("\n"); 00757 #endif 00758 /*@switchbreak@*/ break; 00759 case FTS_DNR: 00760 case FTS_ERR: 00761 case FTS_NS: 00762 (void) fprintf(stderr, "%s: %s: %s\n", __progname, 00763 dc->p->fts_path, strerror(dc->p->fts_errno)); 00764 /*@switchbreak@*/ break; 00765 default: 00766 if (!F_ISSET(dc, DIRSONLY)) 00767 rpmdcVisitF(dc); 00768 /*@switchbreak@*/ break; 00769 } 00770 } 00771 (void) Fts_close(dc->t); 00772 dc->p = NULL; 00773 dc->t = NULL; 00774 return rval; 00775 } 00776 00777 static int rpmdcLoadManifests(rpmdc dc) 00778 /*@globals h_errno, fileSystem, internalState @*/ 00779 /*@modifies dc, h_errno, fileSystem, internalState @*/ 00780 { 00781 return (dc->manifests != NULL ? (*dc->parse) (dc) : 0); 00782 } 00783 00784 #if !defined(POPT_ARG_ARGV) 00785 static int _poptSaveString(const char ***argvp, unsigned int argInfo, const char * val) 00786 /*@*/ 00787 { 00788 ARGV_t argv; 00789 int argc = 0; 00790 if (argvp == NULL) 00791 return -1; 00792 if (*argvp) 00793 while ((*argvp)[argc] != NULL) 00794 argc++; 00795 *argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp)); 00796 if ((argv = *argvp) != NULL) { 00797 argv[argc++] = xstrdup(val); 00798 argv[argc ] = NULL; 00799 } 00800 return 0; 00801 } 00802 00805 static void rpmdcArgCallback(poptContext con, 00806 /*@unused@*/ enum poptCallbackReason reason, 00807 const struct poptOption * opt, /*@unused@*/ const char * arg, 00808 /*@unused@*/ void * data) 00809 /*@globals fileSystem @*/ 00810 /*@modifies fileSystem @*/ 00811 { 00812 /* XXX avoid accidental collisions with POPT_BIT_SET for flags */ 00813 if (opt->arg == NULL) 00814 switch (opt->val) { 00815 int xx; 00816 case 'c': 00817 assert(arg != NULL); 00818 xx = _poptSaveString(&_dc.manifests, opt->argInfo, arg); 00819 break; 00820 00821 default: 00822 fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val); 00823 poptPrintUsage(con, stderr, 0); 00824 /*@-exitarg@*/ 00825 exit(2); 00826 /*@=exitarg@*/ 00827 /*@notreached@*/ break; 00828 } 00829 } 00830 #endif /* POPT_ARG_ARGV */ 00831 00832 static struct poptOption _optionsTable[] = { 00833 #if !defined(POPT_ARG_ARGV) 00834 /*@-type@*/ /* FIX: cast? */ 00835 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE, 00836 rpmdcArgCallback, 0, NULL, NULL }, 00837 /*@=type@*/ 00838 #endif /* POPT_ARG_ARGV */ 00839 00840 { "0install", '0', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_0INSTALL, 00841 N_("Print 0install manifest"), NULL }, 00842 00843 { "binary", 'b', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_BINARY, 00844 N_("Read in binary mode"), NULL }, 00845 00846 #if !defined(POPT_ARG_ARGV) 00847 { "check", 'c', POPT_ARG_STRING, NULL, 'c', 00848 N_("Read digests from MANIFEST file and verify (may be used more than once)"), 00849 N_("MANIFEST") }, 00850 #else 00851 { "check", 'c', POPT_ARG_ARGV, &_dc.manifests, 0, 00852 N_("Read digests from MANIFEST file and verify (may be used more than once)"), 00853 N_("MANIFEST") }, 00854 #endif 00855 { "create",'c', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_CREATE, 00856 N_("Print file tree specification to stdout"), NULL }, 00857 { "dirs",'d', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_DIRSONLY, 00858 N_("Directories only"), NULL }, 00859 00860 { "text", 't', POPT_BIT_CLR, &_dc.flags, RPMDC_FLAGS_BINARY, 00861 N_("read in text mode (default)"), NULL }, 00862 00863 #ifdef NOTYET /* XXX todo for popt-1.15 */ 00864 { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0, 00865 N_("\ 00866 The following two options are useful only when verifying digests:\ 00867 "), NULL }, 00868 #endif 00869 00870 { "status", '\0', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_STATUS, 00871 N_("no output when verifying"), NULL }, 00872 { "warn", 'w', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_WARN, 00873 N_("warn about improperly formatted checksum lines"), NULL }, 00874 00875 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0, 00876 N_("Available digests:"), NULL }, 00877 00878 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0, 00879 N_("Common options for all rpmio executables:"), 00880 NULL }, 00881 00882 POPT_AUTOALIAS 00883 POPT_AUTOHELP 00884 00885 { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0, 00886 N_("\ 00887 When checking, the input should be a former output of this program. The\n\ 00888 default mode is to print a line with digest, a character indicating type\n\ 00889 (`*' for binary, ` ' for text), and name for each FILE.\n\ 00890 \0"), NULL }, /* embed NUL to work around expandMacro bug in Doxygen 1.7.1 */ 00891 00892 POPT_TABLEEND 00893 }; 00894 00895 static struct poptOption *optionsTable = &_optionsTable[0]; 00896 00897 int 00898 main(int argc, char *argv[]) 00899 { 00900 poptContext optCon = rpmioInit(argc, argv, optionsTable); 00901 ARGV_t av; 00902 int ac; 00903 int rc = 0; 00904 int xx; 00905 00906 rpmswEnter(&dc->totalops, -1); 00907 00908 if (F_ISSET(dc, 0INSTALL)) { 00909 dc->parse = rpmdcParseZeroInstall; 00910 dc->print = rpmdcPrintZeroInstall; 00911 if ((int)rpmioDigestHashAlgo < 0) 00912 rpmioDigestHashAlgo = PGPHASHALGO_SHA1; 00913 dc->algo = rpmioDigestHashAlgo; 00914 dc->oalgo = dc->algo; 00915 dc->oalgoName = rpmdcAlgo2Name(dc->oalgo); 00916 if (!strcmp(dc->oalgoName, "sha1")) 00917 dc->oalgoName = "sha1new"; 00918 } else { 00919 dc->parse = rpmdcParseCoreutils; 00920 dc->print = rpmdcPrintCoreutils; 00921 if ((int)rpmioDigestHashAlgo < 0) 00922 rpmioDigestHashAlgo = PGPHASHALGO_MD5; 00923 dc->algo = rpmioDigestHashAlgo; 00924 } 00925 00926 if (dc->ofn == NULL) 00927 dc->ofn = "-"; 00928 dc->ftsoptions = rpmioFtsOpts; 00929 if (!(dc->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL))) 00930 dc->ftsoptions |= FTS_PHYSICAL; 00931 dc->ftsoptions |= FTS_NOCHDIR; 00932 00933 dc->ofd = Fopen(dc->ofn, "w.ufdio"); 00934 if (F_ISSET(dc, 0INSTALL)) 00935 fdInitDigest(dc->ofd, dc->oalgo, 0); 00936 00937 av = poptGetArgs(optCon); 00938 ac = argvCount(av); 00939 if ((ac == 0 && dc->manifests == NULL) 00940 || (ac > 0 && dc->manifests != NULL)) 00941 { 00942 poptPrintUsage(optCon, stderr, 0); 00943 rc = 2; 00944 goto exit; 00945 } 00946 00947 if (dc->manifests != NULL) { 00948 if ((xx = rpmdcLoadManifests(dc)) != 0) 00949 rc = xx; 00950 } else { 00951 int i; 00952 for (i = 0; i < ac; i++) 00953 xx = argvAdd(&dc->paths, av[i]); 00954 } 00955 if (rc) 00956 goto exit; 00957 00958 if (dc->manifests != NULL) { 00959 dc->ix = 0; 00960 av = dc->paths; 00961 if (av != NULL) 00962 while ((dc->fn = *av++) != NULL) { 00963 if ((xx = Lstat(dc->fn, &dc->sb)) != 0 00964 || (xx = rpmdcVisitF(dc)) != 0) 00965 rc = xx; 00966 dc->ix++; 00967 } 00968 } else { 00969 if ((xx = rpmdcCWalk(dc)) != 0) 00970 rc = xx; 00971 } 00972 00973 exit: 00974 if (dc->nfailed) 00975 fprintf(stderr, "%s: WARNING: %u of %u computed checksums did NOT match\n", 00976 __progname, (unsigned) dc->nfailed, (unsigned) dc->ncomputed); 00977 00978 if (dc->ofd) { 00979 /* Print the output spewage digest for 0install format manifests. */ 00980 if (rc == 0 && F_ISSET(dc, 0INSTALL) && dc->manifests == NULL) { 00981 static int asAscii = 1; 00982 char *t; 00983 fdFiniDigest(dc->ofd, dc->oalgo, &dc->digest, &dc->digestlen, asAscii); 00984 assert(dc->digest != NULL); 00985 t = rpmExpand(dc->oalgoName, "=", dc->digest, "\n", NULL); 00986 (void) Fwrite(t, strlen(t), sizeof(*t), dc->ofd); 00987 t = _free(t); 00988 dc->digest = _free(dc->digest); 00989 } 00990 (void) Fclose(dc->ofd); 00991 dc->ofd = NULL; 00992 } 00993 00994 #ifdef NOTYET 00995 dc->manifests = argvFree(dc->manifests); 00996 #endif 00997 dc->algos = argiFree(dc->algos); 00998 dc->digests = argvFree(dc->digests); 00999 dc->paths = argvFree(dc->paths); 01000 01001 rpmswExit(&dc->totalops, 0); 01002 if (_rpmsw_stats) { 01003 rpmswPrint(" total:", &dc->totalops); 01004 rpmswPrint(" read:", &dc->readops); 01005 rpmswPrint("digest:", &dc->digestops); 01006 } 01007 01008 optCon = rpmioFini(optCon); 01009 01010 return rc; 01011 }