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