work in progress
[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 #include <fcntl.h>
25 #include <unistd.h>
26
27 #include "verbose.h"
28 #include "wgtpkg.h"
29
30 struct fdb {
31         unsigned int count;
32         struct filedesc **files;
33 };
34
35 static struct fdb allfiles = { .count = 0, .files = NULL };
36 static struct fdb allsignatures = { .count = 0, .files = NULL };
37
38 static const char author_file[] = "author-signature.xml";
39 static const char distributor_file_prefix[] = "signature";
40 static const char distributor_file_suffix[] = ".xml";
41
42 static unsigned int what_signature(const char *name)
43 {
44         unsigned int len, id, nid;
45
46         if (!strcmp(name, author_file))
47                 return UINT_MAX;
48
49         len = sizeof(distributor_file_prefix)-1;
50         if (memcmp(name, distributor_file_prefix, len))
51                 return 0;
52         if (name[len] <= '0' || name[len] > '9')
53                 return 0;
54         id = (unsigned int)(name[len++] - '0');
55         while ('0' <= name[len] && name[len] <= '9') {
56                 nid = 10 * id + (unsigned int)(name[len++] - '0');
57                 if (nid < id || nid == UINT_MAX) {
58                         syslog(LOG_WARNING, "number too big for %s", name);
59                         return 0;
60                 }
61                 id = nid;
62         }
63         if (strcmp(name+len, distributor_file_suffix))
64                 return 0;
65
66         return id;
67 }
68
69 static struct filedesc *get_filedesc(const char *name, int create)
70 {
71         int cmp;
72         unsigned int low, up, mid, sig;
73         struct filedesc *result, **grow;
74
75         /* search */
76         low = 0;
77         up = allfiles.count;
78         while(low < up) {
79                 mid = (low + up) >> 1;
80                 result = allfiles.files[mid];
81                 cmp = strcmp(result->name, name);
82                 if (!cmp)
83                         return result; /* found */
84                 if (cmp > 0)
85                         up = mid;
86                 else
87                         low = mid + 1;
88         }
89
90         /* not found, can create ? */
91         if (!create)
92                 return NULL;
93
94         sig = what_signature(name);
95
96         /* allocations */
97         grow = realloc(allfiles.files, (allfiles.count + 1) * sizeof(struct filedesc *));
98         if (grow == NULL) {
99                 ERROR("realloc failed in get_filedesc");
100                 return NULL;
101         }
102         allfiles.files = grow;
103
104         if (sig) {
105                 grow = realloc(allsignatures.files, (allsignatures.count + 1) * sizeof(struct filedesc *));
106                 if (grow == NULL) {
107                         ERROR("second realloc failed in get_filedesc");
108                         return NULL;
109                 }
110                 allsignatures.files = grow;
111         }
112
113         result = malloc(sizeof(struct filedesc) + strlen(name));
114         if (!result) {
115                 ERROR("calloc failed in get_filedesc");
116                 return NULL;
117         }
118
119         /* initialisation */
120         result->type = type_unset;
121         result->flags = sig == 0 ? 0 : sig == UINT_MAX ? flag_author_signature : flag_distributor_signature;
122         result->zindex = 0;
123         result->signum = sig;
124         strcpy(result->name, name);
125
126         /* insertion */
127         if (low < allfiles.count)
128                 memmove(allfiles.files+low+1, allfiles.files+low, (allfiles.count - low) * sizeof(struct filedesc *));
129         allfiles.files[low] = result;
130         allfiles.count++;
131         if (sig) {
132                 for (low = 0 ; low < allsignatures.count && sig > allsignatures.files[low]->signum ; low++);
133                 if (low < allsignatures.count)
134                         memmove(allsignatures.files+low+1, allsignatures.files+low, (allsignatures.count - low) * sizeof(struct filedesc *));
135                 allsignatures.files[low] = result;
136                 allsignatures.count++;
137         }
138
139         return result;
140 }
141         
142
143 static struct filedesc *file_add(const char *name, enum entrytype type)
144 {
145         struct filedesc *desc;
146
147         desc = get_filedesc(name, 1);
148         if (!desc)
149                 errno = ENOMEM;
150         else if (desc->type == type_unset)
151                 desc->type = type;
152         else {
153                 ERROR("redeclaration of %s in file_add", name);
154                 errno = EEXIST;
155                 desc = NULL;
156         }
157         return desc;
158 }
159
160 void file_reset()
161 {
162         unsigned int i;
163
164         allsignatures.count = 0;
165         for (i = 0 ; i < allfiles.count ; i++)
166                 free(allfiles.files[i]);
167         allfiles.count = 0;
168 }
169
170 unsigned int file_count()
171 {
172         return allfiles.count;
173 }
174
175 struct filedesc *file_of_index(unsigned int index)
176 {
177         assert(index < allfiles.count);
178         return allfiles.files[index];
179 }
180
181 struct filedesc *file_of_name(const char *name)
182 {
183         return get_filedesc(name, 0);
184 }
185
186 struct filedesc *file_add_directory(const char *name)
187 {
188         return file_add(name, type_directory);
189 }
190
191 struct filedesc *file_add_file(const char *name)
192 {
193         return file_add(name, type_file);
194 }
195
196 unsigned int signature_count()
197 {
198         return allsignatures.count;
199 }
200
201 struct filedesc *signature_of_index(unsigned int index)
202 {
203         assert(index < allsignatures.count);
204         return allsignatures.files[index];
205 }
206
207 struct filedesc *get_signature(unsigned int number)
208 {
209         unsigned int idx;
210
211         if (number == 0)
212                 number = UINT_MAX;
213         for (idx = 0 ; idx < allsignatures.count ; idx++)
214                 if (allsignatures.files[idx]->signum == number)
215                         return allsignatures.files[idx];
216         return NULL;
217 }
218
219 struct filedesc *create_signature(unsigned int number)
220 {
221         struct filedesc *result;
222         char *name;
223         int len;
224
225         result = NULL;
226         if (number == 0 || number == UINT_MAX)
227                 len = asprintf(&name, "%s", author_file);
228         else
229                 len = asprintf(&name, "%s%u%s", distributor_file_prefix, number, distributor_file_suffix);
230
231         if (len < 0)
232                 ERROR("asprintf failed in create_signature");
233         else {
234                 assert(len > 0);
235                 result = file_of_name(name);
236                 if (result == NULL)
237                         result = file_add_file(name);
238                 free(name);
239         }
240
241         return result;
242 }
243
244 /* remove flags that are not related to being signature */
245 void file_clear_flags()
246 {
247         unsigned int i;
248         for (i = 0 ; i < allfiles.count ; i++)
249                 allfiles.files[i]->flags &= flag_signature;
250 }
251
252 static int fill_files_rec(char name[PATH_MAX], int offset)
253 {
254         int len, err, fd;
255         DIR *dir;
256         struct dirent *ent;
257
258         fd = openat(workdirfd, offset ? name : ".", O_DIRECTORY|O_RDONLY);
259         if (fd < 0) {
260                 ERROR("openat %.*s failed in fill_files_rec", offset, name);
261                 return -1;
262         }
263         dir = fdopendir(fd);
264         if (!dir) {
265                 ERROR("opendir %.*s failed in fill_files_rec", offset, name);
266                 close(fd);
267                 return -1;
268         }
269         if (offset)
270                 name[offset++] = '/';
271
272         ent = readdir(dir);
273         while (ent != NULL) {
274                 len = strlen(ent->d_name);
275                 if (ent->d_name[0] == '.' && (len == 1 || 
276                         (ent->d_name[1] == '.' && len == 2)))
277                         ;
278                 else if (offset + len >= PATH_MAX) {
279                         closedir(dir);
280                         ERROR("name too long in fill_files_rec");
281                         errno = ENAMETOOLONG;
282                         return -1;
283                 } else {
284                         memcpy(name + offset, ent->d_name, 1+len);
285                         switch (ent->d_type) {
286                         case DT_DIR:
287                                 if (file_add_directory(name) == NULL) {
288                                         closedir(dir);
289                                         return -1;
290                                 }
291                                 err = fill_files_rec(name, offset + len);
292                                 if (err) {
293                                         closedir(dir);
294                                         return err;
295                                 }
296                                 break;
297                         case DT_REG:
298                                 if (file_add_file(name) == NULL) {
299                                         closedir(dir);
300                                         return -1;
301                                 }
302                                 break;
303                         default:
304                                 break;
305                         }
306                 }
307                 ent = readdir(dir);
308         }
309
310         closedir(dir);
311         return 0;
312 }
313
314 int fill_files()
315 {
316         char name[PATH_MAX];
317         return fill_files_rec(name, 0);
318 }
319