1 /*
2 * Copyright (c) 2018, SUSE LLC
3 *
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
6 */
7
8 /*
9 * filelistfilter.c
10 *
11 * Support repodata with a filelist filtered by a custom filter
12 */
13
14 #define _GNU_SOURCE
15 #include <string.h>
16 #include <fnmatch.h>
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <unistd.h>
22
23 #include "repo.h"
24 #include "pool.h"
25 #include "util.h"
26
27 static Id default_filelist_filter;
28
29 #define FF_EXACT 0
30 #define FF_END 1
31 #define FF_START 2
32 #define FF_SUB 3 /* FF_END | FF_START */
33 #define FF_GLOB 4
34 #define FF_START5 5
35
36 void
repodata_free_filelistfilter(Repodata * data)37 repodata_free_filelistfilter(Repodata *data)
38 {
39 if (data->filelistfilter)
40 {
41 if (data->filelistfilter != &default_filelist_filter)
42 solv_free(data->filelistfilter);
43 data->filelistfilter = 0;
44 }
45 data->filelistfilterdata = solv_free(data->filelistfilterdata);
46 }
47
48 static void
repodata_set_filelistfilter(Repodata * data)49 repodata_set_filelistfilter(Repodata *data)
50 {
51 Id type;
52 Queue q;
53 int i, j;
54 char *filterdata;
55 int nfilterdata;
56
57 if (data->filelistfilter && data->filelistfilter != &default_filelist_filter)
58 data->filelistfilter = solv_free(data->filelistfilter);
59 data->filelistfilterdata = solv_free(data->filelistfilterdata);
60 type = repodata_lookup_type(data, SOLVID_META, REPOSITORY_FILTEREDFILELIST);
61 if (type != REPOKEY_TYPE_IDARRAY)
62 {
63 data->filelistfilter = &default_filelist_filter;
64 return;
65 }
66 queue_init(&q);
67 repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_FILTEREDFILELIST, &q);
68 if (q.count == 3)
69 {
70 /* check if this is the default filter */
71 int t = 0;
72 for (i = 0; i < 3; i++)
73 {
74 Id id = q.elements[i];
75 const char *g = data->localpool ? stringpool_id2str(&data->spool, id) : pool_id2str(data->repo->pool, id);
76 if (!strcmp(g, "*bin/*"))
77 t |= 1;
78 else if (!strcmp(g, "/etc/*"))
79 t |= 2;
80 else if (!strcmp(g, "/usr/lib/sendmail"))
81 t |= 4;
82 }
83 if (t == 7)
84 {
85 queue_free(&q);
86 data->filelistfilter = &default_filelist_filter;
87 return;
88 }
89 }
90 data->filelistfilter = solv_calloc(q.count * 2 + 1, sizeof(Id));
91 filterdata = solv_calloc_block(1, 1, 255);
92 nfilterdata = 1;
93
94 for (i = j = 0; i < q.count; i++)
95 {
96 Id id = q.elements[i];
97 const char *g = data->localpool ? stringpool_id2str(&data->spool, id) : pool_id2str(data->repo->pool, id);
98 const char *p;
99 int t = FF_EXACT;
100 int gl;
101 if (!id || !g || !*g)
102 continue;
103 for (p = g; *p && t != FF_GLOB; p++)
104 {
105 if (*p == '*')
106 {
107 if (p == g)
108 t |= FF_END;
109 else if (!p[1])
110 t |= FF_START;
111 else
112 t = FF_GLOB;
113 }
114 else if (*p == '[' || *p == '?')
115 t = FF_GLOB;
116 }
117 gl = strlen(g);
118 if (t == FF_END) /* not supported */
119 t = FF_GLOB;
120 if (t == FF_START && gl == 5)
121 t = FF_START5;
122 filterdata = solv_extend(filterdata, nfilterdata, gl + 1, 1, 255);
123 data->filelistfilter[j++] = nfilterdata;
124 data->filelistfilter[j++] = t;
125 switch (t)
126 {
127 case FF_START:
128 case FF_START5:
129 strcpy(filterdata + nfilterdata, g);
130 filterdata[nfilterdata + gl - 1] = 0;
131 nfilterdata += gl;
132 break;
133 case FF_SUB:
134 strcpy(filterdata + nfilterdata, g + 1);
135 filterdata[nfilterdata + gl - 2] = 0;
136 nfilterdata += gl - 1;
137 break;
138 default:
139 strcpy(filterdata + nfilterdata, g);
140 nfilterdata += gl + 1;
141 break;
142 }
143 }
144 filterdata = solv_realloc(filterdata, nfilterdata);
145 data->filelistfilter[j++] = 0;
146 data->filelistfilterdata = filterdata;
147 queue_free(&q);
148 }
149
150 int
repodata_filelistfilter_matches(Repodata * data,const char * str)151 repodata_filelistfilter_matches(Repodata *data, const char *str)
152 {
153 Id *ff;
154 if (data && !data->filelistfilter)
155 repodata_set_filelistfilter(data);
156 if (!data || data->filelistfilter == &default_filelist_filter)
157 {
158 /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
159 if (strstr(str, "bin/"))
160 return 1;
161 if (!strncmp(str, "/etc/", 5))
162 return 1;
163 if (!strcmp(str, "/usr/lib/sendmail"))
164 return 1;
165 return 0;
166 }
167 for (ff = data->filelistfilter; *ff; ff += 2)
168 {
169 const char *g = data->filelistfilterdata + *ff;
170 switch (ff[1])
171 {
172 case FF_EXACT:
173 if (!strcmp(str, g))
174 return 1;
175 break;
176 case FF_START:
177 if (!strncmp(str, g, strlen(g)))
178 return 1;
179 break;
180 case FF_SUB:
181 if (!strstr(str, g))
182 return 1;
183 break;
184 case FF_START5:
185 if (!strncmp(str, g, 5))
186 return 1;
187 break;
188 default:
189 if (!fnmatch(g, str, 0))
190 return 1;
191 break;
192 }
193 }
194 return 0;
195 }
196
197