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