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