rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

rpmdigest.c
Go to the documentation of this file.
1 #include "system.h"
2 /*@unchecked@*/
3 extern const char * __progname;
4
5 #define _RPMIOB_INTERNAL
6 #include <rpmiotypes.h>
7 #include <rpmio_internal.h> /* XXX fdGetFILE */
8 #include <poptIO.h>
9 #include "debug.h"
10
11 static int _rpmdc_debug = 0;
12
13 /* XXX older 0install manifest format. */
14 static int _old_0install = 0;
15
16 #define _KFB(n) (1U << (n))
17 #define _DFB(n) (_KFB(n) | 0x40000000)
18
19 #define F_ISSET(_dc, _FLAG) ((_dc)->flags & ((RPMDC_FLAGS_##_FLAG) & ~0x40000000))
20
24 enum dcFlags_e {
25 RPMDC_FLAGS_NONE = 0,
26 /* 0 reserved */
27 RPMDC_FLAGS_WARN = _DFB( 1),
28 RPMDC_FLAGS_CREATE = _DFB( 2),
29 RPMDC_FLAGS_DIRSONLY = _DFB( 3),
30 /* 4-13 reserved */
31 RPMDC_FLAGS_BINARY = _DFB(14),
32 RPMDC_FLAGS_STATUS = _DFB(15),
33 RPMDC_FLAGS_0INSTALL = _DFB(16)
34 /* 17-31 unused */
35 };
36
39 typedef struct rpmdc_s * rpmdc;
40
43 struct rpmdc_s {
44 int ftsoptions;
45 FTS * t;
46 FTSENT * p;
47 struct stat sb;
49 enum dcFlags_e flags;
50 uint32_t algo;
51 uint32_t dalgo;
52 /*@observer@*/ /*@null@*/
53 const char * dalgoName;
54 const char * digest;
55 size_t digestlen;
56 const char * fn;
57 FD_t fd;
58 int (*parse) (rpmdc dc);
59 const char * (*print) (rpmdc dc, int rc);
60 const char * ofn;
61 FD_t ofd;
62 uint32_t oalgo;
63 const char * oalgoName;
64 ARGV_t manifests;
65 ARGI_t algos;
66 ARGV_t digests;
67 ARGV_t paths;
68 unsigned char buf[BUFSIZ];
69 ssize_t nb;
70 int ix;
71
72 size_t ncomputed;
73 size_t nchecked;
74 size_t nmatched;
75 size_t nfailed;
76 struct rpmop_s totalops;
77 struct rpmop_s readops;
78 struct rpmop_s digestops;
79 };
80
83 static struct rpmdc_s _dc = {
84 .ftsoptions = FTS_PHYSICAL,
85 .flags = RPMDC_FLAGS_CREATE
86 };
87
90 static rpmdc dc = &_dc;
91
92 /*==============================================================*/
93 static uint32_t rpmdcName2Algo(const char * dname)
94 /*@*/
95 {
96 struct poptOption * opt = rpmioDigestPoptTable;
97 uint32_t dalgo = 0xffffffff;
98
99 /* XXX compatible with 0install legacy derangement. bug imho. */
100 if (!strcmp(dname, "sha1new"))
101 dname = "sha1";
102
103 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
104 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
105 continue;
106 if (opt->longName == NULL)
107 continue;
108 if (!(opt->val > 0 && opt->val < 256))
109 continue;
110 if (strcmp(opt->longName, dname))
111 continue;
112 dalgo = (uint32_t) opt->val;
113 break;
114 }
115 return dalgo;
116 }
117
118 /*@null@*/
119 static const char * rpmdcAlgo2Name(uint32_t dalgo)
120 /*@*/
121 {
122 struct poptOption * opt = rpmioDigestPoptTable;
123 const char * dalgoName = NULL;
124
125 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
126 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
127 continue;
128 if (opt->longName == NULL)
129 continue;
130 if (!(opt->val > 0 && opt->val < 256))
131 continue;
132 if ((uint32_t)opt->val != dalgo)
133 continue;
134 dalgoName = opt->longName;
135 break;
136 }
137 return dalgoName;
138 }
139
140 /*==============================================================*/
141
142 static int rpmdcParseCoreutils(rpmdc dc)
143 /*@globals h_errno, fileSystem, internalState @*/
144 /*@modifies h_errno, fileSystem, internalState @*/
145 {
146 int rc = -1; /* assume failure */
147
148 if (dc->manifests != NULL) /* note rc=0 return with no files to load. */
149 while ((dc->fn = *dc->manifests++) != NULL) {
150 char buf[BUFSIZ];
151 unsigned lineno;
152 FILE *fp;
153
154 if (strcmp(dc->fn, "-") == 0) {
155 dc->fd = NULL;
156 fp = stdin;
157 } else {
158 /* XXX .fpio is needed because of fgets(3) usage. */
159 dc->fd = Fopen(dc->fn, "r.fpio");
160 if (dc->fd == NULL || Ferror(dc->fd) || (fp = fdGetFILE(dc->fd)) == NULL) {
161 fprintf(stderr, _("%s: Failed to open %s: %s\n"),
162 __progname, dc->fn, Fstrerror(dc->fd));
163 if (dc->fd != NULL) (void) Fclose(dc->fd);
164 dc->fd = NULL;
165 fp = NULL;
166 goto exit;
167 }
168 }
169
170 lineno = 0;
171 while (fgets(buf, sizeof(buf), fp) != NULL) {
172 const char * dname, * digest, * path;
173 char *se = buf + (int)strlen(buf);
174 int c, xx;
175
176 lineno++;
177 while (se > buf && xisspace((int)se[-1]))
178 se--;
179 *se = '\0';
180
181 /* Skip blank lines */
182 if (buf[0] == '\0') /*@innercontinue@*/ continue;
183 /* Skip comment lines */
184 if (buf[0] == '#') /*@innercontinue@*/ continue;
185
186 /* Parse "[algo:]digest [* ]path" line. */
187 dname = NULL; path = NULL;
188 for (digest = se = buf; (c = (int)*se) != 0; se++)
189 switch (c) {
190 default:
191 /*@switchbreak@*/ break;
192 case ':':
193 *se++ = '\0';
194 dname = digest;
195 digest = se;
196 /*@switchbreak@*/ break;
197 case ' ':
198 se[0] = '\0'; /* loop will terminate */
199 if (se[1] == ' ' || se[1] == '*')
200 se[1] = '\0';
201 path = se + 2;
202 /*@switchbreak@*/ break;
203 }
204 if (path == NULL) {
205 fprintf(stderr, _("%s: %s line %u: No file path found.\n"),
206 __progname, dc->fn, lineno);
207 rc = 2;
208 goto exit;
209 }
210
211 /* Map name to algorithm number. */
212 if (dname) {
213 if ((dc->dalgo = rpmdcName2Algo(dname)) != 0xffffffff)
214 dc->dalgoName = xstrdup(dname);
215 if (dc->dalgo == 0xffffffff) {
216 fprintf(stderr, _("%s: %s line %u: Unknown digest name \"%s\"\n"),
217 __progname, dc->fn, lineno, dname);
218 rc = 2;
219 goto exit;
220 }
221 } else
222 dc->dalgo = dc->algo;
223
224 /* Save {algo, digest, path} for processing. */
225 xx = argiAdd(&dc->algos, -1, dc->dalgo);
226 xx = argvAdd(&dc->digests, digest);
227 xx = argvAdd(&dc->paths, path);
228 }
229
230 if (dc->fd != NULL) {
231 (void) Fclose(dc->fd);
232 dc->fd = NULL;
233 }
234 }
235 rc = 0;
236
237 exit:
238 return rc;
239 }
240
241 /*@null@*/
242 static const char * rpmdcPrintCoreutils(rpmdc dc, int rc)
243 {
244 const char *msg = (rc ? "FAILED" : "OK");
245 char * t, * te;
246 size_t nb = 0;
247
248 /* Don't bother formatting if noone cares. */
249 if (rc == 0 && F_ISSET(dc, STATUS))
250 return NULL;
251
252 /* Calculate size of message. */
253 if (dc->dalgoName != NULL)
254 nb += strlen(dc->dalgoName) + sizeof(":") - 1;
255 assert(dc->digest != NULL);
256 if (dc->digest != NULL && dc->digestlen > 0)
257 nb += dc->digestlen;
258 nb += sizeof(" *") - 1;
259 if (dc->fn != NULL)
260 nb += strlen(dc->fn);
261 nb += strlen(msg);
262 nb += sizeof("\n"); /* XXX trailing NUL */
263
264 /* Compose the message. */
265 te = t = xmalloc(nb);
266 *te = '\0';
267
268 if (dc->manifests) {
269 if (rc || !F_ISSET(dc, STATUS)) {
270 if (dc->fn)
271 te = stpcpy( stpcpy(te, dc->fn), ": ");
272 te = stpcpy(te, msg);
273 *te++ = '\n';
274 }
275 } else {
276 if (dc->dalgoName)
277 te = stpcpy( stpcpy(te, dc->dalgoName), ":");
278 te = stpcpy(te, dc->digest);
279 *te++ = ' ';
280 *te++ = (F_ISSET(dc, BINARY) ? '*' : ' ');
281 te = stpcpy(te, dc->fn);
282 *te++ = '\n';
283 }
284 *te = '\0';
285
286 return t;
287 }
288
289 /*==============================================================*/
290
291 static int rpmdcParseZeroInstall(rpmdc dc)
292 /*@globals h_errno, fileSystem, internalState @*/
293 /*@modifies h_errno, fileSystem, internalState @*/
294 {
295 int rc = 0; /* assume success */
296
297 if (dc->manifests != NULL) /* note rc=0 return with no files to load. */
298 while ((dc->fn = *dc->manifests++) != NULL) {
299 unsigned lineno;
300 char * be;
301 rpmiob iob = NULL;
302 int xx = rpmiobSlurp(dc->fn, &iob);
303 const char * digest;
304 char * f;
305 char * fe;
306
307 if (!(xx == 0 && iob != NULL)) {
308 fprintf(stderr, _("%s: Failed to open %s\n"), __progname, dc->fn);
309 rc = -1;
310 goto bottom;
311 }
312
313 be = (char *)(iob->b + iob->blen);
314 while (be > (char *)iob->b && (be[-1] == '\n' || be[-1] == '\r')) {
315 be--;
316 *be = '\0';
317 }
318
319 /* Parse "algo=digest" from last line. */
320 be = strrchr((char *)iob->b, '=');
321 if (be == NULL) {
322 fprintf(stderr,
323 _("%s: %s: Manifest needs \"algo=digest\" as last line\n"),
324 __progname, dc->fn);
325 rc = 2;
326 goto bottom;
327 }
328 *be = '\0';
329 dc->digest = be + 1;
330 while (be > (char *)iob->b && !(be[-1] == '\n' || be[-1] == '\r'))
331 be--;
332 if (be <= (char *)iob->b) {
333 fprintf(stderr, _("%s: %s: Manifest is empty\n"),
334 __progname, dc->fn);
335 rc = 2;
336 goto bottom;
337 }
338
339 /* Map name to algorithm number. */
340 if ((dc->dalgo = rpmdcName2Algo(be)) == 0xffffffff) {
341 fprintf(stderr, _("%s: %s: Unknown digest algo name \"%s\"\n"),
342 __progname, dc->fn, be);
343 rc = 2;
344 goto bottom;
345 }
346 *be = '\0';
347
348 /* Verify the manifest digest. */
349 { DIGEST_CTX ctx = rpmDigestInit(dc->dalgo, 0);
350
351 (void) rpmDigestUpdate(ctx, (char *)iob->b, (be - (char *)iob->b));
352 digest = NULL;
353 (void) rpmDigestFinal(ctx, &digest, NULL, 1);
354 if (strcmp(dc->digest, digest)) {
355 fprintf(stderr,
356 _("%s: %s: Manifest digest check: Expected(%s) != (%s)\n"),
357 __progname, dc->fn, dc->digest, digest);
358 rc = 2;
359 goto bottom;
360 }
361 digest = _free(digest);
362 }
363
364 /* Parse and save manifest items. */
365 lineno = 0;
366 for (f = (char *)iob->b; *f; f = fe) {
367 static const char hexdigits[] = "0123456789ABCDEFabcdef";
368 const char * _dn = NULL;
369 const char * path;
370
371 lineno++;
372 fe = f;
373 while (*fe && !(*fe == '\n' || *fe == '\r'))
374 fe++;
375 while (*fe && (*fe == '\n' || *fe == '\r'))
376 *fe++ = '\0';
377 switch ((int)*f) {
378 case 'D':
379 _dn = f + 2;
380 continue;
381 /*@notreached@*/ break;
382 case 'F':
383 case 'S':
384 case 'X':
385 digest = f + 2;
386 f += 2;
387 while (*f && strchr(hexdigits, *f) != NULL)
388 f++;
389 if (*f != ' ') {
390 fprintf(stderr, _("%s: %s line %u: Malformed digest field.\n"),
391 __progname, dc->fn, lineno);
392 rc = 2;
393 goto bottom;
394 }
395 *f++ = '\0';
396 while (*f && xisdigit(*f))
397 f++;
398 if (*f != ' ') {
399 fprintf(stderr, _("%s: %s line %u: Malformed mtime field.\n"),
400 __progname, dc->fn, lineno);
401 rc = 2;
402 goto bottom;
403 }
404 *f++ = '\0';
405 while (*f && xisdigit(*f))
406 f++;
407 if (*f != ' ') {
408 fprintf(stderr, _("%s: %s line %u: Malformed size field.\n"),
409 __progname, dc->fn, lineno);
410 rc = 2;
411 goto bottom;
412 }
413 *f++ = '\0';
414 if (*f == '\0') {
415 fprintf(stderr, _("%s: %s line %u: No file path.\n"),
416 __progname, dc->fn, lineno);
417 rc = 2;
418 goto bottom;
419 }
420
421 if (_dn && *_dn == '/')
422 path = rpmExpand(_dn+1, "/", f, NULL);
423 else
424 path = xstrdup(f);
425
426 /* Save {algo, digest, path} for processing. */
427 xx = argiAdd(&dc->algos, -1, dc->dalgo);
428 xx = argvAdd(&dc->digests, digest);
429 xx = argvAdd(&dc->paths, path);
430 path = _free(path);
431 break;
432 }
433 }
434
435 bottom:
436 iob = rpmiobFree(iob);
437 if (rc != 0)
438 goto exit;
439 }
440
441 exit:
442 return rc;
443 }
444
445 /*@null@*/
446 static const char * rpmdcPrintZeroInstall(rpmdc dc, int rc)
447 {
448 char * t, * te;
449 size_t nb = 0;
450 char _mtime[32];
451 char _size[32];
452 const struct stat * st = &dc->sb;
453 const char * _bn;
454
455 /* Don't bother formatting if noone cares. */
456 if (rc == 0 && F_ISSET(dc, STATUS))
457 return NULL;
458
459 snprintf(_mtime, sizeof(_mtime), "%llu",
460 (unsigned long long) st->st_mtime);
461 _mtime[sizeof(_mtime)-1] = '\0';
462 snprintf(_size, sizeof(_size), "%llu",
463 (unsigned long long) st->st_size);
464 _size[sizeof(_size)-1] = '\0';
465 if ((_bn = strrchr(dc->fn, '/')) != NULL)
466 _bn++;
467 else
468 _bn = dc->fn;
469
470 /* Calculate size of message. */
471 nb += sizeof("F");
472 if (dc->digest != NULL && dc->digestlen > 0)
473 nb += 1 + dc->digestlen;
474 nb += 1 + strlen(_mtime);
475 nb += 1 + strlen(_size);
476 nb += 1 + strlen(_bn);
477 nb += sizeof("\n"); /* XXX trailing NUL */
478
479 /* Compose the message. */
480 te = t = xmalloc(nb);
481 *te = '\0';
482
483 if (dc->manifests) {
484 const char *msg = (rc ? "FAILED" : "OK");
485 if (rc || !F_ISSET(dc, STATUS)) {
486 if (dc->fn)
487 te = stpcpy( stpcpy(te, dc->fn), ": ");
488 te = stpcpy(te, msg);
489 *te++ = '\n';
490 }
491 } else {
492 if (S_ISDIR(st->st_mode)) {
493 *te++ = 'D';
494 if (_old_0install) {
495 *te++ = ' ';
496 te = stpcpy(te, _mtime);
497 }
498 *te++ = ' ';
499 *te++ = '/';
500 te = stpcpy(te, _bn);
501 *te++ = '\n';
502 } else if (S_ISREG(st->st_mode) || S_ISLNK(st->st_mode)) {
503 if (S_ISLNK(st->st_mode))
504 *te++ = 'S';
505 else
506 *te++ = (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ? 'X' : 'F';
507 *te++ = ' ';
508 te = stpcpy(te, dc->digest);
509 *te++ = ' ';
510 te = stpcpy(te, _mtime);
511 *te++ = ' ';
512 te = stpcpy(te, _size);
513 *te++ = ' ';
514 te = stpcpy(te, _bn);
515 *te++ = '\n';
516 }
517 }
518 *te = '\0';
519
520 return t;
521 }
522
523 /*==============================================================*/
524
525 static int rpmdcPrintFile(rpmdc dc)
526 {
527 static int asAscii = 1;
528 int rc = 0;
529
530 if (_rpmdc_debug)
531 fprintf(stderr, "\trpmdcPrintFile(%p) fd %p fn %s\n", dc, dc->fd, dc->fn);
532
533 assert(dc->fd != NULL);
534 fdFiniDigest(dc->fd, dc->dalgo, &dc->digest, &dc->digestlen, asAscii);
535 assert(dc->digest != NULL);
536 dc->ncomputed++;
537
538 if (dc->manifests) {
539 dc->nchecked++;
540 if ((rc = strcmp(dc->digest, dc->digests[dc->ix])) != 0)
541 dc->nfailed++;
542 else
543 dc->nmatched++;
544 }
545
546 { const char * t = (*dc->print) (dc, rc);
547 if (dc->ofd && t && *t) {
548 size_t nb = strlen(t);
549 nb = Fwrite(t, nb, sizeof(*t), dc->ofd);
550 (void) Fflush(dc->ofd);
551 }
552 t = _free(t);
553 }
554
555 dc->digest = _free(dc->digest);
556 dc->digestlen = 0;
557 return rc;
558 }
559
560 static int rpmdcFiniFile(rpmdc dc)
561 {
562 uint32_t dalgo = (dc->manifests ? dc->algos->vals[dc->ix] : dc->algo);
563 int rc = 0;
564 int xx;
565
566 /* Only regular files have dc->fd != NULL here. Skip all other paths. */
567 if (dc->fd == NULL)
568 return rc;
569
570 if (_rpmdc_debug)
571 fprintf(stderr, "\trpmdcFiniFile(%p) fn %s\n", dc, dc->fn);
572 switch (dalgo) {
573 default:
574 dc->dalgo = dalgo;
575 dc->dalgoName = NULL;
576 xx = rpmdcPrintFile(dc);
577 if (xx) rc = xx;
578 break;
579 case 256: /* --all digests requested. */
580 { struct poptOption * opt = rpmioDigestPoptTable;
581 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
582 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
583 continue;
584 if (opt->arg != (void *)&rpmioDigestHashAlgo)
585 continue;
586 dc->dalgo = opt->val;
587 if (!(dc->dalgo > 0 && dc->dalgo < 256))
588 continue;
589 dc->dalgoName = opt->longName;
590 xx = rpmdcPrintFile(dc);
591 if (xx) rc = xx;
592 }
593 } break;
594 }
595
596 (void) rpmswAdd(&dc->readops, fdstat_op(dc->fd, FDSTAT_READ));
597 (void) rpmswAdd(&dc->digestops, fdstat_op(dc->fd, FDSTAT_DIGEST));
598 Fclose(dc->fd);
599 dc->fd = NULL;
600
601 return rc;
602 }
603
604 static int rpmdcCalcFile(rpmdc dc)
605 {
606 int rc = 0;
607
608 if (_rpmdc_debug)
609 fprintf(stderr, "\trpmdcCalcFile(%p) fn %s\n", dc, dc->fn);
610 /* Skip (unopened) non-files. */
611 if (dc->fd != NULL)
612 do {
613 dc->nb = Fread(dc->buf, sizeof(dc->buf[0]), sizeof(dc->buf), dc->fd);
614 if (Ferror(dc->fd)) {
615 rc = 2;
616 break;
617 }
618 } while (dc->nb > 0);
619
620 return rc;
621 }
622
623 static int rpmdcInitFile(rpmdc dc)
624 {
625 int rc = 0;
626
627 if (_rpmdc_debug)
628 fprintf(stderr, "\trpmdcInitFile(%p) fn %s\n", dc, dc->fn);
629 /* Skip non-files. */
630 if (!S_ISREG(dc->sb.st_mode)) {
631 /* XXX not found return code? */
632 goto exit;
633 }
634
635 dc->fd = Fopen(dc->fn, "r.ufdio");
636 if (dc->fd == NULL || Ferror(dc->fd)) {
637 fprintf(stderr, _("open of %s failed: %s\n"), dc->fn, Fstrerror(dc->fd));
638 if (dc->fd != NULL) Fclose(dc->fd);
639 dc->fd = NULL;
640 rc = 2;
641 goto exit;
642 }
643
644 switch (dc->algo) {
645 default:
646 /* XXX TODO: instantiate verify digests for all identical paths. */
647 dc->dalgo = dc->algo;
648 fdInitDigest(dc->fd, dc->dalgo, 0);
649 break;
650 case 256: /* --all digests requested. */
651 { struct poptOption * opt = rpmioDigestPoptTable;
652 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
653 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
654 continue;
655 if (opt->longName == NULL)
656 continue;
657 if (!(opt->val > 0 && opt->val < 256))
658 continue;
659 dc->dalgo = opt->val;
660 dc->dalgoName = opt->longName;
661 fdInitDigest(dc->fd, dc->dalgo, 0);
662 }
663 } break;
664 }
665
666 exit:
667 return rc;
668 }
669
670 static int
671 rpmdcVisitF(rpmdc dc)
672 /*@modifies dc @*/
673 {
674 int rc = 0;
675 int xx;
676
677 if (_rpmdc_debug)
678 fprintf(stderr, "*** rpmdcVisitF(%p) fn %s\n", dc, dc->fn);
679 if ((xx = rpmdcInitFile(dc)) != 0)
680 rc = xx;
681 else {
682 if ((xx = rpmdcCalcFile(dc)) != 0)
683 rc = xx;
684 if ((xx = rpmdcFiniFile(dc)) != 0)
685 rc = xx;
686 }
687 return rc;
688 }
689
690 static int
691 rpmdcSortLexical(const FTSENT ** a, const FTSENT ** b)
692 /*@*/
693 {
694 return strcmp((*a)->fts_name, (*b)->fts_name);
695 }
696
697 static int
698 rpmdcSortDirsLast(const FTSENT ** a, const FTSENT ** b)
699 /*@*/
700 {
701 if (S_ISDIR((*a)->fts_statp->st_mode)) {
702 if (!S_ISDIR((*b)->fts_statp->st_mode))
703 return 1;
704 } else if (S_ISDIR((*b)->fts_statp->st_mode))
705 return -1;
706 return strcmp((*a)->fts_name, (*b)->fts_name);
707 }
708
709 static int
710 rpmdcCWalk(rpmdc dc)
711 {
712 char *const * paths = (char * const *) dc->paths;
713 int ftsoptions = dc->ftsoptions;
714 int rval = 0;
715
716 dc->t = Fts_open(paths, ftsoptions,
717 (F_ISSET(dc, 0INSTALL) && _old_0install ? rpmdcSortLexical : rpmdcSortDirsLast));
718 if (dc->t == NULL) {
719 fprintf(stderr, "Fts_open: %s", strerror(errno));
720 return -1;
721 }
722
723 while ((dc->p = Fts_read(dc->t)) != NULL) {
724 #ifdef NOTYET
725 int indent = 0;
726 if (F_ISSET(dc, INDENT))
727 indent = dc->p->fts_level * 4;
728 if (rpmdcCheckExcludes(dc->p->fts_name, dc->p->fts_path)) {
729 (void) Fts_set(dc->t, dc->p, FTS_SKIP);
730 continue;
731 }
732 #endif
733
734 dc->fn = dc->p->fts_path; /* XXX eliminate dc->fn */
735 memcpy(&dc->sb, dc->p->fts_statp, sizeof(dc->sb));
736
737 switch(dc->p->fts_info) {
738 case FTS_D:
739 #ifdef NOTYET
740 if (!F_ISSET(dc, DIRSONLY))
741 (void) printf("\n");
742 if (!F_ISSET(dc, NOCOMMENT))
743 (void) printf("# %s\n", dc->p->fts_path);
744 (void) rpmdcVisitD(dc);
745 #endif
746 /* XXX don't visit topdirs for 0install. */
747 if (F_ISSET(dc, 0INSTALL) && dc->p->fts_level > 0)
748 rpmdcVisitF(dc);
749 /*@switchbreak@*/ break;
750 case FTS_DP:
751 #ifdef NOTYET
752 if (!F_ISSET(dc, NOCOMMENT) && (dc->p->fts_level > 0))
753 (void) printf("%*s# %s\n", indent, "", dc->p->fts_path);
754 (void) printf("%*s..\n", indent, "");
755 if (!F_ISSET(dc, DIRSONLY))
756 (void) printf("\n");
757 #endif
758 /*@switchbreak@*/ break;
759 case FTS_DNR:
760 case FTS_ERR:
761 case FTS_NS:
762 (void) fprintf(stderr, "%s: %s: %s\n", __progname,
763 dc->p->fts_path, strerror(dc->p->fts_errno));
764 /*@switchbreak@*/ break;
765 default:
766 if (!F_ISSET(dc, DIRSONLY))
767 rpmdcVisitF(dc);
768 /*@switchbreak@*/ break;
769 }
770 }
771 (void) Fts_close(dc->t);
772 dc->p = NULL;
773 dc->t = NULL;
774 return rval;
775 }
776
777 static int rpmdcLoadManifests(rpmdc dc)
778 /*@globals h_errno, fileSystem, internalState @*/
779 /*@modifies dc, h_errno, fileSystem, internalState @*/
780 {
781 return (dc->manifests != NULL ? (*dc->parse) (dc) : 0);
782 }
783
784 #if !defined(POPT_ARG_ARGV)
785 static int _poptSaveString(const char ***argvp, unsigned int argInfo, const char * val)
786 /*@*/
787 {
788 ARGV_t argv;
789 int argc = 0;
790 if (argvp == NULL)
791 return -1;
792 if (*argvp)
793 while ((*argvp)[argc] != NULL)
794 argc++;
795 *argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp));
796 if ((argv = *argvp) != NULL) {
797 argv[argc++] = xstrdup(val);
798 argv[argc ] = NULL;
799 }
800 return 0;
801 }
802
805 static void rpmdcArgCallback(poptContext con,
806 /*@unused@*/ enum poptCallbackReason reason,
807 const struct poptOption * opt, /*@unused@*/ const char * arg,
808 /*@unused@*/ void * data)
809 /*@globals fileSystem @*/
810 /*@modifies fileSystem @*/
811 {
812 /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
813 if (opt->arg == NULL)
814 switch (opt->val) {
815 int xx;
816 case 'c':
817 assert(arg != NULL);
818 xx = _poptSaveString(&_dc.manifests, opt->argInfo, arg);
819 break;
820
821 default:
822 fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val);
823 poptPrintUsage(con, stderr, 0);
824 /*@-exitarg@*/
825 exit(2);
826 /*@=exitarg@*/
827 /*@notreached@*/ break;
828 }
829 }
830 #endif /* POPT_ARG_ARGV */
831
832 static struct poptOption _optionsTable[] = {
833 #if !defined(POPT_ARG_ARGV)
834 /*@-type@*/ /* FIX: cast? */
835 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
836 rpmdcArgCallback, 0, NULL, NULL },
837 /*@=type@*/
838 #endif /* POPT_ARG_ARGV */
839
840 { "0install", '0', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_0INSTALL,
841 N_("Print 0install manifest"), NULL },
842
843 { "binary", 'b', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_BINARY,
844 N_("Read in binary mode"), NULL },
845
846 #if !defined(POPT_ARG_ARGV)
847 { "check", 'c', POPT_ARG_STRING, NULL, 'c',
848 N_("Read digests from MANIFEST file and verify (may be used more than once)"),
849 N_("MANIFEST") },
850 #else
851 { "check", 'c', POPT_ARG_ARGV, &_dc.manifests, 0,
852 N_("Read digests from MANIFEST file and verify (may be used more than once)"),
853 N_("MANIFEST") },
854 #endif
855 { "create",'c', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_CREATE,
856 N_("Print file tree specification to stdout"), NULL },
857 { "dirs",'d', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_DIRSONLY,
858 N_("Directories only"), NULL },
859
860 { "text", 't', POPT_BIT_CLR, &_dc.flags, RPMDC_FLAGS_BINARY,
861 N_("read in text mode (default)"), NULL },
862
863 #ifdef NOTYET /* XXX todo for popt-1.15 */
864 { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
865 N_("\
866 The following two options are useful only when verifying digests:\
867 "), NULL },
868 #endif
869
870 { "status", '\0', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_STATUS,
871 N_("no output when verifying"), NULL },
872 { "warn", 'w', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_WARN,
873 N_("warn about improperly formatted checksum lines"), NULL },
874
875 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
876 N_("Available digests:"), NULL },
877
878 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
879 N_("Common options for all rpmio executables:"),
880 NULL },
881
882 POPT_AUTOALIAS
883 POPT_AUTOHELP
884
885 { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
886 N_("\
887 When checking, the input should be a former output of this program. The\n\
888 default mode is to print a line with digest, a character indicating type\n\
889 (`*' for binary, ` ' for text), and name for each FILE.\n\
890 \0"), NULL }, /* embed NUL to work around expandMacro bug in Doxygen 1.7.1 */
891
892 POPT_TABLEEND
893 };
894
895 static struct poptOption *optionsTable = &_optionsTable[0];
896
897 int
898 main(int argc, char *argv[])
899 {
900 poptContext optCon = rpmioInit(argc, argv, optionsTable);
901 ARGV_t av;
902 int ac;
903 int rc = 0;
904 int xx;
905
906 rpmswEnter(&dc->totalops, -1);
907
908 if (F_ISSET(dc, 0INSTALL)) {
909 dc->parse = rpmdcParseZeroInstall;
910 dc->print = rpmdcPrintZeroInstall;
911 if ((int)rpmioDigestHashAlgo < 0)
912 rpmioDigestHashAlgo = PGPHASHALGO_SHA1;
913 dc->algo = rpmioDigestHashAlgo;
914 dc->oalgo = dc->algo;
915 dc->oalgoName = rpmdcAlgo2Name(dc->oalgo);
916 if (!strcmp(dc->oalgoName, "sha1"))
917 dc->oalgoName = "sha1new";
918 } else {
919 dc->parse = rpmdcParseCoreutils;
920 dc->print = rpmdcPrintCoreutils;
921 if ((int)rpmioDigestHashAlgo < 0)
922 rpmioDigestHashAlgo = PGPHASHALGO_MD5;
923 dc->algo = rpmioDigestHashAlgo;
924 }
925
926 if (dc->ofn == NULL)
927 dc->ofn = "-";
928 dc->ftsoptions = rpmioFtsOpts;
929 if (!(dc->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)))
930 dc->ftsoptions |= FTS_PHYSICAL;
931 dc->ftsoptions |= FTS_NOCHDIR;
932
933 dc->ofd = Fopen(dc->ofn, "w.ufdio");
934 if (F_ISSET(dc, 0INSTALL))
935 fdInitDigest(dc->ofd, dc->oalgo, 0);
936
937 av = poptGetArgs(optCon);
938 ac = argvCount(av);
939 if ((ac == 0 && dc->manifests == NULL)
940 || (ac > 0 && dc->manifests != NULL))
941 {
942 poptPrintUsage(optCon, stderr, 0);
943 rc = 2;
944 goto exit;
945 }
946
947 if (dc->manifests != NULL) {
948 if ((xx = rpmdcLoadManifests(dc)) != 0)
949 rc = xx;
950 } else {
951 int i;
952 for (i = 0; i < ac; i++)
953 xx = argvAdd(&dc->paths, av[i]);
954 }
955 if (rc)
956 goto exit;
957
958 if (dc->manifests != NULL) {
959 dc->ix = 0;
960 av = dc->paths;
961 if (av != NULL)
962 while ((dc->fn = *av++) != NULL) {
963 if ((xx = Lstat(dc->fn, &dc->sb)) != 0
964 || (xx = rpmdcVisitF(dc)) != 0)
965 rc = xx;
966 dc->ix++;
967 }
968 } else {
969 if ((xx = rpmdcCWalk(dc)) != 0)
970 rc = xx;
971 }
972
973 exit:
974 if (dc->nfailed)
975 fprintf(stderr, "%s: WARNING: %u of %u computed checksums did NOT match\n",
976 __progname, (unsigned) dc->nfailed, (unsigned) dc->ncomputed);
977
978 if (dc->ofd) {
979 /* Print the output spewage digest for 0install format manifests. */
980 if (rc == 0 && F_ISSET(dc, 0INSTALL) && dc->manifests == NULL) {
981 static int asAscii = 1;
982 char *t;
983 fdFiniDigest(dc->ofd, dc->oalgo, &dc->digest, &dc->digestlen, asAscii);
984 assert(dc->digest != NULL);
985 t = rpmExpand(dc->oalgoName, "=", dc->digest, "\n", NULL);
986 (void) Fwrite(t, strlen(t), sizeof(*t), dc->ofd);
987 t = _free(t);
988 dc->digest = _free(dc->digest);
989 }
990 (void) Fclose(dc->ofd);
991 dc->ofd = NULL;
992 }
993
994 #ifdef NOTYET
995 dc->manifests = argvFree(dc->manifests);
996 #endif
997 dc->algos = argiFree(dc->algos);
998 dc->digests = argvFree(dc->digests);
999 dc->paths = argvFree(dc->paths);
1000
1001 rpmswExit(&dc->totalops, 0);
1002 if (_rpmsw_stats) {
1003 rpmswPrint(" total:", &dc->totalops);
1004 rpmswPrint(" read:", &dc->readops);
1005 rpmswPrint("digest:", &dc->digestops);
1006 }
1007
1008 optCon = rpmioFini(optCon);
1009
1010 return rc;
1011 }