rpm 5.2.1

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

- File List
- Globals

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++) {
258 rpmpsAppend(ps, RPMPROB_NOREPACKAGE,
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 {
400 int ifmask= (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL|INSTALL_ERASE);
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 */
442 rpmtsSetType(ts, RPMTRANS_TYPE_ROLLBACK);
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;
481 rpmcliPackagesTotal = 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;
511 rpmlog(RPMLOG_NOTICE,
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++;
561 rpmcliPackagesTotal++;
562 if (!(ia->installInterfaceFlags & ifmask))
563 ia->installInterfaceFlags |= INSTALL_UPGRADE;
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)
599 rpmcliPackagesTotal++;
600
601 if (!(ia->installInterfaceFlags & ifmask))
602 ia->installInterfaceFlags |= INSTALL_ERASE;
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;
621 rpmlog(RPMLOG_NOTICE,
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 }