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