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