rpm 5.3.7

tools/rpmdigest.c

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