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