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