Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / rpmgi.c
1 /** \ingroup rpmio
2  * \file lib/rpmgi.c
3  */
4 #include "system.h"
5
6 #include <rpm/rpmtypes.h>
7 #include <rpm/rpmlib.h>         /* rpmReadPackageFile */
8 #include <rpm/rpmte.h>          /* XXX rpmElementType */
9 #include <rpm/rpmts.h>
10 #include <rpm/rpmgi.h>
11 #include <rpm/rpmdb.h>
12 #include <rpm/rpmmacro.h>               /* XXX rpmExpand */
13 #include <rpm/rpmfileutil.h>
14 #include <rpm/rpmlog.h>
15
16 #include "rpmio/fts.h"
17 #include "lib/manifest.h"
18
19 #include "debug.h"
20
21 int _rpmgi_debug = 0;
22
23 rpmgiFlags giFlags = RPMGI_NONE;
24
25 /** \ingroup rpmgi
26  */
27 struct rpmgi_s {
28     rpmts ts;                   /*!< Iterator transaction set. */
29     rpmTag tag;         /*!< Iterator type. */
30     const void * keyp;          /*!< Iterator key. */
31     size_t keylen;              /*!< Iterator key length. */
32
33     rpmgiFlags flags;           /*!< Iterator control bits. */
34     int active;                 /*!< Iterator is active? */
35     int i;                      /*!< Element index. */
36     int errors;
37     char * hdrPath;             /*!< Path to current iterator header. */
38     Header h;                   /*!< Current iterator header. */
39
40     rpmtsi tsi;
41
42     rpmdbMatchIterator mi;
43
44     FD_t fd;
45
46     ARGV_t argv;
47     int argc;
48
49     int ftsOpts;
50     FTS * ftsp;
51     FTSENT * fts;
52
53     int nrefs;                  /*!< Reference count. */
54 };
55
56 static const char * const ftsInfoStrings[] = {
57     "UNKNOWN",
58     "D",
59     "DC",
60     "DEFAULT",
61     "DNR",
62     "DOT",
63     "DP",
64     "ERR",
65     "F",
66     "INIT",
67     "NS",
68     "NSOK",
69     "SL",
70     "SLNONE",
71     "W",
72 };
73
74 static const char * ftsInfoStr(int fts_info)
75 {
76
77     if (!(fts_info >= 1 && fts_info <= 14))
78         fts_info = 0;
79     return ftsInfoStrings[ fts_info ];
80 }
81
82 /**
83  * Open a file after macro expanding path.
84  * @todo There are two error messages printed on header, then manifest failures.
85  * @param path          file path
86  * @param fmode         open mode
87  * @return              file handle
88  */
89 static FD_t rpmgiOpen(const char * path, const char * fmode)
90 {
91     char * fn = rpmExpand(path, NULL);
92     FD_t fd = Fopen(fn, fmode);
93
94     if (fd == NULL || Ferror(fd)) {
95         rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), fn, Fstrerror(fd));
96         if (fd != NULL) (void) Fclose(fd);
97         fd = NULL;
98     }
99     fn = _free(fn);
100
101     return fd;
102 }
103
104 /**
105  * Load manifest into iterator arg list.
106  * @param gi            generalized iterator
107  * @param path          file path
108  * @return              RPMRC_OK on success
109  */
110 static rpmRC rpmgiLoadManifest(rpmgi gi, const char * path)
111 {
112     FD_t fd = rpmgiOpen(path, "r.ufdio");
113     rpmRC rpmrc = RPMRC_FAIL;
114
115     if (fd != NULL) {
116         rpmrc = rpmReadPackageManifest(fd, &gi->argc, &gi->argv);
117         (void) Fclose(fd);
118     }
119     return rpmrc;
120 }
121
122 /**
123  * Return header from package.
124  * @param gi            generalized iterator
125  * @param path          file path
126  * @return              header (NULL on failure)
127  */
128 static Header rpmgiReadHeader(rpmgi gi, const char * path)
129 {
130     FD_t fd = rpmgiOpen(path, "r.ufdio");
131     Header h = NULL;
132
133     if (fd != NULL) {
134         /* XXX what if path needs expansion? */
135         rpmRC rpmrc = rpmReadPackageFile(gi->ts, fd, path, &h);
136
137         (void) Fclose(fd);
138
139         switch (rpmrc) {
140         case RPMRC_NOTFOUND:
141             /* XXX Read a package manifest. Restart ftswalk on success. */
142         case RPMRC_FAIL:
143         default:
144             h = headerFree(h);
145             break;
146         case RPMRC_NOTTRUSTED:
147         case RPMRC_NOKEY:
148         case RPMRC_OK:
149             break;
150         }
151     }
152
153     return h;
154 }
155
156 /**
157  * Read next header from package, lazily expanding manifests as found.
158  * @todo An empty file read as manifest truncates argv returning RPMRC_NOTFOUND.
159  * @todo Chained manifests lose an arg someplace.
160  * @param gi            generalized iterator
161  * @return              RPMRC_OK on success
162  */
163 static rpmRC rpmgiLoadReadHeader(rpmgi gi)
164 {
165     rpmRC rpmrc = RPMRC_NOTFOUND;
166     Header h = NULL;
167
168     if (gi->argv != NULL && gi->argv[gi->i] != NULL)
169     do {
170         char * fn;      /* XXX gi->hdrPath? */
171
172         fn = gi->argv[gi->i];
173         if (!(gi->flags & RPMGI_NOHEADER)) {
174             h = rpmgiReadHeader(gi, fn);
175             if (h != NULL)
176                 rpmrc = RPMRC_OK;
177         } else
178             rpmrc = RPMRC_OK;
179
180         if (rpmrc == RPMRC_OK || gi->flags & RPMGI_NOMANIFEST)
181             break;
182         if (errno == ENOENT) {
183             break;
184         }
185
186         /* Not a header, so try for a manifest. */
187         gi->argv[gi->i] = NULL;         /* Mark the insertion point */
188         rpmrc = rpmgiLoadManifest(gi, fn);
189         if (rpmrc != RPMRC_OK) {
190             gi->argv[gi->i] = fn;       /* Manifest failed, restore fn */
191             rpmlog(RPMLOG_ERR, 
192                    _("%s: not an rpm package (or package manifest)\n"), fn);
193             break;
194         }
195         fn = _free(fn);
196         rpmrc = RPMRC_NOTFOUND;
197     } while (1);
198
199     if (rpmrc == RPMRC_OK && h != NULL)
200         gi->h = headerLink(h);
201     h = headerFree(h);
202
203     return rpmrc;
204 }
205
206 /**
207  * Filter file tree walk path.
208  * @param gi            generalized iterator
209  * @return              RPMRC_OK on success
210  */
211 static rpmRC rpmgiWalkPathFilter(rpmgi gi)
212 {
213     FTSENT * fts = gi->fts;
214     rpmRC rpmrc = RPMRC_NOTFOUND;
215     static const int indent = 2;
216
217
218 if (_rpmgi_debug < 0)
219 rpmlog(RPMLOG_DEBUG, "FTS_%s\t%*s %s%s\n", ftsInfoStr(fts->fts_info),
220                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
221                 fts->fts_name,
222         ((fts->fts_info == FTS_D || fts->fts_info == FTS_DP) ? "/" : ""));
223
224     switch (fts->fts_info) {
225     case FTS_F:
226         /* Ignore all but *.rpm files. */
227         if (!rpmFileHasSuffix(fts->fts_name, ".rpm"))
228             break;
229         rpmrc = RPMRC_OK;
230         break;
231     default:
232         break;
233     }
234     return rpmrc;
235 }
236
237 /**
238  * Read header from next package, lazily walking file tree.
239  * @param gi            generalized iterator
240  * @return              RPMRC_OK on success
241  */
242 static rpmRC rpmgiWalkReadHeader(rpmgi gi)
243 {
244     rpmRC rpmrc = RPMRC_NOTFOUND;
245
246     if (gi->ftsp != NULL)
247     while ((gi->fts = Fts_read(gi->ftsp)) != NULL) {
248         rpmrc = rpmgiWalkPathFilter(gi);
249         if (rpmrc == RPMRC_OK)
250             break;
251     }
252
253     if (rpmrc == RPMRC_OK) {
254         Header h = NULL;
255         if (!(gi->flags & RPMGI_NOHEADER)) {
256             /* XXX rpmrc = rpmgiLoadReadHeader(gi); */
257             if (gi->fts != NULL)        /* XXX can't happen */
258                 h = rpmgiReadHeader(gi, gi->fts->fts_path);
259         }
260         if (h != NULL)
261             gi->h = headerLink(h);
262         h = headerFree(h);
263     }
264
265     return rpmrc;
266 }
267
268 /**
269  * Append globbed arg list to iterator.
270  * @param gi            generalized iterator
271  * @param argv          arg list to be globbed (or NULL)
272  * @returns             RPMRC_OK on success
273  */
274 static rpmRC rpmgiGlobArgv(rpmgi gi, ARGV_const_t argv)
275 {
276     const char * arg;
277     rpmRC rpmrc = RPMRC_OK;
278     int ac = 0;
279     int xx;
280
281     /* XXX Expand globs only if requested or for gi specific tags */
282     if ((gi->flags & RPMGI_NOGLOB)
283      || !(gi->tag == RPMDBI_HDLIST || gi->tag == RPMDBI_ARGLIST || gi->tag == RPMDBI_FTSWALK))
284     {
285         if (argv != NULL) {
286             while (argv[ac] != NULL)
287                 ac++;
288 /* XXX argv is not NULL */
289             xx = argvAppend(&gi->argv, argv);
290         }
291         gi->argc = ac;
292         return rpmrc;
293     }
294
295     if (argv != NULL)
296     while ((arg = *argv++) != NULL) {
297         char * t = rpmEscapeSpaces(arg);
298         char ** av = NULL;
299
300         xx = rpmGlob(t, &ac, &av);
301         xx = argvAppend(&gi->argv, av);
302         gi->argc += ac;
303         av = argvFree(av);
304         t = _free(t);
305         ac = 0;
306     }
307     return rpmrc;
308 }
309
310 /**
311  * Return rpmdb match iterator with filters (if any) set.
312  * @param gi            generalized iterator
313  * @returns             RPMRC_OK on success
314  */
315 static rpmRC rpmgiInitFilter(rpmgi gi)
316 {
317     rpmRC rpmrc = RPMRC_OK;
318     ARGV_t av;
319     int res = 0;
320
321     gi->mi = rpmtsInitIterator(gi->ts, gi->tag, gi->keyp, gi->keylen);
322
323 if (_rpmgi_debug < 0)
324 fprintf(stderr, "*** gi %p\tmi %p\n", gi, gi->mi);
325
326     if (gi->argv != NULL)
327     for (av = gi->argv; *av != NULL; av++) {
328         rpmTag tag = RPMTAG_NAME;
329         const char * pat;
330         char * a, * ae;
331
332         pat = a = xstrdup(*av);
333         tag = RPMTAG_NAME;
334
335         /* Parse for "tag=pattern" args. */
336         if ((ae = strchr(a, '=')) != NULL) {
337             *ae++ = '\0';
338             tag = rpmTagGetValue(a);
339             if (tag == RPMTAG_NOT_FOUND) {
340                 rpmlog(RPMLOG_ERR, _("unknown tag: \"%s\"\n"), a);
341                 res = 1;
342             }
343             pat = ae;
344         }
345
346         if (!res) {
347 if (_rpmgi_debug  < 0)
348 fprintf(stderr, "\tav %p[%ld]: \"%s\" -> %s ~= \"%s\"\n", gi->argv, (long) (av - gi->argv), *av, rpmTagGetName(tag), pat);
349             res = rpmdbSetIteratorRE(gi->mi, tag, RPMMIRE_DEFAULT, pat);
350         }
351         a = _free(a);
352
353         if (res == 0)
354             continue;
355
356         rpmrc = RPMRC_FAIL;
357         break;
358     }
359
360     return rpmrc;
361 }
362
363 rpmgi rpmgiUnlink(rpmgi gi, const char * msg)
364 {
365     if (gi == NULL) return NULL;
366
367 if (_rpmgi_debug && msg != NULL)
368 fprintf(stderr, "--> gi %p -- %d: %s\n", gi, gi->nrefs, msg);
369
370     gi->nrefs--;
371     return NULL;
372 }
373
374 rpmgi rpmgiLink(rpmgi gi, const char * msg)
375 {
376     if (gi == NULL) return NULL;
377     gi->nrefs++;
378
379 if (_rpmgi_debug && msg != NULL)
380 fprintf(stderr, "--> gi %p ++ %d: %s\n", gi, gi->nrefs, msg);
381
382     return gi;
383 }
384
385 rpmgi rpmgiFree(rpmgi gi)
386 {
387     if (gi == NULL)
388         return NULL;
389
390     if (gi->nrefs > 1)
391         return rpmgiUnlink(gi, __FUNCTION__);
392
393     (void) rpmgiUnlink(gi, __FUNCTION__);
394
395
396     gi->hdrPath = _free(gi->hdrPath);
397     gi->h = headerFree(gi->h);
398
399     gi->argv = argvFree(gi->argv);
400
401     if (gi->ftsp != NULL) {
402         int xx;
403         xx = Fts_close(gi->ftsp);
404         gi->ftsp = NULL;
405         gi->fts = NULL;
406     }
407     if (gi->fd != NULL) {
408         (void) Fclose(gi->fd);
409         gi->fd = NULL;
410     }
411     gi->tsi = rpmtsiFree(gi->tsi);
412     gi->mi = rpmdbFreeIterator(gi->mi);
413     gi->ts = rpmtsFree(gi->ts);
414
415     memset(gi, 0, sizeof(*gi));         /* XXX trash and burn */
416     gi = _free(gi);
417     return NULL;
418 }
419
420 rpmgi rpmgiNew(rpmts ts, rpmTag tag, const void * keyp, size_t keylen)
421 {
422     rpmgi gi = xcalloc(1, sizeof(*gi));
423
424     if (gi == NULL)
425         return NULL;
426
427     gi->ts = rpmtsLink(ts, __FUNCTION__);
428     gi->tag = tag;
429     gi->keyp = keyp;
430     gi->keylen = keylen;
431
432     gi->flags = 0;
433     gi->active = 0;
434     gi->i = -1;
435     gi->errors = 0;
436     gi->hdrPath = NULL;
437     gi->h = NULL;
438
439     gi->tsi = NULL;
440     gi->mi = NULL;
441     gi->fd = NULL;
442     gi->argv = argvNew();
443     gi->argc = 0;
444     gi->ftsOpts = 0;
445     gi->ftsp = NULL;
446     gi->fts = NULL;
447
448     gi = rpmgiLink(gi, __FUNCTION__);
449
450     return gi;
451 }
452
453 rpmRC rpmgiNext(rpmgi gi)
454 {
455     char hnum[32];
456     rpmRC rpmrc = RPMRC_NOTFOUND;
457     int xx;
458
459     if (gi == NULL)
460         return rpmrc;
461
462     /* Free header from previous iteration. */
463     gi->h = headerFree(gi->h);
464     gi->hdrPath = _free(gi->hdrPath);
465     hnum[0] = '\0';
466
467     if (++gi->i >= 0)
468     switch (gi->tag) {
469     default:
470     case RPMDBI_PACKAGES:
471         if (!gi->active) {
472             rpmrc = rpmgiInitFilter(gi);
473             if (rpmrc != RPMRC_OK)
474                 goto enditer;
475             gi->active = 1;
476         }
477         if (gi->mi != NULL) {   /* XXX unnecessary */
478             Header h = rpmdbNextIterator(gi->mi);
479             if (h != NULL) {
480                 if (!(gi->flags & RPMGI_NOHEADER))
481                     gi->h = headerLink(h);
482                 sprintf(hnum, "%u", rpmdbGetIteratorOffset(gi->mi));
483                 gi->hdrPath = rpmExpand("rpmdb h# ", hnum, NULL);
484                 rpmrc = RPMRC_OK;
485                 /* XXX header reference held by iterator, so no headerFree */
486             } else
487                 rpmrc = RPMRC_NOTFOUND;
488         }
489         if (rpmrc != RPMRC_OK) {
490             goto enditer;
491         }
492         break;
493     case RPMDBI_ADDED:
494     {   rpmte p;
495
496         if (!gi->active) {
497             gi->tsi = rpmtsiInit(gi->ts);
498             gi->active = 1;
499         }
500         if ((p = rpmtsiNext(gi->tsi, TR_ADDED)) != NULL) {
501             Header h = rpmteHeader(p);
502             if (h != NULL)
503                 if (!(gi->flags & RPMGI_NOHEADER)) {
504                     gi->h = headerLink(h);
505                 sprintf(hnum, "%u", (unsigned)gi->i);
506                 gi->hdrPath = rpmExpand("added h# ", hnum, NULL);
507                 rpmrc = RPMRC_OK;
508                 h = headerFree(h);
509             }
510         }
511         if (rpmrc != RPMRC_OK) {
512             gi->tsi = rpmtsiFree(gi->tsi);
513             goto enditer;
514         }
515     }   break;
516     case RPMDBI_HDLIST:
517         if (!gi->active) {
518             const char * path = "/usr/share/comps/%{_arch}/hdlist";
519             gi->fd = rpmgiOpen(path, "r.ufdio");
520             gi->active = 1;
521         }
522         if (gi->fd != NULL) {
523             Header h = headerRead(gi->fd, HEADER_MAGIC_YES);
524             if (h != NULL) {
525                 if (!(gi->flags & RPMGI_NOHEADER))
526                     gi->h = headerLink(h);
527                 sprintf(hnum, "%u", (unsigned)gi->i);
528                 gi->hdrPath = rpmExpand("hdlist h# ", hnum, NULL);
529                 rpmrc = RPMRC_OK;
530                 h = headerFree(h);
531             }
532         }
533         if (rpmrc != RPMRC_OK) {
534             if (gi->fd != NULL) (void) Fclose(gi->fd);
535             gi->fd = NULL;
536             goto enditer;
537         }
538         break;
539     case RPMDBI_ARGLIST: 
540         /* XXX gi->active initialize? */
541 if (_rpmgi_debug  < 0)
542 fprintf(stderr, "*** gi %p\t%p[%d]: %s\n", gi, gi->argv, gi->i, gi->argv[gi->i]);
543
544         /* 
545          * Read next header, lazily expanding manifests as found,
546          * count + skip errors.
547          */
548         rpmrc = RPMRC_NOTFOUND; 
549         while (gi->i < gi->argc) {
550             if ((rpmrc = rpmgiLoadReadHeader(gi)) == RPMRC_OK) 
551                 break;
552             gi->errors++;
553             gi->i++;
554         }
555
556         if (rpmrc != RPMRC_OK)  /* XXX check this */
557             goto enditer;
558
559         gi->hdrPath = xstrdup(gi->argv[gi->i]);
560         break;
561     case RPMDBI_FTSWALK:
562         if (gi->argv == NULL)           /* HACK */
563             goto enditer;
564
565         if (!gi->active) {
566             gi->ftsp = Fts_open((char *const *)gi->argv, gi->ftsOpts, NULL);
567             /* XXX NULL with open(2)/malloc(3) errno set */
568             gi->active = 1;
569         }
570
571         /* Read next header, lazily walking file tree. */
572         rpmrc = rpmgiWalkReadHeader(gi);
573
574         if (rpmrc != RPMRC_OK) {
575             xx = Fts_close(gi->ftsp);
576             gi->ftsp = NULL;
577             goto enditer;
578         }
579
580         if (gi->fts != NULL)
581             gi->hdrPath = xstrdup(gi->fts->fts_path);
582         break;
583     }
584
585     if ((gi->flags & RPMGI_TSADD) && gi->h != NULL) {
586         /* XXX rpmgi hack: Save header in transaction element. */
587         xx = rpmtsAddInstallElement(gi->ts, gi->h, (fnpyKey)gi->hdrPath, 2, NULL);
588     }
589
590     return rpmrc;
591
592 enditer:
593     gi->mi = rpmdbFreeIterator(gi->mi);
594     if (gi->flags & RPMGI_TSORDER) {
595         rpmts ts = gi->ts;
596         rpmps ps;
597
598         /* XXX installed database needs close here. */
599         xx = rpmtsCloseDB(ts);
600         xx = rpmtsSetDBMode(ts, -1);    /* XXX disable lazy opens */
601
602         xx = rpmtsCheck(ts);
603
604         /* XXX query/verify will need the glop added to a buffer instead. */
605         ps = rpmtsProblems(ts);
606         if (rpmpsNumProblems(ps) > 0) {
607             /* XXX rpminstall will need RPMLOG_ERR */
608             rpmlog(RPMLOG_INFO, _("Failed dependencies:\n"));
609             if (rpmIsVerbose())
610                 rpmpsPrint(NULL, ps);
611         }
612         ps = rpmpsFree(ps);
613         rpmtsCleanProblems(ts);
614
615         xx = rpmtsOrder(ts);
616
617         gi->tag = RPMDBI_ADDED;                 /* XXX hackery */
618         gi->flags &= ~(RPMGI_TSADD|RPMGI_TSORDER);
619
620     }
621
622     gi->h = headerFree(gi->h);
623     gi->hdrPath = _free(gi->hdrPath);
624     gi->i = -1;
625     gi->active = 0;
626     return rpmrc;
627 }
628
629 const char * rpmgiHdrPath(rpmgi gi)
630 {
631     return (gi != NULL ? gi->hdrPath : NULL);
632 }
633
634 Header rpmgiHeader(rpmgi gi)
635 {
636     return (gi != NULL ? gi->h : NULL);
637 }
638
639 rpmts rpmgiTs(rpmgi gi)
640 {
641     return (gi != NULL ? gi->ts : NULL);
642 }
643
644 rpmRC rpmgiSetArgs(rpmgi gi, ARGV_const_t argv, int ftsOpts, rpmgiFlags flags)
645 {
646     gi->ftsOpts = ftsOpts;
647     gi->flags = flags;
648     return rpmgiGlobArgv(gi, argv);
649 }
650
651 rpmgiFlags rpmgiGetFlags(rpmgi gi)
652 {
653     return (gi != NULL ? gi->flags : RPMGI_NONE);
654 }
655
656 int rpmgiNumErrors(rpmgi gi)
657 {
658     return (gi != NULL ? gi->errors : -1);
659 }