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