refactoring sources
[src/app-framework-main.git] / src / 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 /* remove flags that are not related to being signature */
242 void file_clear_flags()
243 {
244         unsigned int i;
245         for (i = 0 ; i < allfiles.count ; i++)
246                 allfiles.files[i]->flags &= flag_signature;
247 }
248
249 static int fill_files_rec(char name[PATH_MAX], int offset)
250 {
251         int len, err;
252         DIR *dir;
253         struct dirent *ent;
254
255         if (offset == 0)
256                 dir = opendir(".");
257         else {
258                 dir = opendir(name);
259                 name[offset++] = '/';
260         }
261         if (!dir) {
262                 syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, name);
263                 return -1;
264         }
265
266         ent = readdir(dir);
267         while (ent != NULL) {
268                 len = strlen(ent->d_name);
269                 if (ent->d_name[0] == '.' && (len == 1 || 
270                         (ent->d_name[1] == '.' && len == 2)))
271                         ;
272                 else if (offset + len >= PATH_MAX) {
273                         closedir(dir);
274                         syslog(LOG_ERR, "name too long in fill_files_rec");
275                         errno = ENAMETOOLONG;
276                         return -1;
277                 } else {
278                         memcpy(name + offset, ent->d_name, 1+len);
279                         switch (ent->d_type) {
280                         case DT_DIR:
281                                 if (file_add_directory(name) == NULL) {
282                                         closedir(dir);
283                                         return -1;
284                                 }
285                                 err = fill_files_rec(name, offset + len);
286                                 if (err) {
287                                         closedir(dir);
288                                         return err;
289                                 }
290                                 break;
291                         case DT_REG:
292                                 if (file_add_file(name) == NULL) {
293                                         closedir(dir);
294                                         return -1;
295                                 }
296                                 break;
297                         default:
298                                 break;
299                         }
300                 }
301                 ent = readdir(dir);
302         }
303
304         closedir(dir);
305         return 0;
306 }
307
308 int fill_files()
309 {
310         char name[PATH_MAX];
311         return fill_files_rec(name, 0);
312 }
313