rpm  5.2.1
rpmcache.c
Go to the documentation of this file.
1 
5 #include "system.h"
6 const char *__progname;
7 
8 #include <fnmatch.h>
9 #include <fts.h>
10 
11 #include <rpmio.h>
12 #include <rpmiotypes.h>
13 #include <poptIO.h>
14 
15 #include <rpmtypes.h>
16 #include <rpmtag.h>
17 
18 #include "rpmdb.h"
19 
20 #include "rpmps.h"
21 
22 #include "misc.h" /* XXX rpmMkdirPath */
23 
24 #define _RPMGI_INTERNAL
25 #include <rpmgi.h>
26 
27 #include <rpmcli.h>
28 
29 #include "debug.h"
30 
31 static int _debug = 0;
32 
33 /* XXX should be flag in ts */
34 static int noCache = -1;
35 
36 static ARGV_t ftsSet;
37 
38 const char * bhpath;
39 int bhpathlen = 0;
40 int bhlvl = -1;
41 
42 struct ftsglob_s {
43  const char ** patterns;
44  int fnflags;
45 };
46 
47 static struct ftsglob_s * bhglobs;
48 static int nbhglobs = 5;
49 
50 static int indent = 2;
51 
52 typedef struct Item_s {
53  const char * path;
54  uint32_t size;
55  uint32_t mtime;
56  rpmds this;
58 } * Item;
59 
60 static Item * items = NULL;
61 static int nitems = 0;
62 
63 static inline Item freeItem(Item item) {
64  if (item != NULL) {
65  item->path = _free(item->path);
66  (void)rpmdsFree(item->this);
67  item->this = NULL;
68  (void)headerFree(item->h);
69  item->h = NULL;
70  item = _free(item);
71  }
72  return NULL;
73 }
74 
75 static inline Item newItem(void) {
76  Item item = xcalloc(1, sizeof(*item));
77  return item;
78 }
79 
80 static int cmpItem(const void * a, const void * b) {
81  Item aitem = *(Item *)a;
82  Item bitem = *(Item *)b;
83  int rc = strcmp(rpmdsN(aitem->this), rpmdsN(bitem->this));
84  return rc;
85 }
86 
87 static void freeItems(void) {
88  int i;
89  for (i = 0; i < nitems; i++)
90  items[i] = freeItem(items[i]);
91  items = _free(items);
92  nitems = 0;
93 }
94 
95 static int ftsCachePrint(/*@unused@*/ rpmts ts, FILE * fp)
96 {
97  int rc = 0;
98  int i;
99 
100  if (fp == NULL) fp = stdout;
101  for (i = 0; i < nitems; i++) {
102  Item ip;
103 
104  ip = items[i];
105  if (ip == NULL) {
106  rc = 1;
107  break;
108  }
109 
110  fprintf(fp, "%s\n", ip->path);
111  }
112  return rc;
113 }
114 
115 static int ftsCacheUpdate(rpmts ts)
116 {
117  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
118  uint32_t tid = rpmtsGetTid(ts);
119  rpmmi mi;
120  unsigned char * md5;
121  int rc = 0;
122  int xx;
123  int i;
124 
125  rc = rpmtsCloseDB(ts);
126  rc = rpmDefineMacro(NULL, "_dbpath %{_cache_dbpath}", RMIL_CMDLINE);
127  rc = rpmtsOpenDB(ts, O_RDWR);
128  if (rc != 0)
129  return rc;
130 
131  for (i = 0; i < nitems; i++) {
132  Item ip;
133 
134  ip = items[i];
135  if (ip == NULL) {
136  rc = 1;
137  break;
138  }
139 
140  /* --- Check that identical package is not already cached. */
141  he->tag = RPMTAG_SIGMD5;
142  xx = headerGet(ip->h, he, 0);
143  md5 = he->p.ui8p;
144  if (!xx || md5 == NULL) {
145  md5 = _free(md5);
146  rc = 1;
147  break;
148  }
149  mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, md5, 16);
150  md5 = _free(md5);
151  rc = rpmmiCount(mi);
152  mi = rpmmiFree(mi);
153  if (rc) {
154  rc = 0;
155  continue;
156  }
157 
158  /* --- Add cache tags to new cache header. */
159  he->tag = RPMTAG_CACHECTIME;
160  he->t = RPM_UINT32_TYPE;
161  he->p.ui32p = &tid;
162  he->c = 1;
163  he->append = 1;
164  rc = headerPut(ip->h, he, 0);
165  he->append = 0;
166  if (rc != 1) break;
167 
168  he->tag = RPMTAG_CACHEPKGPATH;
169  he->t = RPM_STRING_ARRAY_TYPE;
170  he->p.argv = &ip->path;
171  he->c = 1;
172  he->append = 1;
173  rc = headerPut(ip->h, he, 0);
174  he->append = 0;
175  if (rc != 1) break;
176 
177  he->tag = RPMTAG_CACHEPKGSIZE;
178  he->t = RPM_UINT32_TYPE;
179  he->p.ui32p = &ip->size;
180  he->c = 1;
181  he->append = 1;
182  rc = headerPut(ip->h, he, 0);
183  he->append = 0;
184  if (rc != 1) break;
185 
187  he->t = RPM_UINT32_TYPE;
188  he->p.ui32p = &ip->mtime;
189  he->c = 1;
190  he->append = 1;
191  rc = headerPut(ip->h, he, 0);
192  he->append = 0;
193  if (rc != 1) break;
194 
195  /* --- Add new cache header to database. */
196  if (!(rpmtsVSFlags(ts) & RPMVSF_NOHDRCHK))
197  rc = rpmdbAdd(rpmtsGetRdb(ts), tid, ip->h, ts);
198  else
199  rc = rpmdbAdd(rpmtsGetRdb(ts), tid, ip->h, NULL);
200  if (rc) break;
201 
202  }
203  xx = rpmtsCloseDB(ts);
204  return rc;
205 }
206 
208 {
209  FTSENT * fts = gi->fts;
210  rpmds add = NULL;
211  struct stat sb, * st;
212  int ec = -1; /* assume not found */
213  int i = 0;
214  int xx;
215 
216  rpmlog(RPMLOG_DEBUG, "============== %s\n", fts->fts_accpath);
217 
218  /* XXX DIEDIEDIE: check platform compatibility. */
219 
221 
222  if (items != NULL && nitems > 0) {
223  Item needle = memset(alloca(sizeof(*needle)), 0, sizeof(*needle));
224  Item * found, * fneedle = &needle;
225 
226  needle->this = add;
227 
228  found = bsearch(fneedle, items, nitems, sizeof(*found), cmpItem);
229 
230  /* Rewind to the first item with same name. */
231  while (found > items && cmpItem(found-1, fneedle) == 0)
232  found--;
233 
234  /* Check that all saved items are newer than this item. */
235  if (found != NULL)
236  while (found < (items + nitems) && cmpItem(found, fneedle) == 0) {
237  ec = rpmdsCompare(needle->this, (*found)->this);
238  if (ec == 0) {
239  found++;
240  continue;
241  }
242  i = found - items;
243  break;
244  }
245  }
246 
247  /*
248  * At this point, ec is
249  * -1 no item with the same name has been seen.
250  * 0 item exists, but already saved item EVR is newer.
251  * 1 item exists, but already saved item EVR is same/older.
252  */
253  if (ec == 0) {
254  goto exit;
255  } else if (ec == 1) {
256  items[i] = freeItem(items[i]);
257  } else {
258  i = nitems++;
259  items = xrealloc(items, nitems * sizeof(*items));
260  }
261 
262  items[i] = newItem();
263  items[i]->path = xstrdup(fts->fts_path);
264  st = fts->fts_statp;
265  if (st == NULL || ((long)st & 0xffff0000) == 0L) {
266  st = &sb;
267  memset(st, 0, sizeof(*st));
268  xx = Stat(fts->fts_accpath, &sb);
269  }
270 
271  if (st != NULL) {
272  items[i]->size = st->st_size;
273  items[i]->mtime = st->st_mtime;
274  }
275  st = NULL;
277  items[i]->h = headerLink(h);
278 
279  if (nitems > 1)
280  qsort(items, nitems, sizeof(*items), cmpItem);
281 
282 #if 0
283  fprintf(stderr, "\t%*s [%d] %s\n",
284  indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
285  i, fts->fts_name);
286 #endif
287 
288 exit:
289  (void)rpmdsFree(add);
290  add = NULL;
291  return (ec ? RPMRC_NOTFOUND : RPMRC_OK);
292 }
293 
294 static const char * ftsInfoStrings[] = {
295  "UNKNOWN",
296  "D",
297  "DC",
298  "DEFAULT",
299  "DNR",
300  "DOT",
301  "DP",
302  "ERR",
303  "F",
304  "INIT",
305  "NS",
306  "NSOK",
307  "SL",
308  "SLNONE",
309  "W",
310 };
311 
312 static const char * ftsInfoStr(int fts_info) {
313  if (!(fts_info >= 1 && fts_info <= 14))
314  fts_info = 0;
315  return ftsInfoStrings[ fts_info ];
316 }
317 
319 {
320  FTS * ftsp = gi->ftsp;
321  FTSENT * fts = gi->fts;
322  struct ftsglob_s * bhg;
323  const char ** patterns;
324  const char * pattern;
325  const char * s;
326  int lvl;
327  int xx;
328 
329  switch (fts->fts_info) {
330  case FTS_D: /* preorder directory */
331  if (fts->fts_pathlen < bhpathlen)
332  break;
333 
334  /* Grab the level of the beehive top directory. */
335  if (bhlvl < 0) {
336  if (fts->fts_pathlen == bhpathlen && !strcmp(fts->fts_path, bhpath))
337  bhlvl = fts->fts_level;
338  else
339  break;
340  }
341  lvl = fts->fts_level - bhlvl;
342 
343  if (lvl < 0)
344  break;
345 
346 #if 0
347  if (_debug)
348  fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
349  indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
350  fts->fts_name);
351 #endif
352 
353  /* Full path glob expression check. */
354  bhg = bhglobs;
355 
356  if ((patterns = bhg->patterns) != NULL)
357  while ((pattern = *patterns++) != NULL) {
358  if (*pattern == '/')
359  xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
360  else
361  xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
362  if (xx == 0)
363  break;
364  }
365 
366  /* Level specific glob expression check(s). */
367  if (lvl == 0 || lvl >= nbhglobs)
368  break;
369  bhg += lvl;
370 
371  if ((patterns = bhg->patterns) != NULL)
372  while ((pattern = *patterns++) != NULL) {
373  if (*pattern == '/')
374  xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
375  else
376  xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
377  if (xx == 0)
378  break;
379  else
380  xx = Fts_set(ftsp, fts, FTS_SKIP);
381  }
382 
383  break;
384  case FTS_DP: /* postorder directory */
385 #if 0
386  if (_debug)
387  fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
388  indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
389  fts->fts_name);
390 #endif
391  break;
392  case FTS_F: /* regular file */
393 #if 0
394  if (_debug)
395  fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
396  indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
397  fts->fts_name);
398 #endif
399  if (fts->fts_level >= 0) {
400  /* Ignore source packages. */
401  if (!strcmp(fts->fts_parent->fts_name, "SRPMS")) {
402  xx = Fts_set(ftsp, fts->fts_parent, FTS_SKIP);
403  break;
404  }
405  }
406 
407  /* Ignore all but *.rpm files. */
408  s = fts->fts_name + fts->fts_namelen + 1 - sizeof(".rpm");
409  if (strcmp(s, ".rpm"))
410  break;
411 
412  break;
413  case FTS_NS: /* stat(2) failed */
414  case FTS_DNR: /* unreadable directory */
415  case FTS_ERR: /* error; errno is set */
416  if (_debug)
417  fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
418  indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
419  fts->fts_name);
420  break;
421  case FTS_DC: /* directory that causes cycles */
422  case FTS_DEFAULT: /* none of the above */
423  case FTS_DOT: /* dot or dot-dot */
424  case FTS_INIT: /* initialized only */
425  case FTS_NSOK: /* no stat(2) requested */
426  case FTS_SL: /* symbolic link */
427  case FTS_SLNONE: /* symbolic link without target */
428  case FTS_W: /* whiteout object */
429  default:
430  if (_debug)
431  fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
432  indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
433  fts->fts_name);
434  break;
435  }
436 
437  return RPMRC_OK;
438 }
439 
445 static void initGlobs(/*@unused@*/ rpmts ts, const char ** argv)
446 {
447  char buf[BUFSIZ];
448  int i;
449 
450  buf[0] = '\0';
451  if (argv != NULL && * argv != NULL) {
452  const char * arg;
453  int single = (Glob_pattern_p(argv[0], 0) && argv[1] == NULL);
454  char * t;
455 
456  t = buf;
457  if (!single)
458  t = stpcpy(t, "@(");
459  while ((arg = *argv++) != NULL) {
460  t = stpcpy(t, arg);
461  *t++ = '|';
462  }
463  t[-1] = (char)(single ? '\0' : ')');
464  *t = '\0';
465  }
466 
467  bhpath = rpmExpand("%{_bhpath}", NULL);
468  bhpathlen = strlen(bhpath);
469 
470  ftsSet = xcalloc(2, sizeof(*ftsSet));
471  ftsSet[0] = rpmExpand("%{_bhpath}", NULL);
472 
473  nbhglobs = 5;
474  bhglobs = xcalloc(nbhglobs, sizeof(*bhglobs));
475  for (i = 0; i < nbhglobs; i++) {
476  const char * pattern;
477  const char * macro;
478 
479  switch (i) {
480  case 0:
481  macro = "%{_bhpath}";
482  break;
483  case 1:
484  macro = "%{_bhcoll}";
485  break;
486  case 2:
487  macro = (buf[0] == '\0' ? "%{_bhN}" : buf);
488  break;
489  case 3:
490  macro = "%{_bhVR}";
491  break;
492  case 4:
493  macro = "%{_bhA}";
494  break;
495  default:
496  macro = NULL;
497  break;
498  }
499  bhglobs[i].patterns = xcalloc(2, sizeof(*bhglobs[i].patterns));
500  if (macro == NULL)
501  continue;
502  pattern = rpmExpand(macro, NULL);
503  if (pattern == NULL || *pattern == '\0') {
504  pattern = _free(pattern);
505  continue;
506  }
507  bhglobs[i].patterns[0] = pattern;
508  bhglobs[i].fnflags = (FNM_PATHNAME | FNM_PERIOD | FNM_EXTMATCH);
509  if (bhglobs[i].patterns[0] != NULL)
510  rpmlog(RPMLOG_DEBUG, "\t%d \"%s\"\n",
511  i, bhglobs[i].patterns[0]);
512  }
513 }
514 
515 static void freeGlobs(void)
516 {
517  int i;
518  for (i = 0; i < nbhglobs; i++) {
519  bhglobs[i].patterns[0] = _free(bhglobs[i].patterns[0]);
520  bhglobs[i].patterns = _free(bhglobs[i].patterns);
521  }
522  bhglobs = _free(bhglobs);
523  ftsSet[0] = _free(ftsSet[0]);
524  ftsSet = _free(ftsSet);
525 }
526 
527 static rpmVSFlags vsflags = 0;
528 
529 static struct poptOption optionsTable[] = {
530  { "nolegacy", '\0', POPT_BIT_SET, &vsflags, RPMVSF_NEEDPAYLOAD,
531  N_("don't verify header+payload signature"), NULL },
532 
533  { "cache", '\0', POPT_ARG_VAL, &noCache, 0,
534  N_("update cache database"), NULL },
535  { "nocache", '\0', POPT_ARG_VAL, &noCache, -1,
536  N_("don't update cache database, only print package paths"), NULL },
537 
538  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0,
539  N_("File tree walk options:"),
540  NULL },
541 
542  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
543  N_("Common options for all rpm modes and executables:"),
544  NULL },
545 
546  POPT_AUTOALIAS
547  POPT_AUTOHELP
548  POPT_TABLEEND
549 };
550 
551 int
552 main(int argc, char *argv[])
553 {
554  poptContext optCon = rpmcliInit(argc, argv, optionsTable);
555  rpmts ts = NULL;
556  rpmgi gi = NULL;
557  const char * s;
558  int ec = 1;
559  rpmRC rpmrc;
560  int xx;
561 
562  if (optCon == NULL)
563  exit(EXIT_FAILURE);
564 
565  /* Configure the path to cache database, creating if necessary. */
566  s = rpmExpand("%{?_cache_dbpath}", NULL);
567  if (!(s && *s))
568  rpmrc = RPMRC_FAIL;
569  else
570  rpmrc = rpmMkdirPath(s, "cache_dbpath");
571  if (rpmrc == RPMRC_OK && Access(s, W_OK))
572  rpmrc = RPMRC_FAIL;
573  s = _free(s);
574  if (rpmrc != RPMRC_OK) {
575  fprintf(stderr, _("%s: %%{_cache_dbpath} macro is mis-configured.\n"),
576  __progname);
577  exit(EXIT_FAILURE);
578  }
579 
580  ts = rpmtsCreate();
581 
588  (void) rpmtsSetVSFlags(ts, vsflags);
589 
590  { uint32_t tid = (uint32_t) time(NULL);
591  (void) rpmtsSetTid(ts, tid);
592  }
593 
594  initGlobs(ts, poptGetArgs(optCon));
595 
596  gi = rpmgiNew(ts, RPMDBI_FTSWALK, NULL, 0);
597 
598  if (rpmioFtsOpts == 0)
600 
601  if (noCache)
603  else
605 
607 
608  gi->walkPathFilter = cacheWalkPathFilter;
609  gi->stash = cacheStashLatest;
610  while ((rpmrc = rpmgiNext(gi)) == RPMRC_OK)
611  {};
612 
613  if (noCache)
614  ec = ftsCachePrint(ts, stdout);
615  else
616  ec = ftsCacheUpdate(ts);
617  if (ec) {
618  fprintf(stderr, _("%s: cache operation failed: ec %d.\n"),
619  __progname, ec);
620  }
621 
622  freeItems();
623  freeGlobs();
624 
625  gi = rpmgiFree(gi);
626  (void)rpmtsFree(ts);
627  ts = NULL;
628  optCon = rpmcliFini(optCon);
629 
630  return ec;
631 }