rpm  5.4.10
rpmrollback.c
Go to the documentation of this file.
1 
5 #include "system.h"
6 
7 #include <rpmio.h>
8 #include <rpmiotypes.h>
9 #include <rpmcb.h>
10 #include <argv.h>
11 
12 #include <rpmtypes.h>
13 #include <rpmtag.h>
14 #include <pkgio.h>
15 #include <rpmdb.h>
16 
17 #include <rpmds.h>
18 #include "manifest.h"
19 #include "misc.h" /* XXX rpmGlob() */
20 
21 #define _RPMTE_INTERNAL /* XXX findErases needs rpmte internals. */
22 #define _RPMTS_INTERNAL /* XXX ts->teErase, ts->probs */
23 #define _RPMTS_PRINT
24 #include <rpmgi.h> /* XXX rpmgiEscapeSpaces */
25 
26 #include <rpmcli.h>
27 #define _RPMROLLBACK_INTERNAL
28 #include <rpmrollback.h>
29 
30 #include "debug.h"
31 
32 /*@access FD_t @*/ /* XXX void * arg */
33 /*@access rpmts @*/
34 /*@access rpmte @*/ /* XXX p->hdrid, p->pkgid, p->NEVRA */
35 /*@access IDTX @*/
36 /*@access IDT @*/
37 
38 #ifdef __cplusplus
39 
40 #define QVA_ISSET(_qvaflags, _FLAG) ((_qvaflags) & (VERIFY_##_FLAG))
41 
42 #define VSF_ISSET(_vsflags, _FLAG) ((_vsflags) & (RPMVSF_##_FLAG))
43 #define VSF_SET(_vsflags, _FLAG) \
44  (*((unsigned *)&(_vsflags)) |= (RPMVSF_##_FLAG))
45 #define VSF_CLR(_vsflags, _FLAG) \
46  (*((unsigned *)&(_vsflags)) &= ~(RPMVSF_##_FLAG))
47 
48 GENfree(IDTX)
49 GENfree(IDT)
50 
51 #else /* __cplusplus */
52 
53 #define QVA_ISSET(_qvaflags, _FLAG) ((_qvaflags) & (VERIFY_##_FLAG))
54 
55 #define VSF_ISSET(_vsflags, _FLAG) ((_vsflags) & (RPMVSF_##_FLAG))
56 #define VSF_SET(_vsflags, _FLAG) (_vsflags) |= (RPMVSF_##_FLAG)
57 #define VSF_CLR(_vsflags, _FLAG) (_vsflags) &= ~(RPMVSF_##_FLAG)
58 
59 #endif /* __cplusplus */
60 
61 /*@unchecked@*/
62 static int reverse = -1;
63 
66 static int IDTintcmp(const void * a, const void * b)
67  /*@*/
68 {
69  /*@-castexpose@*/
70  return ( reverse * (((IDT)a)->val.u32 - ((IDT)b)->val.u32) );
71  /*@=castexpose@*/
72 }
73 
74 IDTX IDTXfree(IDTX idtx)
75 {
76  if (idtx) {
77  int i;
78  if (idtx->idt)
79  for (i = 0; i < idtx->nidt; i++) {
80  IDT idt = idtx->idt + i;
81  (void)headerFree(idt->h);
82  idt->h = NULL;
83  idt->key = _free(idt->key);
84  }
85  idtx->idt = _free(idtx->idt);
86  idtx = _free(idtx);
87  }
88  return NULL;
89 }
90 
91 IDTX IDTXnew(void)
92 {
93  IDTX idtx = (IDTX) xcalloc(1, sizeof(*idtx));
94  idtx->delta = 10;
95  idtx->size = (int)sizeof(*((IDT)0));
96  return idtx;
97 }
98 
99 IDTX IDTXgrow(IDTX idtx, int need)
100 {
101  if (need < 0) return NULL;
102  if (idtx == NULL)
103  idtx = IDTXnew();
104  if (need == 0) return idtx;
105 
106  if ((idtx->nidt + need) > idtx->alloced) {
107  while (need > 0) {
108  idtx->alloced += idtx->delta;
109  need -= idtx->delta;
110  }
111  idtx->idt = (IDT) xrealloc(idtx->idt, (idtx->alloced * idtx->size));
112  }
113  return idtx;
114 }
115 
116 IDTX IDTXsort(IDTX idtx)
117 {
118  if (idtx != NULL && idtx->idt != NULL && idtx->nidt > 0)
119  qsort(idtx->idt, idtx->nidt, idtx->size, IDTintcmp);
120  return idtx;
121 }
122 
123 IDTX IDTXload(rpmts ts, rpmTag tag, rpmuint32_t rbtid)
124 {
125  HE_t he = (HE_t) memset(alloca(sizeof(*he)), 0, sizeof(*he));
126  IDTX idtx = NULL;
127  rpmmi mi;
128  Header h;
129  rpmuint32_t tid;
130  int xx;
131 
132  mi = rpmtsInitIterator(ts, tag, NULL, 0);
133 #ifdef NOTYET
134  (void) rpmmiAddPattern(mi, RPMTAG_NAME, RPMMIRE_DEFAULT, '!gpg-pubkey');
135 #endif
136  while ((h = rpmmiNext(mi)) != NULL) {
137  he->tag = tag;
138  xx = headerGet(h, he, 0);
139  if (!xx || he->p.ui32p == NULL)
140  continue;
141  tid = (he->p.ui32p ? he->p.ui32p[0] : 0);
142  he->p.ptr = _free(he->p.ptr);
143 
144  if (tid == 0 || tid == 0xffffffff)
145  continue;
146 
147  /* Don't bother with headers installed prior to the rollback goal. */
148  if (tid < rbtid)
149  continue;
150 
151  idtx = IDTXgrow(idtx, 1);
152  if (idtx == NULL || idtx->idt == NULL)
153  continue;
154 
155  { IDT idt;
156  /*@-nullderef@*/
157  idt = idtx->idt + idtx->nidt;
158  /*@=nullderef@*/
159  idt->done = 0;
160  idt->h = headerLink(h);
161  idt->key = NULL;
162  idt->instance = rpmmiInstance(mi);
163  idt->val.u32 = tid;
164  }
165  idtx->nidt++;
166  }
167  mi = rpmmiFree(mi);
168 
169  return IDTXsort(idtx);
170 }
171 
172 IDTX IDTXglob(rpmts ts, const char * globstr, rpmTag tag, rpmuint32_t rbtid)
173 {
174  HE_t he = (HE_t) memset(alloca(sizeof(*he)), 0, sizeof(*he));
175  IDTX idtx = NULL;
176  Header h;
177  rpmuint32_t tid;
178  FD_t fd;
179  const char ** av = NULL;
180  const char * fn;
181  int ac = 0;
182  rpmRC rpmrc;
183  int xx;
184  int i;
185 
186  av = NULL; ac = 0;
187  fn = rpmgiEscapeSpaces(globstr);
188  xx = rpmGlob(fn, &ac, &av);
189  fn = _free(fn);
190 
191  if (xx == 0)
192  for (i = 0; i < ac; i++) {
193  int isSource;
194 
195  fd = Fopen(av[i], "r.fdio");
196  if (fd == NULL || Ferror(fd)) {
197  rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), av[i],
198  Fstrerror(fd));
199  if (fd != NULL) (void) Fclose(fd);
200  continue;
201  }
202 
203  rpmrc = rpmReadPackageFile(ts, fd, av[i], &h);
204  (void) Fclose(fd);
205  switch (rpmrc) {
206  default:
207  goto bottom;
208  /*@notreached@*/ /*@switchbreak@*/ break;
209  case RPMRC_NOTTRUSTED:
210  case RPMRC_NOKEY:
211  case RPMRC_OK:
212  isSource =
213  (headerIsEntry(h, RPMTAG_SOURCERPM) == 0 &&
214  headerIsEntry(h, RPMTAG_ARCH) != 0);
215  if (isSource)
216  goto bottom;
217  /*@switchbreak@*/ break;
218  }
219 
220 { const char * origin = headerGetOrigin(h);
221 assert(origin != NULL);
222 assert(!strcmp(av[i], origin));
223 }
224  he->tag = tag;
225  xx = headerGet(h, he, 0);
226  if (!xx || he->p.ui32p == NULL)
227  goto bottom;
228  tid = (he->p.ui32p ? he->p.ui32p[0] : 0);
229  he->p.ptr = _free(he->p.ptr);
230 
231  /* Don't bother with headers installed prior to the rollback goal. */
232  if (tid < rbtid)
233  goto bottom;
234 
235  idtx = IDTXgrow(idtx, 1);
236  if (idtx == NULL || idtx->idt == NULL)
237  goto bottom;
238 
239  { IDT idt;
240  idt = idtx->idt + idtx->nidt;
241  idt->done = 0;
242  idt->h = headerLink(h);
243  idt->key = av[i];
244  av[i] = NULL;
245  idt->instance = 0;
246  idt->val.u32 = tid;
247  }
248  idtx->nidt++;
249 bottom:
250  (void)headerFree(h);
251  h = NULL;
252  }
253 
254  for (i = 0; i < ac; i++)
255  av[i] = _free(av[i]);
256  av = _free(av); ac = 0;
257 
258  return IDTXsort(idtx);
259 }
260 
270 static int cmpArgvStr(rpmts ts, const char *lname, const char ** AV, int AC,
271  /*@null@*/ const char * B)
272  /*@modifies ts @*/
273 {
274  const char * A;
275  int i;
276 
277  if (AV != NULL && AC > 0 && B == NULL) {
278  if (!strcmp(lname, "NEVRA")) {
279  rpmps ps = rpmtsProblems(ts);
280  for (i = 0; i < AC && (A = AV[i]) != NULL; i++) {
282  NULL, NULL, /* NEVRA, key */
283  lname, NULL, /* dn, bn */
284  A, /* altNEVRA */
285  0);
286  }
287  ps = rpmpsFree(ps);
288  }
289  return 0;
290  }
291 
292  if (AV != NULL && B != NULL)
293  for (i = 0; i < AC && (A = AV[i]) != NULL; i++) {
294  if (*A && *B && !strcmp(A, B))
295  return 1;
296  }
297  return 0;
298 }
299 
315 static int findErases(rpmts ts, /*@null@*/ rpmte p, unsigned thistid,
316  /*@null@*/ IDT ip, int niids)
317  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
318  /*@modifies ts, p, ip, rpmGlobalMacroContext, fileSystem, internalState @*/
319 {
320  HE_t he = (HE_t) memset(alloca(sizeof(*he)), 0, sizeof(*he));
321  int rc = 0;
322  int xx;
323 
324  /* Erase the previously installed packages for this transaction.
325  * Provided this transaction is not excluded from the rollback.
326  */
327  while (ip != NULL && ip->val.u32 == thistid) {
328 
329  if (ip->done)
330  goto bottom;
331 
332  {
333  const char ** flinkPkgid = NULL;
334  const char ** flinkHdrid = NULL;
335  const char ** flinkNEVRA = NULL;
336  rpmuint32_t pn, hn, nn;
337  int bingo;
338 
339  he->tag = RPMTAG_BLINKPKGID;
340  xx = headerGet(ip->h, he, 0);
341  flinkPkgid = he->p.argv;
342  pn = he->c;
343 
344  /* XXX Always erase packages at beginning of upgrade chain. */
345  if (pn == 1 && flinkPkgid[0] != NULL && !strcmp(flinkPkgid[0], RPMTE_CHAIN_END)) {
346  flinkPkgid = _free(flinkPkgid);
347  goto erase;
348  }
349 
350  he->tag = RPMTAG_BLINKHDRID;
351  xx = headerGet(ip->h, he, 0);
352  flinkHdrid = he->p.argv;
353  hn = he->c;
354  he->tag = RPMTAG_BLINKNEVRA;
355  xx = headerGet(ip->h, he, 0);
356  flinkNEVRA = he->p.argv;
357  nn = he->c;
358 
359  /*
360  * Link data may be missing and can have multiple entries.
361  */
362  /* XXX Until link tags are reliably populated, check in the order
363  * NEVRA -> hdrid -> pkgid
364  * because NEVRA is easier to debug (hdrid/pkgid are more precise.)
365  */
366  bingo = 0;
367  if (!bingo)
368  bingo = cmpArgvStr(ts, "NEVRA", flinkNEVRA, nn, (p ? p->NEVRA : NULL));
369  if (!bingo)
370  bingo = cmpArgvStr(ts, "Hdrid", flinkHdrid, hn, (p ? p->hdrid : NULL));
371 /*@-nullstate@*/
372  if (!bingo)
373  bingo = cmpArgvStr(ts, "Pkgid", flinkPkgid, pn, (p ? p->pkgid : NULL));
374 /*@=nullstate@*/
375  flinkPkgid = _free(flinkPkgid);
376  flinkHdrid = _free(flinkHdrid);
377  flinkNEVRA = _free(flinkNEVRA);
378 
379  if (bingo < 0) {
380  rc = -1;
381  goto exit;
382  }
383 
384  if (!bingo)
385  goto bottom;
386  }
387 
388 erase:
389  rpmlog(RPMLOG_DEBUG, D_("\t--- erase h#%u\n"), ip->instance);
390 
391  rc = rpmtsAddEraseElement(ts, ip->h, ip->instance);
392  if (rc != 0)
393  goto exit;
394 
395  /* Cross link the transaction elements to mimic --upgrade. */
396  if (p != NULL) {
397  rpmte q = ts->teErase;
398  xx = rpmteChain(p, q, ip->h, "Rollback");
399  }
400 
401 #ifdef NOTYET
402  ip->instance = 0;
403 #endif
404  ip->done = 1;
405 
406 bottom:
407 
408  /* Go to the next header in the rpmdb */
409  niids--;
410  if (niids > 0)
411  ip++;
412  else
413  ip = NULL;
414  }
415 
416 exit:
417  return rc;
418 }
419 
421 int rpmRollback(rpmts ts, QVA_t ia, const char ** argv)
422 {
424  unsigned thistid = 0xffffffff;
425  unsigned prevtid;
426  time_t tid;
427  IDTX itids = NULL;
428  IDTX rtids = NULL;
429  IDT rp;
430  int nrids = 0;
431  IDT ip;
432  int niids = 0;
433  int rc = 0;
434  rpmVSFlags vsflags, ovsflags;
435  int numAdded;
436  int numRemoved;
437  unsigned int _unsafe_rollbacks = 0;
438  rpmtransFlags transFlags = ia->transFlags;
439  rpmdepFlags depFlags = ia->depFlags;
440  int xx;
441 
442  if (argv != NULL && *argv != NULL) {
443  rc = -1;
444  goto exit;
445  }
446 
447  _unsafe_rollbacks = rpmExpandNumeric("%{?_unsafe_rollbacks}");
448 
449  vsflags = (rpmVSFlags) rpmExpandNumeric("%{?_vsflags_erase}");
450  vsflags = (rpmVSFlags) 0; /* XXX FIXME: ignore default disablers. */
451 #if defined(SUPPORT_NOSIGNATURES)
452  if (!QVA_ISSET(ia->qva_flags, DIGEST)) {
453  VSF_SET(vsflags, NOSHA1HEADER);
454  VSF_SET(vsflags, NOMD5HEADER);
455  VSF_SET(vsflags, NOSHA1);
456  VSF_SET(vsflags, NOMD5);
457  }
458  if (!QVA_ISSET(ia->qva_flags, SIGNATURE)) {
459  VSF_SET(vsflags, NODSAHEADER);
460  VSF_SET(vsflags, NORSAHEADER);
461  VSF_SET(vsflags, NODSA);
462  VSF_SET(vsflags, NORSA);
463  }
464  if (!QVA_ISSET(ia->qva_flags, HDRCHK)) {
465  VSF_SET(vsflags, NOHDRCHK);
466  }
467  VSF_SET(vsflags, NEEDPAYLOAD); /* XXX needed? */
468 #endif
469  ovsflags = rpmtsSetVSFlags(ts, vsflags);
470 
471  (void) rpmtsSetFlags(ts, transFlags);
472  (void) rpmtsSetDFlags(ts, depFlags);
473 
474  /* Make the transaction a rollback transaction. In a rollback
475  * a best effort is what we want
476  */
478 
479  itids = IDTXload(ts, RPMTAG_INSTALLTID, ia->rbtid);
480  if (itids != NULL) {
481  ip = itids->idt;
482  niids = itids->nidt;
483  } else {
484  ip = NULL;
485  niids = 0;
486  }
487 
488  { const char * globstr = rpmExpand("%{_repackage_dir}/*/*.rpm", NULL);
489  if (globstr == NULL || *globstr == '%') {
490  globstr = _free(globstr);
491  rc = -1;
492  goto exit;
493  }
494  rtids = IDTXglob(ts, globstr, RPMTAG_REMOVETID, ia->rbtid);
495 
496  if (rtids != NULL) {
497  rp = rtids->idt;
498  nrids = rtids->nidt;
499  } else {
500  rp = NULL;
501  nrids = 0;
502  }
503  globstr = _free(globstr);
504  }
505 
506  { int notifyFlags;
507  notifyFlags = ia->installInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
508  xx = rpmtsSetNotifyCallback(ts,
509  rpmShowProgress, (void *) ((long)notifyFlags));
510  }
511 
512  /* Run transactions until rollback goal is achieved. */
513  do {
514  prevtid = thistid;
515  rc = 0;
517  numAdded = 0;
518  numRemoved = 0;
520  (ia->installInterfaceFlags & ~ifmask);
521 
522  /* Find larger of the remaining install/erase transaction id's. */
523  thistid = 0;
524  if (ip != NULL && ip->val.u32 > thistid)
525  thistid = ip->val.u32;
526  if (rp != NULL && rp->val.u32 > thistid)
527  thistid = rp->val.u32;
528 
529  /* If we've achieved the rollback goal, then we're done. */
530  if (thistid == 0 || thistid < ia->rbtid)
531  break;
532 
533  /* If we've reached the (configured) rollback goal, then we're done. */
534  if (_unsafe_rollbacks && thistid <= _unsafe_rollbacks)
535  break;
536 
537  /* Is this transaction excluded from the rollback? */
538  if (ia->rbtidExcludes != NULL && ia->numrbtidExcludes > 0)
539  {
540  rpmuint32_t *excludedTID;
541  int excluded = 0;
542  for(excludedTID = ia->rbtidExcludes;
543  excludedTID < ia->rbtidExcludes + ia->numrbtidExcludes;
544  excludedTID++) {
545  if (thistid == *excludedTID) {
546  time_t ttid = (time_t)thistid;
548  _("Excluding TID from rollback: %-24.24s (0x%08x)\n"),
549  ctime(&ttid), thistid);
550  excluded = 1;
551  /*@innerbreak@*/ break;
552  }
553  }
554  if (excluded) {
555  /* Iterate over repackaged packages */
556  while (rp != NULL && rp->val.u32 == thistid) {
557  /* Go to the next repackaged package */
558  nrids--;
559  if (nrids > 0)
560  rp++;
561  else
562  rp = NULL;
563  }
564  /* Iterate over installed packages */
565  while (ip != NULL && ip->val.u32 == thistid) {
566  /* Go to the next header in the rpmdb */
567  niids--;
568  if (niids > 0)
569  ip++;
570  else
571  ip = NULL;
572  }
573  continue; /* with next transaction */
574  }
575  }
576 
577  rpmtsEmpty(ts);
578  (void) rpmtsSetFlags(ts, transFlags);
579  (void) rpmtsSetDFlags(ts, depFlags);
580  ts->probs = rpmpsFree(ts->probs);
581 
582  /* Install the previously erased packages for this transaction.
583  */
584  while (rp != NULL && rp->val.u32 == thistid) {
585  if (!rp->done) {
586  rpmlog(RPMLOG_DEBUG, D_("\t+++ install %s\n"),
587  (rp->key ? rp->key : "???"));
588 
589 /*@-abstract@*/
590  rc = rpmtsAddInstallElement(ts, rp->h, (fnpyKey)rp->key,
591  0, ia->relocations);
592 /*@=abstract@*/
593  if (rc != 0)
594  goto exit;
595 
596  numAdded++;
598  if (!(ia->installInterfaceFlags & ifmask))
601 
602  /* Re-add linked (i.e. from upgrade/obsoletes) erasures. */
603  rc = findErases(ts, ts->teInstall, thistid, ip, niids);
604  if (rc < 0)
605  goto exit;
606 #ifdef NOTYET
607  (void)headerFree(rp->h);
608  rpm->h = NULL;
609 #endif
610  rp->done = 1;
611  }
612 
613  /* Go to the next repackaged package */
614  nrids--;
615  if (nrids > 0)
616  rp++;
617  else
618  rp = NULL;
619  }
620 
621  /* Re-add pure (i.e. not from upgrade/obsoletes) erasures. */
622  rc = findErases(ts, NULL, thistid, ip, niids);
623  if (rc < 0)
624  goto exit;
625 
626  /* Check that all erasures have been re-added. */
627  while (ip != NULL && ip->val.u32 == thistid) {
628 #ifdef NOTNOW
629 /* XXX Prevent incomplete rollback transactions. */
630 assert(ip->done || ia->no_rollback_links);
631 #endif
632  if (!(ip->done || ia->no_rollback_links)) {
633  numRemoved++;
634 
635  if (_unsafe_rollbacks != 0)
637 
638  if (!(ia->installInterfaceFlags & ifmask))
641  }
642 
643  /* Go to the next header in the rpmdb */
644  niids--;
645  if (niids > 0)
646  ip++;
647  else
648  ip = NULL;
649  }
650 
651  /* Print any rollback transaction problems */
652  xx = rpmcliInstallProblems(ts, _("Missing re-packaged package(s)"), 1);
653 
654  /* Anything to do? */
655  if (rpmcliPackagesTotal <= 0)
656  break;
657 
658  tid = (time_t)thistid;
660  _("Rollback packages (+%d/-%d) to %-24.24s (0x%08x):\n"),
661  numAdded, numRemoved, ctime(&tid), thistid);
662 
663  rc = (ia->rbCheck ? (*ia->rbCheck) (ts) : 0);
664  if (rc != 0)
665  goto exit;
666 
667  rc = (ia->rbOrder ? (*ia->rbOrder) (ts) : 0);
668  if (rc != 0)
669  goto exit;
670 
671  /* Drop added/available package indices and dependency sets. */
672  rpmtsClean(ts);
673 
674  /* Print the transaction set. */
675  xx = rpmtsPrint(ts, stdout);
676 
677  rc = (ia->rbRun
679  : 0);
680  if (rc != 0)
681  goto exit;
682 
683  /* Remove repackaged packages after successful reinstall. */
684  if (rtids && !rpmIsDebug()) {
685  int i;
686  rpmlog(RPMLOG_NOTICE, _("Cleaning up repackaged packages:\n"));
687  if (rtids->idt)
688  for (i = 0; i < rtids->nidt; i++) {
689  IDT rrp = rtids->idt + i;
690  if (rrp->val.u32 != thistid)
691  /*@innercontinue@*/ continue;
692  if (rrp->key) { /* XXX can't happen */
693  rpmlog(RPMLOG_NOTICE, _("\tRemoving %s:\n"), rrp->key);
694  (void) unlink(rrp->key); /* XXX: Should check rc??? */
695  }
696  }
697  }
698 
699  /* The rpmdb has changed, so reload installed package chains. */
700  itids = IDTXfree(itids);
701  itids = IDTXload(ts, RPMTAG_INSTALLTID, ia->rbtid);
702  if (itids != NULL) {
703  ip = itids->idt;
704  niids = itids->nidt;
705  } else {
706  ip = NULL;
707  niids = 0;
708  }
709 
710  /* Re-position the iterator at the current install tid. */
711  while (ip != NULL && ip->val.u32 == thistid) {
712  /* Go to the next header in the rpmdb */
713  niids--;
714  if (niids > 0)
715  ip++;
716  else
717  ip = NULL;
718  }
719 
720  } while (1);
721 
722 exit:
723  rtids = IDTXfree(rtids);
724  itids = IDTXfree(itids);
725 
726  rpmtsEmpty(ts);
727  (void) rpmtsSetFlags(ts, transFlags);
728  (void) rpmtsSetDFlags(ts, depFlags);
729 
730  return rc;
731 }