1 #include "system.h"
2
3 #include <errno.h>
4 #include <libgen.h>
5 #include <sys/select.h>
6 #include <sys/wait.h>
7 #include <signal.h>
8 #include <fcntl.h>
9 #include <magic.h>
10 #include <regex.h>
11 #ifdef HAVE_LIBELF
12 #include <gelf.h>
13 #endif
14
15 #include <rpm/header.h>
16 #include <rpm/argv.h>
17 #include <rpm/rpmfc.h>
18 #include <rpm/rpmlog.h>
19 #include <rpm/rpmfileutil.h>
20 #include <rpm/rpmds.h>
21 #include <rpm/rpmfi.h>
22 #include <rpm/rpmstrpool.h>
23
24 #include "lib/rpmfi_internal.h" /* rpmfiles stuff for now */
25 #include "build/rpmbuild_internal.h"
26
27 #include "debug.h"
28
29 struct matchRule {
30 regex_t *path;
31 regex_t *magic;
32 regex_t *mime;
33 ARGV_t flags;
34 };
35
36 typedef struct rpmfcAttr_s {
37 char *name;
38 struct matchRule incl;
39 struct matchRule excl;
40 } * rpmfcAttr;
41
42 typedef struct {
43 int fileIx;
44 rpmds dep;
45 } rpmfcFileDep;
46
47 typedef struct {
48 rpmfcFileDep *data;
49 int size;
50 int alloced;
51 } rpmfcFileDeps;
52
53 #undef HASHTYPE
54 #undef HTKEYTYPE
55 #undef HTDATATYPE
56 #define HASHTYPE fattrHash
57 #define HTKEYTYPE int
58 #define HTDATATYPE int
59 #include "lib/rpmhash.H"
60 #include "lib/rpmhash.C"
61
62 struct rpmfc_s {
63 Package pkg;
64 int nfiles; /*!< no. of files */
65 int fknown; /*!< no. of classified files */
66 int fwhite; /*!< no. of "white" files */
67 int skipProv; /*!< Don't auto-generate Provides:? */
68 int skipReq; /*!< Don't auto-generate Requires:? */
69 char *buildRoot; /*!< (Build) root dir */
70 size_t brlen; /*!< rootDir length */
71
72 rpmfcAttr *atypes; /*!< known file attribute types */
73
74 char ** fn; /*!< (no. files) file names */
75 char ** ftype; /*!< (no. files) file types */
76 ARGV_t *fattrs; /*!< (no. files) file attribute tokens */
77 rpm_color_t *fcolor;/*!< (no. files) file colors */
78 rpmsid *fcdictx; /*!< (no. files) file class dictionary indices */
79 ARGI_t fddictx; /*!< (no. files) file depends dictionary start */
80 ARGI_t fddictn; /*!< (no. files) file depends dictionary no. entries */
81 ARGI_t ddictx; /*!< (no. dependencies) file->dependency mapping */
82 rpmstrPool cdict; /*!< file class dictionary */
83 rpmfcFileDeps fileDeps; /*!< file dependency mapping */
84
85 fattrHash fahash; /*!< attr:file mapping */
86 rpmstrPool pool; /*!< general purpose string storage */
87 };
88
89 struct rpmfcTokens_s {
90 const char * token;
91 rpm_color_t colors;
92 };
93
intCmp(int a,int b)94 static int intCmp(int a, int b)
95 {
96 return (a != b);
97 }
98
intId(int a)99 static unsigned int intId(int a)
100 {
101 return a;
102 }
103
104
regMatch(regex_t * reg,const char * val)105 static int regMatch(regex_t *reg, const char *val)
106 {
107 return (reg && regexec(reg, val, 0, NULL, 0) == 0);
108 }
109
regFree(regex_t * reg)110 static regex_t * regFree(regex_t *reg)
111 {
112 if (reg) {
113 regfree(reg);
114 free(reg);
115 }
116 return NULL;
117 }
118
ruleFree(struct matchRule * rule)119 static void ruleFree(struct matchRule *rule)
120 {
121 regFree(rule->path);
122 regFree(rule->magic);
123 regFree(rule->mime);
124 argvFree(rule->flags);
125 }
126
rpmfcAttrMacroV(const char * arg,va_list args)127 static char *rpmfcAttrMacroV(const char *arg, va_list args)
128 {
129 const char *s;
130 int blen;
131 char *buf = NULL, *obuf;
132 char *pe;
133 va_list args2;
134
135 if (arg == NULL || rstreq(arg, ""))
136 return NULL;
137
138 va_copy(args2, args);
139 blen = sizeof("%{?_") - 1;
140 for (s = arg; s != NULL; s = va_arg(args, const char *)) {
141 blen += sizeof("_") - 1 + strlen(s);
142 }
143 blen += sizeof("}") - 1;
144
145 buf = xmalloc(blen + 1);
146
147 pe = buf;
148 pe = stpcpy(pe, "%{?_");
149 for (s = arg; s != NULL; s = va_arg(args2, const char *)) {
150 *pe++ = '_';
151 pe = stpcpy(pe, s);
152 }
153 va_end(args2);
154 *pe++ = '}';
155 *pe = '\0';
156
157 obuf = rpmExpand(buf, NULL);
158 free(buf);
159
160 return rstreq(obuf, "") ? _free(obuf) : obuf;
161 }
162
rpmfcAttrMacro(const char * arg,...)163 static char *rpmfcAttrMacro(const char *arg, ...)
164 {
165 va_list args;
166 char *s;
167
168 va_start(args, arg);
169 s = rpmfcAttrMacroV(arg, args);
170 va_end(args);
171 return s;
172 }
173
rpmfcAttrReg(const char * arg,...)174 static regex_t *rpmfcAttrReg(const char *arg, ...)
175 {
176 regex_t *reg = NULL;
177 char *pattern;
178 va_list args;
179
180 va_start(args, arg);
181 pattern = rpmfcAttrMacroV(arg, args);
182 va_end(args);
183 if (pattern) {
184 reg = xcalloc(1, sizeof(*reg));
185 if (regcomp(reg, pattern, REG_EXTENDED) != 0) {
186 rpmlog(RPMLOG_WARNING, _("Ignoring invalid regex %s\n"), pattern);
187 reg = _free(reg);
188 }
189 rfree(pattern);
190 }
191 return reg;
192 }
193
rpmfcAttrNew(const char * name)194 static rpmfcAttr rpmfcAttrNew(const char *name)
195 {
196 rpmfcAttr attr = xcalloc(1, sizeof(*attr));
197 struct matchRule *rules[] = { &attr->incl, &attr->excl, NULL };
198
199 attr->name = xstrdup(name);
200 for (struct matchRule **rule = rules; rule && *rule; rule++) {
201 const char *prefix = (*rule == &attr->incl) ? NULL : "exclude";
202 char *flags;
203
204 if (prefix) {
205 flags = rpmfcAttrMacro(name, prefix, "flags", NULL);
206
207 (*rule)->path = rpmfcAttrReg(name, prefix, "path", NULL);
208 (*rule)->magic = rpmfcAttrReg(name, prefix, "magic", NULL);
209 (*rule)->mime = rpmfcAttrReg(name, prefix, "mime", NULL);
210 } else {
211 flags = rpmfcAttrMacro(name, "flags", NULL);
212
213 (*rule)->path = rpmfcAttrReg(name, "path", NULL);
214 (*rule)->magic = rpmfcAttrReg(name, "magic", NULL);
215 (*rule)->mime = rpmfcAttrReg(name, "mime", NULL);
216 }
217 if ((*rule)->magic && (*rule)->mime) {
218 rpmlog(RPMLOG_WARNING,
219 _("%s: mime and magic supplied, only mime will be used\n"),
220 name);
221 }
222
223
224 (*rule)->flags = argvSplitString(flags, ",", ARGV_SKIPEMPTY);
225 argvSort((*rule)->flags, NULL);
226
227 free(flags);
228 }
229
230 return attr;
231 }
232
rpmfcAttrFree(rpmfcAttr attr)233 static rpmfcAttr rpmfcAttrFree(rpmfcAttr attr)
234 {
235 if (attr) {
236 ruleFree(&attr->incl);
237 ruleFree(&attr->excl);
238 rfree(attr->name);
239 rfree(attr);
240 }
241 return NULL;
242 }
243
rpmfcExpandAppend(ARGV_t * argvp,ARGV_const_t av)244 static int rpmfcExpandAppend(ARGV_t * argvp, ARGV_const_t av)
245 {
246 ARGV_t argv = *argvp;
247 int argc = argvCount(argv);
248 int ac = argvCount(av);
249 int i;
250
251 argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
252 for (i = 0; i < ac; i++)
253 argv[argc + i] = rpmExpand(av[i], NULL);
254 argv[argc + ac] = NULL;
255 *argvp = argv;
256 return 0;
257 }
258
rpmdsSingleNS(rpmstrPool pool,rpmTagVal tagN,const char * namespace,const char * N,const char * EVR,rpmsenseFlags Flags)259 static rpmds rpmdsSingleNS(rpmstrPool pool,
260 rpmTagVal tagN, const char *namespace,
261 const char * N, const char * EVR, rpmsenseFlags Flags)
262 {
263 rpmds ds = NULL;
264 if (namespace) {
265 char *NSN = rpmExpand(namespace, "(", N, ")", NULL);
266 ds = rpmdsSinglePool(pool, tagN, NSN, EVR, Flags);
267 free(NSN);
268 } else {
269 ds = rpmdsSinglePool(pool, tagN, N, EVR, Flags);
270 }
271 return ds;
272 }
273
274 #define max(x,y) ((x) > (y) ? (x) : (y))
275
getOutputFrom(ARGV_t argv,const char * writePtr,size_t writeBytesLeft,StringBuf sb_stdout,int failNonZero,const char * buildRoot)276 static int getOutputFrom(ARGV_t argv,
277 const char * writePtr, size_t writeBytesLeft,
278 StringBuf sb_stdout,
279 int failNonZero, const char *buildRoot)
280 {
281 pid_t child, reaped;
282 int toProg[2] = { -1, -1 };
283 int fromProg[2] = { -1, -1 };
284 int status;
285 int myerrno = 0;
286 int ret = 1; /* assume failure */
287 int doio = (writePtr || sb_stdout);
288
289 if (doio && (pipe(toProg) < 0 || pipe(fromProg) < 0)) {
290 rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
291 return -1;
292 }
293
294 child = fork();
295 if (child < 0) {
296 rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
297 argv[0], strerror(errno));
298 if (doio) {
299 close(toProg[1]);
300 close(toProg[0]);
301 close(fromProg[0]);
302 close(fromProg[1]);
303 }
304 return -1;
305 }
306 if (child == 0) {
307 close(toProg[1]);
308 close(fromProg[0]);
309
310 dup2(toProg[0], STDIN_FILENO); /* Make stdin the in pipe */
311 close(toProg[0]);
312
313 dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
314 close(fromProg[1]);
315
316 rpmlog(RPMLOG_DEBUG, "\texecv(%s) pid %d\n",
317 argv[0], (unsigned)getpid());
318
319 if (buildRoot)
320 setenv("RPM_BUILD_ROOT", buildRoot, 1);
321
322 unsetenv("MALLOC_CHECK_");
323 execvp(argv[0], (char *const *)argv);
324 rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
325 argv[0], strerror(errno));
326 _exit(EXIT_FAILURE);
327 }
328
329 if (!doio)
330 goto reap;
331
332 close(toProg[0]);
333 close(fromProg[1]);
334
335 while (1) {
336 fd_set ibits, obits;
337 int nfd = 0;
338 ssize_t iorc;
339 char buf[BUFSIZ+1];
340
341 FD_ZERO(&ibits);
342 FD_ZERO(&obits);
343
344 FD_SET(fromProg[0], &ibits);
345 nfd = max(nfd, fromProg[0]);
346
347 if (writeBytesLeft > 0) {
348 FD_SET(toProg[1], &obits);
349 nfd = max(nfd, toProg[1]);
350 } else if (toProg[1] >= 0) {
351 /* Close write-side pipe to notify child on EOF */
352 close(toProg[1]);
353 toProg[1] = -1;
354 }
355
356 do {
357 iorc = select(nfd + 1, &ibits, &obits, NULL, NULL);
358 } while (iorc == -1 && errno == EINTR);
359
360 if (iorc < 0) {
361 myerrno = errno;
362 break;
363 }
364
365 /* Write data to child */
366 if (writeBytesLeft > 0 && FD_ISSET(toProg[1], &obits)) {
367 size_t nb = (1024 < writeBytesLeft) ? 1024 : writeBytesLeft;
368 do {
369 iorc = write(toProg[1], writePtr, nb);
370 } while (iorc == -1 && errno == EINTR);
371
372 if (iorc < 0) {
373 myerrno = errno;
374 break;
375 }
376 writeBytesLeft -= iorc;
377 writePtr += iorc;
378 }
379
380 /* Read when we get data back from the child */
381 if (FD_ISSET(fromProg[0], &ibits)) {
382 do {
383 iorc = read(fromProg[0], buf, sizeof(buf)-1);
384 } while (iorc == -1 && errno == EINTR);
385
386 if (iorc == 0) break; /* EOF, we're done */
387 if (iorc < 0) {
388 myerrno = errno;
389 break;
390 }
391 buf[iorc] = '\0';
392 if (sb_stdout)
393 appendStringBuf(sb_stdout, buf);
394 }
395 }
396
397 /* Clean up */
398 if (toProg[1] >= 0)
399 close(toProg[1]);
400 if (fromProg[0] >= 0)
401 close(fromProg[0]);
402
403 reap:
404 /* Collect status from prog */
405 reaped = waitpid(child, &status, 0);
406 rpmlog(RPMLOG_DEBUG, "\twaitpid(%d) rc %d status %x\n",
407 (unsigned)child, (unsigned)reaped, status);
408
409 if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
410 rpmlog(RPMLOG_DEBUG, _("%s failed: %x\n"), argv[0], status);
411 goto exit;
412 }
413 if (writeBytesLeft || myerrno) {
414 rpmlog(RPMLOG_ERR, _("failed to write all data to %s: %s\n"),
415 argv[0], strerror(myerrno));
416 goto exit;
417 }
418 ret = 0;
419
420 exit:
421 return ret;
422 }
423
rpmfcExec(ARGV_const_t av,StringBuf sb_stdin,StringBuf * sb_stdoutp,int failnonzero,const char * buildRoot)424 int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp,
425 int failnonzero, const char *buildRoot)
426 {
427 char * s = NULL;
428 ARGV_t xav = NULL;
429 ARGV_t pav = NULL;
430 int pac = 0;
431 int ec = -1;
432 StringBuf sb = NULL;
433 const char * buf_stdin = NULL;
434 size_t buf_stdin_len = 0;
435
436 if (sb_stdoutp)
437 *sb_stdoutp = NULL;
438 if (!(av && *av))
439 goto exit;
440
441 /* Find path to executable with (possible) args. */
442 s = rpmExpand(av[0], NULL);
443 if (!(s && *s))
444 goto exit;
445
446 /* Parse args buried within expanded executable. */
447 if (!(poptParseArgvString(s, &pac, (const char ***)&pav) == 0 && pac > 0 && pav != NULL))
448 goto exit;
449
450 /* Build argv, appending args to the executable args. */
451 argvAppend(&xav, pav);
452 if (av[1])
453 rpmfcExpandAppend(&xav, av + 1);
454
455 if (sb_stdin != NULL) {
456 buf_stdin = getStringBuf(sb_stdin);
457 buf_stdin_len = strlen(buf_stdin);
458 }
459
460 if (_rpmfc_debug) {
461 char *cmd = argvJoin(xav, " ");
462 rpmlog(RPMLOG_DEBUG, "Executing %s on %s\n", cmd, buf_stdin);
463 free(cmd);
464 }
465
466 /* Read output from exec'd helper. */
467 if (sb_stdoutp != NULL) {
468 sb = newStringBuf();
469 }
470 ec = getOutputFrom(xav, buf_stdin, buf_stdin_len, sb,
471 failnonzero, buildRoot);
472 if (ec) {
473 sb = freeStringBuf(sb);
474 goto exit;
475 }
476
477 if (sb_stdoutp != NULL) {
478 *sb_stdoutp = sb;
479 sb = NULL; /* XXX don't free */
480 }
481
482 exit:
483 freeStringBuf(sb);
484 argvFree(xav);
485 free(pav); /* XXX popt mallocs in single blob. */
486 free(s);
487 return ec;
488 }
489
argvAddUniq(ARGV_t * argvp,const char * key)490 static void argvAddUniq(ARGV_t * argvp, const char * key)
491 {
492 if (argvSearch(*argvp, key, NULL) == NULL) {
493 argvAdd(argvp, key);
494 argvSort(*argvp, NULL);
495 }
496 }
497
498 #define hasAttr(_a, _n) (argvSearch((_a), (_n), NULL) != NULL)
499
rpmfcAddFileDep(rpmfcFileDeps * fileDeps,rpmds ds,int ix)500 static void rpmfcAddFileDep(rpmfcFileDeps *fileDeps, rpmds ds, int ix)
501 {
502 if (fileDeps->size == fileDeps->alloced) {
503 fileDeps->alloced <<= 2;
504 fileDeps->data = xrealloc(fileDeps->data,
505 fileDeps->alloced * sizeof(fileDeps->data[0]));
506 }
507 fileDeps->data[fileDeps->size].fileIx = ix;
508 fileDeps->data[fileDeps->size++].dep = ds;
509 }
510
runCmd(const char * cmd,const char * buildRoot,const char * fn)511 static ARGV_t runCmd(const char *cmd,
512 const char *buildRoot, const char *fn)
513 {
514 ARGV_t output = NULL;
515 ARGV_t av = NULL;
516 StringBuf sb_stdout = NULL;
517 StringBuf sb_stdin = newStringBuf();
518 argvAdd(&av, cmd);
519
520 appendLineStringBuf(sb_stdin, fn);
521 if (rpmfcExec(av, sb_stdin, &sb_stdout, 0, buildRoot) == 0) {
522 argvSplit(&output, getStringBuf(sb_stdout), "\n\r");
523 }
524
525 argvFree(av);
526 freeStringBuf(sb_stdin);
527 freeStringBuf(sb_stdout);
528
529 return output;
530 }
531
runCall(const char * cmd,const char * buildRoot,const char * fn)532 static ARGV_t runCall(const char *cmd,
533 const char *buildRoot, const char *fn)
534 {
535 ARGV_t output = NULL;
536
537 if (_rpmfc_debug)
538 rpmlog(RPMLOG_DEBUG, "Calling %s() on %s\n", cmd, fn);
539
540 /* Hack to pass in the path as what looks like a macro argument */
541 rpmPushMacroFlags(NULL, "1", NULL, fn, 1, RPMMACRO_LITERAL);
542 char *exp = rpmExpand(cmd, NULL);
543 rpmPopMacro(NULL, "1");
544 if (*exp)
545 argvSplit(&output, exp, "\n\r");
546 free(exp);
547
548 return output;
549 }
550
551 struct addReqProvDataFc {
552 rpmfc fc;
553 const char *namespace;
554 regex_t *exclude;
555 };
556
addReqProvFc(void * cbdata,rpmTagVal tagN,const char * N,const char * EVR,rpmsenseFlags Flags,int index)557 static rpmRC addReqProvFc(void *cbdata, rpmTagVal tagN,
558 const char * N, const char * EVR, rpmsenseFlags Flags,
559 int index)
560 {
561 struct addReqProvDataFc *data = cbdata;
562 rpmfc fc = data->fc;
563 const char *namespace = data->namespace;
564 regex_t *exclude = data->exclude;
565
566 rpmds ds = rpmdsSingleNS(fc->pool, tagN, namespace, N, EVR, Flags);
567 /* Add to package and file dependencies unless filtered */
568 if (regMatch(exclude, rpmdsDNEVR(ds)+2) == 0)
569 rpmfcAddFileDep(&fc->fileDeps, ds, index);
570
571 return RPMRC_OK;
572 }
573
574 struct exclreg_s {
575 regex_t *exclude;
576 regex_t *exclude_from;
577 regex_t *global_exclude_from;
578 };
579
exclInit(const char * depname,struct exclreg_s * excl)580 static void exclInit(const char *depname, struct exclreg_s *excl)
581 {
582 excl->exclude = rpmfcAttrReg(depname, "exclude", NULL);
583 excl->exclude_from = rpmfcAttrReg(depname, "exclude", "from", NULL);
584 excl->global_exclude_from = rpmfcAttrReg("global", depname, "exclude", "from", NULL);
585 }
586
exclFini(struct exclreg_s * excl)587 static void exclFini(struct exclreg_s *excl)
588 {
589 regFree(excl->exclude);
590 regFree(excl->exclude_from);
591 regFree(excl->global_exclude_from);
592 memset(excl, 0, sizeof(*excl));
593 }
594
rpmfcHelper(rpmfc fc,int ix,const struct exclreg_s * excl,rpmsenseFlags dsContext,rpmTagVal tagN,const char * namespace,const char * cmd,int callable)595 static int rpmfcHelper(rpmfc fc, int ix, const struct exclreg_s *excl,
596 rpmsenseFlags dsContext, rpmTagVal tagN,
597 const char *namespace, const char *cmd, int callable)
598 {
599 ARGV_t pav = NULL;
600 const char * fn = fc->fn[ix];
601 int pac;
602 int rc = 0;
603
604 /* If the entire path is filtered out, there's nothing more to do */
605 if (regMatch(excl->exclude_from, fn+fc->brlen))
606 goto exit;
607
608 if (regMatch(excl->global_exclude_from, fn+fc->brlen))
609 goto exit;
610
611 if (callable) {
612 pav = runCall(cmd, fc->buildRoot, fn);
613 } else {
614 pav = runCmd(cmd, fc->buildRoot, fn);
615 }
616
617 if (pav == NULL)
618 goto exit;
619
620 pac = argvCount(pav);
621
622 struct addReqProvDataFc data;
623 data.fc = fc;
624 data.namespace = namespace;
625 data.exclude = excl->exclude;
626
627 for (int i = 0; i < pac; i++) {
628 if (parseRCPOT(NULL, fc->pkg, pav[i], tagN, ix, dsContext, addReqProvFc, &data))
629 rc++;
630 }
631
632 argvFree(pav);
633
634 exit:
635 return rc;
636 }
637
638 /* Only used for controlling RPMTAG_FILECLASS inclusion now */
639 static const struct rpmfcTokens_s rpmfcTokens[] = {
640 { "directory", RPMFC_INCLUDE },
641
642 { "ELF 32-bit", RPMFC_ELF32|RPMFC_INCLUDE },
643 { "ELF 64-bit", RPMFC_ELF64|RPMFC_INCLUDE },
644
645 { "troff or preprocessor input", RPMFC_INCLUDE },
646 { "GNU Info", RPMFC_INCLUDE },
647
648 { "perl ", RPMFC_INCLUDE },
649 { "Perl5 module source text", RPMFC_INCLUDE },
650 { "python ", RPMFC_INCLUDE },
651
652 { "libtool library ", RPMFC_INCLUDE },
653 { "pkgconfig ", RPMFC_INCLUDE },
654
655 { "Objective caml ", RPMFC_INCLUDE },
656 { "Mono/.Net assembly", RPMFC_INCLUDE },
657
658 { "current ar archive", RPMFC_INCLUDE },
659 { "Zip archive data", RPMFC_INCLUDE },
660 { "tar archive", RPMFC_INCLUDE },
661 { "cpio archive", RPMFC_INCLUDE },
662 { "RPM v3", RPMFC_INCLUDE },
663 { "RPM v4", RPMFC_INCLUDE },
664
665 { " image", RPMFC_INCLUDE },
666 { " font", RPMFC_INCLUDE },
667 { " Font", RPMFC_INCLUDE },
668
669 { " commands", RPMFC_INCLUDE },
670 { " script", RPMFC_INCLUDE },
671
672 { "empty", RPMFC_INCLUDE },
673
674 { "HTML", RPMFC_INCLUDE },
675 { "SGML", RPMFC_INCLUDE },
676 { "XML", RPMFC_INCLUDE },
677
678 { " source", RPMFC_INCLUDE },
679 { "GLS_BINARY_LSB_FIRST", RPMFC_INCLUDE },
680 { " DB ", RPMFC_INCLUDE },
681
682 { " text", RPMFC_INCLUDE },
683
684 { NULL, RPMFC_BLACK }
685 };
686
argvAddTokens(ARGV_t * argv,const char * tnames)687 static void argvAddTokens(ARGV_t *argv, const char *tnames)
688 {
689 if (tnames) {
690 ARGV_t tokens = NULL;
691 argvSplit(&tokens, tnames, ",");
692 for (ARGV_t token = tokens; token && *token; token++)
693 argvAddUniq(argv, *token);
694 argvFree(tokens);
695 }
696 }
697
matches(const struct matchRule * rule,const char * ftype,const char * fmime,const char * path,int executable)698 static int matches(const struct matchRule *rule,
699 const char *ftype, const char *fmime,
700 const char *path, int executable)
701 {
702 const char *mtype = rule->mime ? fmime : ftype;
703 regex_t *mreg = rule->mime ? rule->mime : rule->magic;
704
705 if (!executable && hasAttr(rule->flags, "exeonly"))
706 return 0;
707 if (mreg && rule->path && hasAttr(rule->flags, "magic_and_path")) {
708 return (regMatch(mreg, mtype) && regMatch(rule->path, path));
709 } else {
710 return (regMatch(mreg, mtype) || regMatch(rule->path, path));
711 }
712 }
713
rpmfcAttributes(rpmfc fc,int ix,const char * ftype,const char * fmime,const char * fullpath)714 static void rpmfcAttributes(rpmfc fc, int ix, const char *ftype, const char *fmime, const char *fullpath)
715 {
716 const char *path = fullpath + fc->brlen;
717 int is_executable = 0;
718 struct stat st;
719 if (stat(fullpath, &st) == 0) {
720 is_executable = (S_ISREG(st.st_mode)) &&
721 (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
722 }
723
724 for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++) {
725 /* Filter out excludes */
726 if (matches(&(*attr)->excl, ftype, fmime, path, is_executable))
727 continue;
728
729 /* Add attributes on libmagic type & path pattern matches */
730 if (matches(&(*attr)->incl, ftype, fmime, path, is_executable)) {
731 argvAddTokens(&fc->fattrs[ix], (*attr)->name);
732 #pragma omp critical(fahash)
733 fattrHashAddEntry(fc->fahash, attr-fc->atypes, ix);
734 }
735 }
736 }
737
738 /* Return color for a given libmagic classification string */
rpmfcColor(const char * fmstr)739 static rpm_color_t rpmfcColor(const char * fmstr)
740 {
741 rpmfcToken fct;
742 rpm_color_t fcolor = RPMFC_BLACK;
743
744 for (fct = rpmfcTokens; fct->token != NULL; fct++) {
745 if (strstr(fmstr, fct->token) == NULL)
746 continue;
747
748 fcolor |= fct->colors;
749 if (fcolor & RPMFC_INCLUDE)
750 break;
751 }
752
753 return fcolor;
754 }
755
rpmfcPrint(const char * msg,rpmfc fc,FILE * fp)756 void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
757 {
758 int ndx;
759 int dx;
760 int fx;
761
762 if (fp == NULL) fp = stderr;
763
764 if (msg)
765 fprintf(fp, "===================================== %s\n", msg);
766
767 if (fc)
768 for (fx = 0; fx < fc->nfiles; fx++) {
769 fprintf(fp, "%3d %s", fx, fc->fn[fx]);
770 if (_rpmfc_debug) {
771 rpmsid cx = fc->fcdictx[fx] + 1; /* id's are one off */
772 rpm_color_t fcolor = fc->fcolor[fx];
773 ARGV_t fattrs = fc->fattrs[fx];
774
775 if (fcolor != RPMFC_BLACK)
776 fprintf(fp, "\t0x%x", fc->fcolor[fx]);
777 else
778 fprintf(fp, "\t%s", rpmstrPoolStr(fc->cdict, cx));
779 if (fattrs) {
780 char *attrs = argvJoin(fattrs, ",");
781 fprintf(fp, " [%s]", attrs);
782 free(attrs);
783 } else {
784 fprintf(fp, " [none]");
785 }
786 }
787 fprintf(fp, "\n");
788
789 if (fc->fddictx == NULL || fc->fddictn == NULL)
790 continue;
791
792 assert(fx < fc->fddictx->nvals);
793 dx = fc->fddictx->vals[fx];
794 assert(fx < fc->fddictn->nvals);
795 ndx = fc->fddictn->vals[fx];
796
797 while (ndx-- > 0) {
798 const char * depval;
799 unsigned char deptype;
800 unsigned ix;
801 rpmds ds;
802
803 ix = fc->ddictx->vals[dx++];
804 deptype = ((ix >> 24) & 0xff);
805 ix &= 0x00ffffff;
806 depval = NULL;
807 ds = rpmfcDependencies(fc, rpmdsDToTagN(deptype));
808 (void) rpmdsSetIx(ds, ix-1);
809 if (rpmdsNext(ds) >= 0)
810 depval = rpmdsDNEVR(ds);
811 if (depval)
812 fprintf(fp, "\t%s\n", depval);
813 }
814 }
815 }
816
rpmfcFree(rpmfc fc)817 rpmfc rpmfcFree(rpmfc fc)
818 {
819 if (fc) {
820 for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++)
821 rpmfcAttrFree(*attr);
822 free(fc->atypes);
823 free(fc->buildRoot);
824 for (int i = 0; i < fc->nfiles; i++) {
825 free(fc->fn[i]);
826 free(fc->ftype[i]);
827 argvFree(fc->fattrs[i]);
828 }
829 free(fc->fn);
830 free(fc->ftype);
831 free(fc->fattrs);
832 free(fc->fcolor);
833 free(fc->fcdictx);
834 freePackage(fc->pkg);
835 argiFree(fc->fddictx);
836 argiFree(fc->fddictn);
837 argiFree(fc->ddictx);
838
839 for (int i = 0; i < fc->fileDeps.size; i++) {
840 rpmdsFree(fc->fileDeps.data[i].dep);
841 }
842 free(fc->fileDeps.data);
843
844 fattrHashFree(fc->fahash);
845 rpmstrPoolFree(fc->cdict);
846
847 rpmstrPoolFree(fc->pool);
848 memset(fc, 0, sizeof(*fc)); /* trash and burn */
849 free(fc);
850 }
851 return NULL;
852 }
853
rpmfcCreate(const char * buildRoot,rpmFlags flags)854 rpmfc rpmfcCreate(const char *buildRoot, rpmFlags flags)
855 {
856 rpmfc fc = xcalloc(1, sizeof(*fc));
857 if (buildRoot) {
858 fc->buildRoot = xstrdup(buildRoot);
859 fc->brlen = strlen(buildRoot);
860 }
861 fc->pool = rpmstrPoolCreate();
862 fc->pkg = xcalloc(1, sizeof(*fc->pkg));
863 fc->fileDeps.alloced = 10;
864 fc->fileDeps.data = xmalloc(fc->fileDeps.alloced *
865 sizeof(fc->fileDeps.data[0]));
866 return fc;
867 }
868
rpmfcNew(void)869 rpmfc rpmfcNew(void)
870 {
871 return rpmfcCreate(NULL, 0);
872 }
873
rpmfcDependencies(rpmfc fc,rpmTagVal tag)874 rpmds rpmfcDependencies(rpmfc fc, rpmTagVal tag)
875 {
876 if (fc) {
877 return *packageDependencies(fc->pkg, tag);
878 }
879 return NULL;
880 }
881
rpmfcProvides(rpmfc fc)882 rpmds rpmfcProvides(rpmfc fc)
883 {
884 return rpmfcDependencies(fc, RPMTAG_PROVIDENAME);
885 }
886
rpmfcRequires(rpmfc fc)887 rpmds rpmfcRequires(rpmfc fc)
888 {
889 return rpmfcDependencies(fc, RPMTAG_REQUIRENAME);
890 }
891
rpmfcRecommends(rpmfc fc)892 rpmds rpmfcRecommends(rpmfc fc)
893 {
894 return rpmfcDependencies(fc, RPMTAG_RECOMMENDNAME);
895 }
896
rpmfcSuggests(rpmfc fc)897 rpmds rpmfcSuggests(rpmfc fc)
898 {
899 return rpmfcDependencies(fc, RPMTAG_SUGGESTNAME);
900 }
901
rpmfcSupplements(rpmfc fc)902 rpmds rpmfcSupplements(rpmfc fc)
903 {
904 return rpmfcDependencies(fc, RPMTAG_SUPPLEMENTNAME);
905 }
906
rpmfcEnhances(rpmfc fc)907 rpmds rpmfcEnhances(rpmfc fc)
908 {
909 return rpmfcDependencies(fc, RPMTAG_ENHANCENAME);
910 }
911
rpmfcConflicts(rpmfc fc)912 rpmds rpmfcConflicts(rpmfc fc)
913 {
914 return rpmfcDependencies(fc, RPMTAG_CONFLICTNAME);
915 }
916
rpmfcObsoletes(rpmfc fc)917 rpmds rpmfcObsoletes(rpmfc fc)
918 {
919 return rpmfcDependencies(fc, RPMTAG_OBSOLETENAME);
920 }
921
922
923 /* Versioned deps are less than unversioned deps */
cmpVerDeps(const void * a,const void * b)924 static int cmpVerDeps(const void *a, const void *b)
925 {
926 rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
927 rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
928
929 int aIsVersioned = rpmdsFlags(fDepA->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
930 int bIsVersioned = rpmdsFlags(fDepB->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
931
932 return bIsVersioned - aIsVersioned;
933 }
934
935 /* Sort by index */
cmpIndexDeps(const void * a,const void * b)936 static int cmpIndexDeps(const void *a, const void *b)
937 {
938 rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
939 rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
940
941 return fDepA->fileIx - fDepB->fileIx;
942 }
943
944 /*
945 * Remove unversioned deps if corresponding versioned deps exist but only
946 * if the versioned dependency has the same type and the same color as the versioned.
947 */
rpmfcNormalizeFDeps(rpmfc fc)948 static void rpmfcNormalizeFDeps(rpmfc fc)
949 {
950 rpmstrPool versionedDeps = rpmstrPoolCreate();
951 rpmfcFileDep *normalizedFDeps = xmalloc(fc->fileDeps.size *
952 sizeof(normalizedFDeps[0]));
953 int ix = 0;
954 char *depStr;
955
956 /* Sort. Versioned dependencies first */
957 qsort(fc->fileDeps.data, fc->fileDeps.size, sizeof(fc->fileDeps.data[0]),
958 cmpVerDeps);
959
960 for (int i = 0; i < fc->fileDeps.size; i++) {
961 switch (rpmdsTagN(fc->fileDeps.data[i].dep)) {
962 case RPMTAG_REQUIRENAME:
963 case RPMTAG_RECOMMENDNAME:
964 case RPMTAG_SUGGESTNAME:
965 rasprintf(&depStr, "%08x_%c_%s",
966 fc->fcolor[fc->fileDeps.data[i].fileIx],
967 rpmdsD(fc->fileDeps.data[i].dep),
968 rpmdsN(fc->fileDeps.data[i].dep));
969
970 if (rpmdsFlags(fc->fileDeps.data[i].dep) & RPMSENSE_SENSEMASK) {
971 /* preserve versioned require dependency */
972 normalizedFDeps[ix++] = fc->fileDeps.data[i];
973 rpmstrPoolId(versionedDeps, depStr, 1);
974 } else if (!rpmstrPoolId(versionedDeps, depStr, 0)) {
975 /* preserve unversioned require dep only if versioned dep doesn't exist */
976 normalizedFDeps[ix++] =fc-> fileDeps.data[i];
977 } else {
978 rpmdsFree(fc->fileDeps.data[i].dep);
979 }
980 free(depStr);
981 break;
982 default:
983 /* Preserve all non-require dependencies */
984 normalizedFDeps[ix++] = fc->fileDeps.data[i];
985 break;
986 }
987 }
988 rpmstrPoolFree(versionedDeps);
989
990 free(fc->fileDeps.data);
991 fc->fileDeps.data = normalizedFDeps;
992 fc->fileDeps.size = ix;
993 }
994
995 struct applyDep_s {
996 rpmTagVal tag;
997 int type;
998 const char *name;
999 };
1000
1001 static const struct applyDep_s applyDepTable[] = {
1002 { RPMTAG_PROVIDENAME, RPMSENSE_FIND_PROVIDES, "provides" },
1003 { RPMTAG_REQUIRENAME, RPMSENSE_FIND_REQUIRES, "requires" },
1004 { RPMTAG_RECOMMENDNAME, RPMSENSE_FIND_REQUIRES, "recommends" },
1005 { RPMTAG_SUGGESTNAME, RPMSENSE_FIND_REQUIRES, "suggests" },
1006 { RPMTAG_SUPPLEMENTNAME, RPMSENSE_FIND_REQUIRES, "supplements" },
1007 { RPMTAG_ENHANCENAME, RPMSENSE_FIND_REQUIRES, "enhances" },
1008 { RPMTAG_CONFLICTNAME, RPMSENSE_FIND_REQUIRES, "conflicts" },
1009 { RPMTAG_OBSOLETENAME, RPMSENSE_FIND_REQUIRES, "obsoletes" },
1010 { 0, 0, NULL },
1011 };
1012
applyAttr(rpmfc fc,int aix,const char * aname,const struct exclreg_s * excl,const struct applyDep_s * dep)1013 static int applyAttr(rpmfc fc, int aix, const char *aname,
1014 const struct exclreg_s *excl,
1015 const struct applyDep_s *dep)
1016 {
1017 int rc = 0;
1018 int n, *ixs;
1019
1020 if (fattrHashGetEntry(fc->fahash, aix, &ixs, &n, NULL)) {
1021 char *mname = rstrscat(NULL, "__", aname, "_", dep->name, NULL);
1022 char *cmd;
1023 int callable = rpmMacroIsParametric(NULL, mname);
1024
1025 if (callable) {
1026 cmd = rstrscat(NULL, "%{", mname,
1027 " %{?", mname, "_opts}", "}", NULL);
1028 } else {
1029 cmd = rpmExpand("%{?", mname, ":%{", mname, "} %{?",
1030 mname, "_opts}}", NULL);
1031 }
1032
1033 if (!rstreq(cmd, "")) {
1034 char *ns = rpmfcAttrMacro(aname, "namespace", NULL);
1035 for (int i = 0; i < n; i++) {
1036 if (rpmfcHelper(fc, ixs[i], excl, dep->type, dep->tag,
1037 ns, cmd, callable))
1038 rc = 1;
1039 }
1040 free(ns);
1041 }
1042 free(cmd);
1043 free(mname);
1044 }
1045 return rc;
1046 }
1047
rpmfcApplyInternal(rpmfc fc)1048 static rpmRC rpmfcApplyInternal(rpmfc fc)
1049 {
1050 rpmRC rc = RPMRC_OK;
1051 rpmds ds, * dsp;
1052 int previx;
1053 unsigned int val;
1054 int dix;
1055 int ix;
1056 const struct applyDep_s *dep;
1057 int skip = 0;
1058 struct exclreg_s excl;
1059
1060 if (fc->skipProv)
1061 skip |= RPMSENSE_FIND_PROVIDES;
1062 if (fc->skipReq)
1063 skip |= RPMSENSE_FIND_REQUIRES;
1064
1065 /* Generate package and per-file dependencies. */
1066 for (dep = applyDepTable; dep->tag; dep++) {
1067 int aix = 0;
1068 if (skip & dep->type)
1069 continue;
1070 exclInit(dep->name, &excl);
1071 for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++, aix++) {
1072 if (applyAttr(fc, aix, (*attr)->name, &excl, dep))
1073 rc = RPMRC_FAIL;
1074 }
1075 exclFini(&excl);
1076 }
1077 /* No more additions after this, freeze pool to minimize memory use */
1078
1079 rpmfcNormalizeFDeps(fc);
1080 for (int i = 0; i < fc->fileDeps.size; i++) {
1081 ds = fc->fileDeps.data[i].dep;
1082 rpmdsMerge(packageDependencies(fc->pkg, rpmdsTagN(ds)), ds);
1083 }
1084
1085 /* Sort by index */
1086 qsort(fc->fileDeps.data, fc->fileDeps.size,
1087 sizeof(fc->fileDeps.data[0]), cmpIndexDeps);
1088
1089 /* Generate per-file indices into package dependencies. */
1090 previx = -1;
1091 for (int i = 0; i < fc->fileDeps.size; i++) {
1092 ds = fc->fileDeps.data[i].dep;
1093 ix = fc->fileDeps.data[i].fileIx;
1094 dsp = packageDependencies(fc->pkg, rpmdsTagN(ds));
1095 dix = rpmdsFind(*dsp, ds);
1096 if (dix < 0)
1097 continue;
1098
1099 val = (rpmdsD(ds) << 24) | (dix & 0x00ffffff);
1100 argiAdd(&fc->ddictx, -1, val);
1101
1102 if (previx != ix) {
1103 previx = ix;
1104 argiAdd(&fc->fddictx, ix, argiCount(fc->ddictx)-1);
1105 }
1106 if (fc->fddictn && fc->fddictn->vals)
1107 fc->fddictn->vals[ix]++;
1108
1109 }
1110 return rc;
1111 }
1112
initAttrs(rpmfc fc)1113 static int initAttrs(rpmfc fc)
1114 {
1115 ARGV_t files = NULL;
1116 char * attrPath = rpmExpand("%{_fileattrsdir}/*.attr", NULL);
1117 int nattrs = 0;
1118
1119 /* Discover known attributes from pathnames + initialize them */
1120 if (rpmGlob(attrPath, NULL, &files) == 0) {
1121 nattrs = argvCount(files);
1122 fc->atypes = xcalloc(nattrs + 1, sizeof(*fc->atypes));
1123 for (int i = 0; i < nattrs; i++) {
1124 char *bn = basename(files[i]);
1125 bn[strlen(bn)-strlen(".attr")] = '\0';
1126 fc->atypes[i] = rpmfcAttrNew(bn);
1127 }
1128 fc->atypes[nattrs] = NULL;
1129 argvFree(files);
1130 }
1131 free(attrPath);
1132 return nattrs;
1133 }
1134
getElfColor(const char * fn)1135 static uint32_t getElfColor(const char *fn)
1136 {
1137 uint32_t color = 0;
1138 #ifdef HAVE_LIBELF
1139 int fd = open(fn, O_RDONLY);
1140 if (fd >= 0) {
1141 Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
1142 GElf_Ehdr ehdr;
1143 if (elf && gelf_getehdr(elf, &ehdr)) {
1144 switch (ehdr.e_ident[EI_CLASS]) {
1145 case ELFCLASS64:
1146 color = RPMFC_ELF64;
1147 break;
1148 case ELFCLASS32:
1149 color = RPMFC_ELF32;
1150 break;
1151 }
1152 elf_end(elf);
1153 }
1154 close(fd);
1155 }
1156 #endif
1157 return color;
1158 }
1159
rpmfcClassify(rpmfc fc,ARGV_t argv,rpm_mode_t * fmode)1160 rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
1161 {
1162 int msflags = MAGIC_CHECK | MAGIC_COMPRESS | MAGIC_NO_CHECK_TOKENS | MAGIC_ERROR;
1163 int mimeflags = msflags | MAGIC_MIME_TYPE;
1164 int nerrors = 0;
1165 rpmRC rc = RPMRC_FAIL;
1166
1167 if (fc == NULL) {
1168 rpmlog(RPMLOG_ERR, _("Empty file classifier\n"));
1169 goto exit;
1170 }
1171
1172 /* It is OK when we have no files to classify. */
1173 if (argv == NULL)
1174 return RPMRC_OK;
1175
1176 if (initAttrs(fc) < 1) {
1177 rpmlog(RPMLOG_ERR, _("No file attributes configured\n"));
1178 goto exit;
1179 }
1180
1181 fc->nfiles = argvCount(argv);
1182 fc->fn = xcalloc(fc->nfiles, sizeof(*fc->fn));
1183 fc->ftype = xcalloc(fc->nfiles, sizeof(*fc->ftype));
1184 fc->fattrs = xcalloc(fc->nfiles, sizeof(*fc->fattrs));
1185 fc->fcolor = xcalloc(fc->nfiles, sizeof(*fc->fcolor));
1186 fc->fcdictx = xcalloc(fc->nfiles, sizeof(*fc->fcdictx));
1187 fc->fahash = fattrHashCreate(fc->nfiles / 3, intId, intCmp, NULL, NULL);
1188
1189 /* Initialize the per-file dictionary indices. */
1190 argiAdd(&fc->fddictx, fc->nfiles-1, 0);
1191 argiAdd(&fc->fddictn, fc->nfiles-1, 0);
1192
1193 /* Build (sorted) file class dictionary. */
1194 fc->cdict = rpmstrPoolCreate();
1195
1196 #pragma omp parallel
1197 {
1198 /* libmagic is not thread-safe, each thread needs to a private handle */
1199 magic_t ms = magic_open(msflags);
1200 magic_t mime = magic_open(mimeflags);
1201
1202 if (ms == NULL || mime == NULL) {
1203 rpmlog(RPMLOG_ERR, _("magic_open(0x%x) failed: %s\n"),
1204 msflags, strerror(errno));
1205 #pragma omp cancel parallel
1206 }
1207
1208 if (magic_load(ms, NULL) == -1) {
1209 rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(ms));
1210 #pragma omp cancel parallel
1211 }
1212 if (magic_load(mime, NULL) == -1) {
1213 rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(mime));
1214 #pragma omp cancel parallel
1215 }
1216
1217 #pragma omp for reduction(+:nerrors)
1218 for (int ix = 0; ix < fc->nfiles; ix++) {
1219 const char * fmime;
1220 const char * ftype;
1221 const char * s = argv[ix];
1222 size_t slen = strlen(s);
1223 int fcolor = RPMFC_BLACK;
1224 rpm_mode_t mode = (fmode ? fmode[ix] : 0);
1225 int is_executable = (mode & (S_IXUSR|S_IXGRP|S_IXOTH));
1226
1227 switch (mode & S_IFMT) {
1228 case S_IFCHR: ftype = "character special"; break;
1229 case S_IFBLK: ftype = "block special"; break;
1230 case S_IFIFO: ftype = "fifo (named pipe)"; break;
1231 case S_IFSOCK: ftype = "socket"; break;
1232 case S_IFDIR: ftype = "directory"; break;
1233 case S_IFLNK:
1234 case S_IFREG:
1235 default:
1236 /* XXX all files with extension ".pm" are perl modules for now. */
1237 if (rpmFileHasSuffix(s, ".pm"))
1238 ftype = "Perl5 module source text";
1239
1240 /* XXX all files with extension ".la" are libtool for now. */
1241 else if (rpmFileHasSuffix(s, ".la"))
1242 ftype = "libtool library file";
1243
1244 /* XXX all files with extension ".pc" are pkgconfig for now. */
1245 else if (rpmFileHasSuffix(s, ".pc"))
1246 ftype = "pkgconfig file";
1247
1248 /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */
1249 else if (slen >= fc->brlen+sizeof("/dev/") && rstreqn(s+fc->brlen, "/dev/", sizeof("/dev/")-1))
1250 ftype = "";
1251 else
1252 ftype = magic_file(ms, s);
1253
1254 /* Silence errors from immaterial %ghosts */
1255 if (ftype == NULL && errno == ENOENT)
1256 ftype = "";
1257
1258 if (ftype == NULL) {
1259 rpmlog(is_executable ? RPMLOG_ERR : RPMLOG_WARNING,
1260 _("Recognition of file \"%s\" failed: mode %06o %s\n"),
1261 s, mode, magic_error(ms));
1262 /* only executable files are critical to dep extraction */
1263 if (is_executable) {
1264 nerrors++;
1265 }
1266 /* unrecognized non-executables get treated as "data" */
1267 ftype = "data";
1268 }
1269 }
1270
1271 fmime = magic_file(mime, s);
1272
1273 /* Silence errors from immaterial %ghosts */
1274 if (fmime == NULL && errno == ENOENT)
1275 fmime = "";
1276
1277 if (fmime == NULL) {
1278 rpmlog(is_executable ? RPMLOG_ERR : RPMLOG_WARNING,
1279 _("Recognition of file \"%s\" failed: mode %06o %s\n"),
1280 s, mode, magic_error(ms));
1281 /* only executable files are critical to dep extraction */
1282 if (is_executable) {
1283 nerrors++;
1284 }
1285 fmime = "application/octet-stream";
1286 }
1287
1288 rpmlog(RPMLOG_DEBUG, "%s: %s (%s)\n", s, fmime, ftype);
1289
1290 /* Save the path. */
1291 fc->fn[ix] = xstrdup(s);
1292
1293 /* Add (filtered) file coloring */
1294 fcolor |= rpmfcColor(ftype);
1295
1296 /* Add attributes based on file type and/or path */
1297 rpmfcAttributes(fc, ix, ftype, fmime, s);
1298
1299 if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE))
1300 fc->ftype[ix] = xstrdup(ftype);
1301
1302 /* Add ELF colors */
1303 if (S_ISREG(mode) && is_executable)
1304 fc->fcolor[ix] = getElfColor(s);
1305 }
1306
1307 if (ms != NULL)
1308 magic_close(ms);
1309 if (mime != NULL)
1310 magic_close(mime);
1311
1312 } /* omp parallel */
1313
1314 /* Add to file class dictionary and index array */
1315 for (int ix = 0; ix < fc->nfiles; ix++) {
1316 const char *ftype = fc->ftype[ix] ? fc->ftype[ix] : "";
1317 /* Pool id's start from 1, for headers we want it from 0 */
1318 fc->fcdictx[ix] = rpmstrPoolId(fc->cdict, ftype, 1) - 1;
1319
1320 if (*ftype)
1321 fc->fknown++;
1322 else
1323 fc->fwhite++;
1324 }
1325
1326 if (nerrors == 0)
1327 rc = RPMRC_OK;
1328
1329 exit:
1330 /* No more additions after this, freeze pool to minimize memory use */
1331 rpmstrPoolFreeze(fc->cdict, 0);
1332
1333 return rc;
1334 }
1335
1336 typedef struct DepMsg_s * DepMsg_t;
1337
1338 struct DepMsg_s {
1339 const char * msg;
1340 char * const argv[4];
1341 rpmTagVal ntag;
1342 rpmTagVal vtag;
1343 rpmTagVal ftag;
1344 int mask;
1345 int xormask;
1346 };
1347
1348 static struct DepMsg_s depMsgs[] = {
1349 { "Provides", { "%{?__find_provides}", NULL, NULL, NULL },
1350 RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
1351 0, -1 },
1352 { "Requires(interp)", { NULL, "interp", NULL, NULL },
1353 RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
1354 RPMSENSE_INTERP, 0 },
1355 { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
1356 -1, -1, RPMTAG_REQUIREFLAGS,
1357 RPMSENSE_RPMLIB, 0 },
1358 { "Requires(verify)", { NULL, "verify", NULL, NULL },
1359 -1, -1, RPMTAG_REQUIREFLAGS,
1360 RPMSENSE_SCRIPT_VERIFY, 0 },
1361 { "Requires(pre)", { NULL, "pre", NULL, NULL },
1362 -1, -1, RPMTAG_REQUIREFLAGS,
1363 RPMSENSE_SCRIPT_PRE, 0 },
1364 { "Requires(post)", { NULL, "post", NULL, NULL },
1365 -1, -1, RPMTAG_REQUIREFLAGS,
1366 RPMSENSE_SCRIPT_POST, 0 },
1367 { "Requires(preun)", { NULL, "preun", NULL, NULL },
1368 -1, -1, RPMTAG_REQUIREFLAGS,
1369 RPMSENSE_SCRIPT_PREUN, 0 },
1370 { "Requires(postun)", { NULL, "postun", NULL, NULL },
1371 -1, -1, RPMTAG_REQUIREFLAGS,
1372 RPMSENSE_SCRIPT_POSTUN, 0 },
1373 { "Requires(pretrans)", { NULL, "pretrans", NULL, NULL },
1374 -1, -1, RPMTAG_REQUIREFLAGS,
1375 RPMSENSE_PRETRANS, 0 },
1376 { "Requires(posttrans)", { NULL, "posttrans", NULL, NULL },
1377 -1, -1, RPMTAG_REQUIREFLAGS,
1378 RPMSENSE_POSTTRANS, 0 },
1379 { "Requires", { "%{?__find_requires}", NULL, NULL, NULL },
1380 -1, -1, RPMTAG_REQUIREFLAGS, /* XXX inherit name/version arrays */
1381 RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 },
1382 { "Conflicts", { "%{?__find_conflicts}", NULL, NULL, NULL },
1383 RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS,
1384 0, -1 },
1385 { "Obsoletes", { "%{?__find_obsoletes}", NULL, NULL, NULL },
1386 RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
1387 0, -1 },
1388 { "Recommends", { "%{?__find_recommends}", NULL, NULL, NULL },
1389 RPMTAG_RECOMMENDNAME, RPMTAG_RECOMMENDVERSION, RPMTAG_RECOMMENDFLAGS,
1390 0, -1 },
1391 { "Suggests", { "%{?__find_suggests}", NULL, NULL, NULL },
1392 RPMTAG_SUGGESTNAME, RPMTAG_SUGGESTVERSION, RPMTAG_SUGGESTFLAGS,
1393 0, -1 },
1394 { "Supplements", { "%{?__find_supplements}", NULL, NULL, NULL },
1395 RPMTAG_SUPPLEMENTNAME, RPMTAG_SUPPLEMENTVERSION, RPMTAG_SUPPLEMENTFLAGS,
1396 0, -1 },
1397 { "Enhances", { "%{?__find_enhances}", NULL, NULL, NULL },
1398 RPMTAG_ENHANCENAME, RPMTAG_ENHANCEVERSION, RPMTAG_ENHANCEFLAGS,
1399 0, -1 },
1400 { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 }
1401 };
1402
1403 static DepMsg_t DepMsgs = depMsgs;
1404
printDeps(rpmfc fc)1405 static void printDeps(rpmfc fc)
1406 {
1407 DepMsg_t dm;
1408 rpmds ds = NULL;
1409 const char * DNEVR;
1410 rpmsenseFlags Flags;
1411 int bingo = 0;
1412
1413 for (dm = DepMsgs; dm->msg != NULL; dm++) {
1414 if (dm->ntag != -1) {
1415 ds = rpmfcDependencies(fc, dm->ntag);
1416 }
1417 if (dm->ftag == 0)
1418 continue;
1419
1420 ds = rpmdsInit(ds);
1421 if (ds == NULL)
1422 continue; /* XXX can't happen */
1423
1424 bingo = 0;
1425 while (rpmdsNext(ds) >= 0) {
1426
1427 Flags = rpmdsFlags(ds);
1428
1429 if (!((Flags & dm->mask) ^ dm->xormask))
1430 continue;
1431 if (bingo == 0) {
1432 rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : ""));
1433 bingo = 1;
1434 }
1435 if ((DNEVR = rpmdsDNEVR(ds)) == NULL)
1436 continue; /* XXX can't happen */
1437 rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2);
1438 }
1439 if (bingo)
1440 rpmlog(RPMLOG_NOTICE, "\n");
1441 }
1442 }
1443
rpmfcApplyExternal(rpmfc fc)1444 static rpmRC rpmfcApplyExternal(rpmfc fc)
1445 {
1446 StringBuf sb_stdin = newStringBuf();
1447 rpmRC rc = RPMRC_OK;
1448
1449 /* Create file manifest buffer to deliver to dependency finder. */
1450 for (int i = 0; i < fc->nfiles; i++)
1451 appendLineStringBuf(sb_stdin, fc->fn[i]);
1452
1453 for (DepMsg_t dm = DepMsgs; dm->msg != NULL; dm++) {
1454 rpmTagVal tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
1455 rpmsenseFlags tagflags;
1456 char * s = NULL;
1457 StringBuf sb_stdout = NULL;
1458 int failnonzero = (tag == RPMTAG_PROVIDEFLAGS);
1459
1460 switch (tag) {
1461 case RPMTAG_PROVIDEFLAGS:
1462 if (fc->skipProv)
1463 continue;
1464 tagflags = RPMSENSE_FIND_PROVIDES;
1465 break;
1466 case RPMTAG_REQUIREFLAGS:
1467 case RPMTAG_RECOMMENDFLAGS:
1468 case RPMTAG_SUGGESTFLAGS:
1469 case RPMTAG_SUPPLEMENTFLAGS:
1470 case RPMTAG_ENHANCEFLAGS:
1471 case RPMTAG_CONFLICTFLAGS:
1472 case RPMTAG_OBSOLETEFLAGS:
1473 if (fc->skipReq)
1474 continue;
1475 tagflags = RPMSENSE_FIND_REQUIRES;
1476 break;
1477 default:
1478 continue;
1479 break;
1480 }
1481
1482 s = rpmExpand(dm->argv[0], NULL);
1483 rpmlog(RPMLOG_NOTICE, _("Finding %s: %s\n"), dm->msg, s);
1484 free(s);
1485
1486 if (rpmfcExec(dm->argv, sb_stdin, &sb_stdout,
1487 failnonzero, fc->buildRoot) == -1)
1488 continue;
1489
1490 if (sb_stdout == NULL) {
1491 rc = RPMRC_FAIL;
1492 rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1493 break;
1494 }
1495
1496 /* Parse dependencies into header */
1497 rc = parseRCPOT(NULL, fc->pkg, getStringBuf(sb_stdout), dm->ntag != -1 ? dm->ntag : RPMTAG_REQUIRENAME, 0, tagflags, addReqProvPkg, NULL);
1498 freeStringBuf(sb_stdout);
1499
1500 if (rc) {
1501 rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1502 break;
1503 }
1504 }
1505
1506 freeStringBuf(sb_stdin);
1507
1508 return rc;
1509 }
1510
1511 typedef const struct macroExport_s {
1512 const char * name;
1513 rpmTagVal tag;
1514 } * macroExport;
1515
1516 static struct macroExport_s const macroExportList[] = {
1517 { "name", RPMTAG_NAME },
1518 { "epoch", RPMTAG_EPOCH },
1519 { "version", RPMTAG_VERSION },
1520 { "release", RPMTAG_RELEASE },
1521 { NULL, 0 }
1522 };
1523
rpmfcApply(rpmfc fc)1524 rpmRC rpmfcApply(rpmfc fc)
1525 {
1526 rpmRC rc;
1527 Package pkg = fc->pkg;
1528 macroExport me;
1529 for (me = macroExportList; me->name; me++) {
1530 char *val = headerGetAsString(pkg->header, me->tag);
1531 if (val) {
1532 rpmPushMacro(NULL, me->name, NULL, val, RMIL_SPEC);
1533 free(val);
1534 }
1535 }
1536 /* If new-fangled dependency generation is disabled ... */
1537 if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) {
1538 /* ... then generate dependencies using %{__find_requires} et al. */
1539 rpmlog(RPMLOG_WARNING,
1540 _("Deprecated external dependency generator is used!\n"));
1541 rc = rpmfcApplyExternal(fc);
1542 } else {
1543 /* ... otherwise generate per-file dependencies */
1544 rc = rpmfcApplyInternal(fc);
1545 }
1546 for (me = macroExportList; me->name; me++)
1547 if (headerIsEntry(pkg->header, me->tag))
1548 rpmPopMacro(NULL, me->name);
1549 return rc;
1550 }
1551
rpmfcGenerateDepends(const rpmSpec spec,Package pkg)1552 rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
1553 {
1554 rpmfi fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD);
1555 rpmfc fc = NULL;
1556 rpm_mode_t * fmode = NULL;
1557 int ac = rpmfiFC(fi);
1558 int genConfigDeps = 0;
1559 rpmRC rc = RPMRC_OK;
1560 int idx;
1561 struct rpmtd_s td;
1562
1563 /* Skip packages with no files. */
1564 if (ac <= 0)
1565 goto exit;
1566
1567 /* Extract absolute file paths in argv format. */
1568 fmode = xcalloc(ac+1, sizeof(*fmode));
1569
1570 fi = rpmfiInit(fi, 0);
1571 while ((idx = rpmfiNext(fi)) >= 0) {
1572 /* Does package have any %config files? */
1573 genConfigDeps |= (rpmfiFFlags(fi) & RPMFILE_CONFIG);
1574 fmode[idx] = rpmfiFMode(fi);
1575 }
1576
1577 fc = rpmfcCreate(spec->buildRoot, 0);
1578 freePackage(fc->pkg);
1579 fc->pkg = pkg;
1580 fc->skipProv = !pkg->autoProv;
1581 fc->skipReq = !pkg->autoReq;
1582
1583 if (!fc->skipProv && genConfigDeps) {
1584 /* Add config dependency, Provides: config(N) = EVR */
1585 rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_PROVIDENAME, "config",
1586 rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
1587 (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1588 rpmdsMerge(packageDependencies(pkg, RPMTAG_PROVIDENAME), ds);
1589 rpmdsFree(ds);
1590 }
1591 if (!fc->skipReq && genConfigDeps) {
1592 rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_REQUIRENAME, "config",
1593 rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
1594 (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1595 rpmdsMerge(packageDependencies(pkg, RPMTAG_REQUIRENAME), ds);
1596 rpmdsFree(ds);
1597 }
1598
1599 /* Build file class dictionary. */
1600 rc = rpmfcClassify(fc, pkg->dpaths, fmode);
1601 if ( rc != RPMRC_OK )
1602 goto exit;
1603
1604 /* Build file/package dependency dictionary. */
1605 rc = rpmfcApply(fc);
1606 if (rc != RPMRC_OK)
1607 goto exit;
1608
1609 /* Add per-file colors(#files) */
1610 headerPutUint32(pkg->header, RPMTAG_FILECOLORS, fc->fcolor, fc->nfiles);
1611
1612 /* Add classes(#classes) */
1613 for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->cdict); id++) {
1614 headerPutString(pkg->header, RPMTAG_CLASSDICT,
1615 rpmstrPoolStr(fc->cdict, id));
1616 }
1617
1618 /* Add per-file classes(#files) */
1619 headerPutUint32(pkg->header, RPMTAG_FILECLASS, fc->fcdictx, fc->nfiles);
1620
1621 /* Add dependency dictionary(#dependencies) */
1622 if (rpmtdFromArgi(&td, RPMTAG_DEPENDSDICT, fc->ddictx)) {
1623 headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1624
1625 /* Add per-file dependency (start,number) pairs (#files) */
1626 if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSX, fc->fddictx)) {
1627 headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1628 }
1629
1630 if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSN, fc->fddictn)) {
1631 headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1632 }
1633 }
1634
1635
1636 if (_rpmfc_debug) {
1637 char *msg = NULL;
1638 rasprintf(&msg, "final: files %d cdict[%d] %d%% ddictx[%d]",
1639 fc->nfiles, rpmstrPoolNumStr(fc->cdict),
1640 ((100 * fc->fknown)/fc->nfiles), argiCount(fc->ddictx));
1641 rpmfcPrint(msg, fc, NULL);
1642 free(msg);
1643 }
1644 exit:
1645 printDeps(fc);
1646
1647 /* Clean up. */
1648 if (fc)
1649 fc->pkg = NULL;
1650 free(fmode);
1651 rpmfcFree(fc);
1652 rpmfiFree(fi);
1653
1654 return rc;
1655 }
1656