caae1c903874deb2b8083f60e083baf8bd2895f5
[src/app-framework-main.git] / src / wgtpkg-zip.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
19 #define _DEFAULT_SOURCE
20
21 #include <limits.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <dirent.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "verbose.h"
32 #include "wgtpkg-files.h"
33 #include "wgtpkg-workdir.h"
34 #include "wgtpkg-zip.h"
35
36 #define MODE_OF_FILE_CREATION 0644
37 #define MODE_OF_DIRECTORY_CREATION 0755
38
39 #if !defined(USE_LIBZIP)
40 #       define USE_LIBZIP 1
41 #endif
42
43 /***********************************************************
44  *        USING LIBZIP
45  ***********************************************************/
46 #if USE_LIBZIP
47
48 #include <zip.h>
49
50 static int is_valid_filename(const char *filename)
51 {
52         int lastsp = 0;
53         int index = 0;
54         unsigned char c;
55
56         c = (unsigned char)filename[index];
57         while (c) {
58                 if ((c < 0x1f)
59                  || ((lastsp = (c == 0x20)) && index == 0)
60                  || c == 0x7f || c == 0x3c || c == 0x3e
61                  || c == 0x3a || c == 0x22 
62                  || c == 0x5c || c == 0x7c || c == 0x3f
63                  || c == 0x2a || c == 0x5e || c == 0x60
64                  || c == 0x7b || c == 0x7d || c == 0x21)
65                         return 0;
66                 c = (unsigned char)filename[++index];
67         }
68         return !lastsp;
69 }
70
71 static int create_directory(char *file, int mode)
72 {
73         int rc;
74         char *last = strrchr(file, '/');
75         if (last != NULL)
76                 *last = 0;
77         rc = mkdirat(workdirfd, file, mode);
78         if (rc) {
79                 if (errno == EEXIST)
80                         rc = 0;
81                 else if (errno == ENOENT) {
82                         rc = create_directory(file, mode);
83                         if (!rc)
84                                 rc = mkdirat(workdirfd, file, mode);
85                 }
86         }
87         if (rc)
88                 ERROR("can't create directory %s", file);
89         if (last != NULL)
90                 *last = '/';
91         return rc;
92 }
93
94 static int create_file(char *file, int fmode, int dmode)
95 {
96         int fd = openat(workdirfd, file, O_CREAT|O_WRONLY|O_TRUNC, fmode);
97         if (fd < 0 && errno == ENOENT) {
98                 if (!create_directory(file, dmode))
99                         fd = openat(workdirfd, file, O_CREAT|O_WRONLY|O_TRUNC, fmode);
100         }
101         if (fd < 0)
102                 ERROR("can't create file %s", file);
103         return fd;
104 }
105
106 /* read (extract) 'zipfile' in current directory */
107 int zread(const char *zipfile, unsigned long long maxsize)
108 {
109         struct filedesc *fdesc;
110         int err, fd, len;
111         struct zip *zip;
112         zip_int64_t z64;
113         unsigned int count, index;
114         struct zip_file *zfile;
115         struct zip_stat zstat;
116         char buffer[32768];
117         ssize_t sizr, sizw;
118         size_t esize;
119
120         /* open the zip file */
121         zip = zip_open(zipfile, ZIP_CHECKCONS, &err);
122         if (!zip) {
123                 ERROR("Can't connect to file %s", zipfile);
124                 return -1;
125         }
126
127         z64 = zip_get_num_entries(zip, 0);
128         if (z64 < 0 || z64 > UINT_MAX) {
129                 ERROR("too many entries in %s", zipfile);
130                 goto error;
131         }
132         count = (unsigned int)z64;
133
134         /* records the files */
135         file_reset();
136         esize = 0;
137         for (index = 0 ; index < count ; index++) {
138                 err = zip_stat_index(zip, index, ZIP_FL_ENC_GUESS, &zstat);
139                 /* check the file name */
140                 if (!is_valid_filename(zstat.name)) {
141                         ERROR("invalid entry %s found in %s", zstat.name, zipfile);
142                         goto error;
143                 }
144                 if (zstat.name[0] == '/') {
145                         ERROR("absolute entry %s found in %s", zstat.name, zipfile);
146                         goto error;
147                 }
148                 len = strlen(zstat.name);
149                 if (len == 0) {
150                         ERROR("empty entry found in %s", zipfile);
151                         goto error;
152                 }
153                 if (zstat.name[len - 1] == '/')
154                         /* record */
155                         fdesc = file_add_directory(zstat.name);
156                 else {
157                         /* get the size */
158                         esize += zstat.size;
159                         /* record */
160                         fdesc = file_add_file(zstat.name);
161                 }
162                 if (!fdesc)
163                         goto error;
164                 fdesc->zindex = index;
165         }
166
167         /* check the size */
168         if (maxsize && esize > maxsize) {
169                 ERROR("extracted size %zu greater than allowed size %llu", esize, maxsize);
170                 goto error;
171         }
172
173         /* unpack the recorded files */
174         assert(count == file_count());
175         for (index = 0 ; index < count ; index++) {
176                 fdesc = file_of_index(index);
177                 assert(fdesc != NULL);
178                 err = zip_stat_index(zip, fdesc->zindex, ZIP_FL_ENC_GUESS, &zstat);
179                 assert(zstat.name[0] != '/');
180                 len = strlen(zstat.name);
181                 assert(len > 0);
182                 if (zstat.name[len - 1] == '/') {
183                         /* directory name */
184                         err = create_directory((char*)zstat.name, MODE_OF_DIRECTORY_CREATION);
185                         if (err && errno != EEXIST)
186                                 goto error;
187                 } else {
188                         /* file name */
189                         zfile = zip_fopen_index(zip, fdesc->zindex, 0);
190                         if (!zfile) {
191                                 ERROR("Can't open %s in %s", zstat.name, zipfile);
192                                 goto error;
193                         }
194                         fd = create_file((char*)zstat.name, MODE_OF_FILE_CREATION, MODE_OF_DIRECTORY_CREATION);
195                         if (fd < 0)
196                                 goto errorz;
197                         /* extract */
198                         z64 = zstat.size;
199                         while (z64) {
200                                 sizr = zip_fread(zfile, buffer, sizeof buffer);
201                                 if (sizr < 0) {
202                                         ERROR("error while reading %s in %s", zstat.name, zipfile);
203                                         goto errorzf;
204                                 }
205                                 sizw = write(fd, buffer, sizr);
206                                 if (sizw < 0) {
207                                         ERROR("error while writing %s", zstat.name);
208                                         goto errorzf;
209                                 }
210                                 z64 -= sizw;
211                         }
212                         close(fd);
213                         zip_fclose(zfile);
214                 }
215         }
216
217         zip_close(zip);
218         return 0;
219
220 errorzf:
221         close(fd);
222 errorz:
223         zip_fclose(zfile);
224 error:
225         zip_close(zip);
226         return -1;
227 }
228
229 struct zws {
230         struct zip *zip;
231         char name[PATH_MAX];
232         char buffer[32768];
233 };
234
235 static int zwr(struct zws *zws, int offset)
236 {
237         int len, err, fd;
238         DIR *dir;
239         struct dirent *ent;
240         zip_int64_t z64;
241         struct zip_source *zsrc;
242         FILE *fp;
243
244         fd = openat(workdirfd, offset ? zws->name : ".", O_DIRECTORY|O_RDONLY);
245         if (fd < 0) {
246                 ERROR("opendir %.*s failed in zwr", offset, zws->name);
247                 return -1;
248         }
249         dir = fdopendir(fd);
250         if (!dir) {
251                 close(fd);
252                 ERROR("opendir %.*s failed in zwr", offset, zws->name);
253                 return -1;
254         }
255
256         if (offset != 0)
257                 zws->name[offset++] = '/';
258
259         ent = readdir(dir);
260         while (ent != NULL) {
261                 len = strlen(ent->d_name);
262                 if (ent->d_name[0] == '.' && (len == 1 || 
263                         (ent->d_name[1] == '.' && len == 2)))
264                         ;
265                 else if (offset + len >= sizeof(zws->name)) {
266                         ERROR("name too long in zwr");
267                         errno = ENAMETOOLONG;
268                         goto error;
269                 } else {
270                         memcpy(zws->name + offset, ent->d_name, 1+len);
271                         if (!is_valid_filename(ent->d_name)) {
272                                 ERROR("invalid name %s", zws->name);
273                                 goto error;
274                         }
275                         switch (ent->d_type) {
276                         case DT_DIR:
277                                 z64 = zip_dir_add(zws->zip, zws->name, ZIP_FL_ENC_UTF_8);
278                                 if (z64 < 0) {
279                                         ERROR("zip_dir_add of %s failed", zws->name);
280                                         goto error;
281                                 }
282                                 err = zwr(zws, offset + len);
283                                 if (err)
284                                         goto error;
285                                 break;
286                         case DT_REG:
287                                 fd = openat(workdirfd, zws->name, O_RDONLY);
288                                 if (fd < 0) {
289                                         ERROR("openat of %s failed", zws->name);
290                                         goto error;
291                                 }
292                                 fp = fdopen(fd, "r");
293                                 if (fp == NULL) {
294                                         ERROR("fdopen of %s failed", zws->name);
295                                         close(fd);
296                                         goto error;
297                                 }
298                                 zsrc = zip_source_filep(zws->zip, fp, 0, 0);
299                                 if (zsrc == NULL) {
300                                         ERROR("zip_source_file of %s failed", zws->name);
301                                         fclose(fp);
302                                         goto error;
303                                 }
304                                 z64 = zip_file_add(zws->zip, zws->name, zsrc, ZIP_FL_ENC_UTF_8);
305                                 if (z64 < 0) {
306                                         ERROR("zip_file_add of %s failed", zws->name);
307                                         zip_source_free(zsrc);
308                                         goto error;
309                                 }
310                                 break;
311                         default:
312                                 break;
313                         }
314                 }
315                 ent = readdir(dir);
316         }
317
318         closedir(dir);
319         return 0;
320 error:
321         closedir(dir);
322         return -1;
323 }
324
325 /* write (pack) content of the current directory in 'zipfile' */
326 int zwrite(const char *zipfile)
327 {
328         int err;
329         struct zws zws;
330
331         zws.zip = zip_open(zipfile, ZIP_CREATE|ZIP_TRUNCATE, &err);
332         if (!zws.zip) {
333                 ERROR("Can't open %s for write", zipfile);
334                 return -1;
335         }
336
337         err = zwr(&zws, 0);
338         zip_close(zws.zip);
339         return err;
340 }
341
342 /***********************************************************
343  *        NOT USING LIBZIP: FORKING
344  ***********************************************************/
345 #else
346
347 #include <sys/wait.h>
348 #include <stdlib.h>
349
350 extern char **environ;
351
352 static char *getbin(const char *progname)
353 {
354         char name[PATH_MAX];
355         char *path;
356         int i;
357
358         if (progname[0] == '/')
359                 return access(progname, X_OK) ? NULL : strdup(progname);
360
361         path = getenv("PATH");
362         while(path && *path) {
363                 for (i = 0 ; path[i] && path[i] != ':' ; i++)
364                         name[i] = path[i];
365                 path += i + !!path[i];
366                 name[i] = '/';
367                 strcpy(name + i + 1, progname);
368                 if (access(name, X_OK) == 0)
369                         return realpath(name, NULL);
370         }
371         return NULL;
372 }
373
374 static int zrun(const char *name, const char *args[])
375 {
376         int rc;
377         siginfo_t si;
378         char *binary;
379
380         binary = getbin(name);
381         if (binary == NULL) {
382                 ERROR("error while forking in zrun: can't find %s", name);
383                 return -1;
384         }
385
386         rc = fork();
387         if (rc == 0) {
388                 rc = execve(binary, (char * const*)args, environ);
389                 ERROR("can't execute %s in zrun: %m", args[0]);
390                 _exit(1);
391                 return rc;
392         }
393
394         free(binary);
395         if (rc < 0) {
396                 /* can't fork */
397                 ERROR("error while forking in zrun: %m");
398                 return rc;
399         }
400
401         /* wait termination of the child */
402         rc = waitid(P_PID, (id_t)rc, &si, WEXITED);
403         if (rc)
404                 ERROR("unexpected wait status in zrun of %s: %m", args[0]);
405         else if (si.si_code != CLD_EXITED)
406                 ERROR("unexpected termination status of %s in zrun", args[0]);
407         else if (si.si_status != 0)
408                 ERROR("child for %s terminated with error code %d in zwrite", args[0], si.si_status);
409         else
410                 return 0;
411         return -1;
412 }
413
414 /* read (extract) 'zipfile' in current directory */
415 int zread(const char *zipfile, unsigned long long maxsize)
416 {
417         int rc;
418         const char *args[6];
419
420         args[0] = "unzip";
421         args[1] = "-q";
422         args[2] = "-d";
423         args[3] = workdir;
424         args[4] = zipfile;
425         args[5] = NULL;
426
427         file_reset();
428         rc = zrun(args[0], args);
429         if (!rc)
430                 rc = fill_files();
431         return rc;
432 }
433
434 /* write (pack) content of the current directory in 'zipfile' */
435 int zwrite(const char *zipfile)
436 {
437         const char *args[6];
438
439         args[0] = "zip";
440         args[1] = "-q";
441         args[2] = "-r";
442         args[3] = zipfile;
443         args[4] = workdir;
444         args[5] = NULL;
445
446         return zrun(args[0], args);
447 }
448
449 #endif
450 /***********************************************************
451 *        TESTING
452 ***********************************************************/
453
454 #if defined(TEST_READ)
455 int main(int ac, char **av)
456 {
457         for(av++ ; *av ; av++)
458                 zread(*av, 0);
459         return 0;
460 }
461 #endif
462
463 #if defined(TEST_WRITE)
464 int main(int ac, char **av)
465 {
466         for(av++ ; *av ; av++)
467                 zwrite(*av);
468         return 0;
469 }
470 #endif
471