Fix a possible access to uninitialized memory
[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
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 (strncmp(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], unsigned offset)
256 {
257         int err, fd;
258         unsigned len;
259         DIR *dir;
260         struct dirent *ent;
261
262         fd = openat(workdirfd, offset ? name : ".", O_DIRECTORY|O_RDONLY);
263         if (fd < 0) {
264                 ERROR("openat %.*s failed in fill_files_rec", offset, name);
265                 return -1;
266         }
267         dir = fdopendir(fd);
268         if (!dir) {
269                 ERROR("opendir %.*s failed in fill_files_rec", offset, name);
270                 close(fd);
271                 return -1;
272         }
273         if (offset)
274                 name[offset++] = '/';
275
276         ent = readdir(dir);
277         while (ent != NULL) {
278                 len = (unsigned)strlen(ent->d_name);
279                 if (ent->d_name[0] == '.' && (len == 1 || 
280                         (ent->d_name[1] == '.' && len == 2)))
281                         ;
282                 else if (offset + len >= PATH_MAX) {
283                         closedir(dir);
284                         ERROR("name too long in fill_files_rec");
285                         errno = ENAMETOOLONG;
286                         return -1;
287                 } else {
288                         memcpy(name + offset, ent->d_name, 1+len);
289                         switch (ent->d_type) {
290                         case DT_DIR:
291                                 if (file_add_directory(name) == NULL) {
292                                         closedir(dir);
293                                         return -1;
294                                 }
295                                 err = fill_files_rec(name, offset + len);
296                                 if (err) {
297                                         closedir(dir);
298                                         return err;
299                                 }
300                                 break;
301                         case DT_REG:
302                                 if (file_add_file(name) == NULL) {
303                                         closedir(dir);
304                                         return -1;
305                                 }
306                                 break;
307                         default:
308                                 break;
309                         }
310                 }
311                 ent = readdir(dir);
312         }
313
314         closedir(dir);
315         return 0;
316 }
317
318 int fill_files()
319 {
320         char name[PATH_MAX];
321         return fill_files_rec(name, 0);
322 }
323