Require libzip >= 1.0
[src/app-framework-main.git] / wgtpkg-files.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 #define _GNU_SOURCE
17 #include <stdlib.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <assert.h>
21 #include <syslog.h>
22 #include <dirent.h>
23 #include <stdio.h>
24
25 #include "wgtpkg.h"
26
27 struct fdb {
28         unsigned int count;
29         struct filedesc **files;
30 };
31
32 static struct fdb allfiles = { .count = 0, .files = NULL };
33 static struct fdb allsignatures = { .count = 0, .files = NULL };
34
35 static const char author_file[] = "author-signature.xml";
36 static const char distributor_file_prefix[] = "signature";
37 static const char distributor_file_suffix[] = ".xml";
38
39 static unsigned int what_signature(const char *name)
40 {
41         unsigned int len, id, nid;
42
43         if (!strcmp(name, author_file))
44                 return UINT_MAX;
45
46         len = sizeof(distributor_file_prefix)-1;
47         if (memcmp(name, distributor_file_prefix, len))
48                 return 0;
49         if (name[len] <= '0' || name[len] > '9')
50                 return 0;
51         id = (unsigned int)(name[len++] - '0');
52         while ('0' <= name[len] && name[len] <= '9') {
53                 nid = 10 * id + (unsigned int)(name[len++] - '0');
54                 if (nid < id || nid == UINT_MAX) {
55                         syslog(LOG_WARNING, "number too big for %s", name);
56                         return 0;
57                 }
58                 id = nid;
59         }
60         if (strcmp(name+len, distributor_file_suffix))
61                 return 0;
62
63         return id;
64 }
65
66 static struct filedesc *get_filedesc(const char *name, int create)
67 {
68         int cmp;
69         unsigned int low, up, mid, sig;
70         struct filedesc *result, **grow;
71
72         /* search */
73         low = 0;
74         up = allfiles.count;
75         while(low < up) {
76                 mid = (low + up) >> 1;
77                 result = allfiles.files[mid];
78                 cmp = strcmp(result->name, name);
79                 if (!cmp)
80                         return result; /* found */
81                 if (cmp > 0)
82                         up = mid;
83                 else
84                         low = mid + 1;
85         }
86
87         /* not found, can create ? */
88         if (!create)
89                 return NULL;
90
91         sig = what_signature(name);
92
93         /* allocations */
94         grow = realloc(allfiles.files, (allfiles.count + 1) * sizeof(struct filedesc *));
95         if (grow == NULL) {
96                 syslog(LOG_ERR, "realloc failed in get_filedesc");
97                 return NULL;
98         }
99         allfiles.files = grow;
100
101         if (sig) {
102                 grow = realloc(allsignatures.files, (allsignatures.count + 1) * sizeof(struct filedesc *));
103                 if (grow == NULL) {
104                         syslog(LOG_ERR, "second realloc failed in get_filedesc");
105                         return NULL;
106                 }
107                 allsignatures.files = grow;
108         }
109
110         result = malloc(sizeof(struct filedesc) + strlen(name));
111         if (!result) {
112                 syslog(LOG_ERR, "calloc failed in get_filedesc");
113                 return NULL;
114         }
115
116         /* initialisation */
117         result->type = type_unset;
118         result->flags = sig == 0 ? 0 : sig == UINT_MAX ? flag_author_signature : flag_distributor_signature;
119         result->zindex = 0;
120         result->signum = sig;
121         strcpy(result->name, name);
122
123         /* insertion */
124         if (low < allfiles.count)
125                 memmove(allfiles.files+low+1, allfiles.files+low, (allfiles.count - low) * sizeof(struct filedesc *));
126         allfiles.files[low] = result;
127         allfiles.count++;
128         if (sig) {
129                 for (low = 0 ; low < allsignatures.count && sig > allsignatures.files[low]->signum ; low++);
130                 if (low < allsignatures.count)
131                         memmove(allsignatures.files+low+1, allsignatures.files+low, (allsignatures.count - low) * sizeof(struct filedesc *));
132                 allsignatures.files[low] = result;
133                 allsignatures.count++;
134         }
135
136         return result;
137 }
138         
139
140 static struct filedesc *file_add(const char *name, enum entrytype type)
141 {
142         struct filedesc *desc;
143
144         desc = get_filedesc(name, 1);
145         if (!desc)
146                 errno = ENOMEM;
147         else if (desc->type == type_unset)
148                 desc->type = type;
149         else {
150                 syslog(LOG_ERR, "redeclaration of %s in file_add", name);
151                 errno = EEXIST;
152                 desc = NULL;
153         }
154         return desc;
155 }
156
157 void file_reset()
158 {
159         unsigned int i;
160
161         allsignatures.count = 0;
162         for (i = 0 ; i < allfiles.count ; i++)
163                 free(allfiles.files[i]);
164         allfiles.count = 0;
165 }
166
167 unsigned int file_count()
168 {
169         return allfiles.count;
170 }
171
172 struct filedesc *file_of_index(unsigned int index)
173 {
174         assert(index < allfiles.count);
175         return allfiles.files[index];
176 }
177
178 struct filedesc *file_of_name(const char *name)
179 {
180         return get_filedesc(name, 0);
181 }
182
183 struct filedesc *file_add_directory(const char *name)
184 {
185         return file_add(name, type_directory);
186 }
187
188 struct filedesc *file_add_file(const char *name)
189 {
190         return file_add(name, type_file);
191 }
192
193 unsigned int signature_count()
194 {
195         return allsignatures.count;
196 }
197
198 struct filedesc *signature_of_index(unsigned int index)
199 {
200         assert(index < allsignatures.count);
201         return allsignatures.files[index];
202 }
203
204 struct filedesc *get_signature(unsigned int number)
205 {
206         unsigned int idx;
207
208         if (number == 0)
209                 number = UINT_MAX;
210         for (idx = 0 ; idx < allsignatures.count ; idx++)
211                 if (allsignatures.files[idx]->signum == number)
212                         return allsignatures.files[idx];
213         return NULL;
214 }
215
216 struct filedesc *create_signature(unsigned int number)
217 {
218         struct filedesc *result;
219         char *name;
220         int len;
221
222         result = NULL;
223         if (number == 0 || number == UINT_MAX)
224                 len = asprintf(&name, "%s", author_file);
225         else
226                 len = asprintf(&name, "%s%u%s", distributor_file_prefix, number, distributor_file_suffix);
227
228         if (len < 0)
229                 syslog(LOG_ERR, "asprintf failed in create_signature");
230         else {
231                 assert(len > 0);
232                 result = file_of_name(name);
233                 if (result == NULL)
234                         result = file_add_file(name);
235                 free(name);
236         }
237
238         return result;
239 }
240
241 void file_clear_flags()
242 {
243         unsigned int i;
244         for (i = 0 ; i < allfiles.count ; i++)
245                 allfiles.files[i]->flags &= flag_signature;
246 }
247
248 static int fill_files_rec(char name[PATH_MAX], int offset)
249 {
250         int len, err;
251         DIR *dir;
252         struct dirent *ent;
253
254         if (offset == 0)
255                 dir = opendir(".");
256         else {
257                 dir = opendir(name);
258                 name[offset++] = '/';
259         }
260         if (!dir) {
261                 syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, name);
262                 return -1;
263         }
264
265         ent = readdir(dir);
266         while (ent != NULL) {
267                 len = strlen(ent->d_name);
268                 if (ent->d_name[0] == '.' && (len == 1 || 
269                         (ent->d_name[1] == '.' && len == 2)))
270                         ;
271                 else if (offset + len >= PATH_MAX) {
272                         closedir(dir);
273                         syslog(LOG_ERR, "name too long in fill_files_rec");
274                         errno = ENAMETOOLONG;
275                         return -1;
276                 } else {
277                         memcpy(name + offset, ent->d_name, 1+len);
278                         switch (ent->d_type) {
279                         case DT_DIR:
280                                 if (file_add_directory(name) == NULL) {
281                                         closedir(dir);
282                                         return -1;
283                                 }
284                                 err = fill_files_rec(name, offset + len);
285                                 if (err) {
286                                         closedir(dir);
287                                         return err;
288                                 }
289                                 break;
290                         case DT_REG:
291                                 if (file_add_file(name) == NULL) {
292                                         closedir(dir);
293                                         return -1;
294                                 }
295                                 break;
296                         default:
297                                 break;
298                         }
299                 }
300                 ent = readdir(dir);
301         }
302
303         closedir(dir);
304         return 0;
305 }
306
307 int fill_files()
308 {
309         char name[PATH_MAX];
310         return fill_files_rec(name, 0);
311 }
312