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