wgtpkg-zip: fix bug when file's size is zero
[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 0640
37 #define MODE_OF_DIRECTORY_CREATION 0750
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                 if (zstat.size == 0) {
181                         /* directory name */
182                         err = create_directory((char*)zstat.name, MODE_OF_DIRECTORY_CREATION);
183                         if (err && errno != EEXIST)
184                                 goto error;
185                 } else {
186                         /* file name */
187                         zfile = zip_fopen_index(zip, fdesc->zindex, 0);
188                         if (!zfile) {
189                                 ERROR("Can't open %s in %s", zstat.name, zipfile);
190                                 goto error;
191                         }
192                         fd = create_file((char*)zstat.name, MODE_OF_FILE_CREATION, MODE_OF_DIRECTORY_CREATION);
193                         if (fd < 0)
194                                 goto errorz;
195                         /* extract */
196                         z64 = zstat.size;
197                         while (z64) {
198                                 sizr = zip_fread(zfile, buffer, sizeof buffer);
199                                 if (sizr < 0) {
200                                         ERROR("error while reading %s in %s", zstat.name, zipfile);
201                                         goto errorzf;
202                                 }
203                                 sizw = write(fd, buffer, sizr);
204                                 if (sizw < 0) {
205                                         ERROR("error while writing %s", zstat.name);
206                                         goto errorzf;
207                                 }
208                                 z64 -= sizw;
209                         }
210                         close(fd);
211                         zip_fclose(zfile);
212                 }
213         }
214
215         zip_close(zip);
216         return 0;
217
218 errorzf:
219         close(fd);
220 errorz:
221         zip_fclose(zfile);
222 error:
223         zip_close(zip);
224         return -1;
225 }
226
227 struct zws {
228         struct zip *zip;
229         char name[PATH_MAX];
230         char buffer[32768];
231 };
232
233 static int zwr(struct zws *zws, int offset)
234 {
235         int len, err, fd;
236         DIR *dir;
237         struct dirent *ent;
238         zip_int64_t z64;
239         struct zip_source *zsrc;
240         FILE *fp;
241
242         fd = openat(workdirfd, offset ? zws->name : ".", O_DIRECTORY|O_RDONLY);
243         if (fd < 0) {
244                 ERROR("opendir %.*s failed in zwr", offset, zws->name);
245                 return -1;
246         }
247         dir = fdopendir(fd);
248         if (!dir) {
249                 close(fd);
250                 ERROR("opendir %.*s failed in zwr", offset, zws->name);
251                 return -1;
252         }
253
254         if (offset != 0)
255                 zws->name[offset++] = '/';
256
257         ent = readdir(dir);
258         while (ent != NULL) {
259                 len = strlen(ent->d_name);
260                 if (ent->d_name[0] == '.' && (len == 1 || 
261                         (ent->d_name[1] == '.' && len == 2)))
262                         ;
263                 else if (offset + len >= sizeof(zws->name)) {
264                         ERROR("name too long in zwr");
265                         errno = ENAMETOOLONG;
266                         goto error;
267                 } else {
268                         memcpy(zws->name + offset, ent->d_name, 1+len);
269                         if (!is_valid_filename(ent->d_name)) {
270                                 ERROR("invalid name %s", zws->name);
271                                 goto error;
272                         }
273                         switch (ent->d_type) {
274                         case DT_DIR:
275                                 z64 = zip_dir_add(zws->zip, zws->name, ZIP_FL_ENC_UTF_8);
276                                 if (z64 < 0) {
277                                         ERROR("zip_dir_add of %s failed", zws->name);
278                                         goto error;
279                                 }
280                                 err = zwr(zws, offset + len);
281                                 if (err)
282                                         goto error;
283                                 break;
284                         case DT_REG:
285                                 fd = openat(workdirfd, zws->name, O_RDONLY);
286                                 if (fd < 0) {
287                                         ERROR("openat of %s failed", zws->name);
288                                         goto error;
289                                 }
290                                 fp = fdopen(fd, "r");
291                                 if (fp == NULL) {
292                                         ERROR("fdopen of %s failed", zws->name);
293                                         close(fd);
294                                         goto error;
295                                 }
296                                 zsrc = zip_source_filep(zws->zip, fp, 0, 0);
297                                 if (zsrc == NULL) {
298                                         ERROR("zip_source_file of %s failed", zws->name);
299                                         fclose(fp);
300                                         goto error;
301                                 }
302                                 z64 = zip_file_add(zws->zip, zws->name, zsrc, ZIP_FL_ENC_UTF_8);
303                                 if (z64 < 0) {
304                                         ERROR("zip_file_add of %s failed", zws->name);
305                                         zip_source_free(zsrc);
306                                         goto error;
307                                 }
308                                 break;
309                         default:
310                                 break;
311                         }
312                 }
313                 ent = readdir(dir);
314         }
315
316         closedir(dir);
317         return 0;
318 error:
319         closedir(dir);
320         return -1;
321 }
322
323 /* write (pack) content of the current directory in 'zipfile' */
324 int zwrite(const char *zipfile)
325 {
326         int err;
327         struct zws zws;
328
329         zws.zip = zip_open(zipfile, ZIP_CREATE|ZIP_TRUNCATE, &err);
330         if (!zws.zip) {
331                 ERROR("Can't open %s for write", zipfile);
332                 return -1;
333         }
334
335         err = zwr(&zws, 0);
336         zip_close(zws.zip);
337         return err;
338 }
339
340 /***********************************************************
341  *        NOT USING LIBZIP: FORKING
342  ***********************************************************/
343 #else
344
345 #include <sys/wait.h>
346 #include <stdlib.h>
347
348 extern char **environ;
349
350 static char *getbin(const char *progname)
351 {
352         char name[PATH_MAX];
353         char *path;
354         int i;
355
356         if (progname[0] == '/')
357                 return access(progname, X_OK) ? NULL : strdup(progname);
358
359         path = getenv("PATH");
360         while(path && *path) {
361                 for (i = 0 ; path[i] && path[i] != ':' ; i++)
362                         name[i] = path[i];
363                 path += i + !!path[i];
364                 name[i] = '/';
365                 strcpy(name + i + 1, progname);
366                 if (access(name, X_OK) == 0)
367                         return realpath(name, NULL);
368         }
369         return NULL;
370 }
371
372 static int zrun(const char *name, const char *args[])
373 {
374         int rc;
375         siginfo_t si;
376         char *binary;
377
378         binary = getbin(name);
379         if (binary == NULL) {
380                 ERROR("error while forking in zrun: can't find %s", name);
381                 return -1;
382         }
383
384         rc = fork();
385         if (rc == 0) {
386                 rc = execve(binary, (char * const*)args, environ);
387                 ERROR("can't execute %s in zrun: %m", args[0]);
388                 _exit(1);
389                 return rc;
390         }
391
392         free(binary);
393         if (rc < 0) {
394                 /* can't fork */
395                 ERROR("error while forking in zrun: %m");
396                 return rc;
397         }
398
399         /* wait termination of the child */
400         rc = waitid(P_PID, (id_t)rc, &si, WEXITED);
401         if (rc)
402                 ERROR("unexpected wait status in zrun of %s: %m", args[0]);
403         else if (si.si_code != CLD_EXITED)
404                 ERROR("unexpected termination status of %s in zrun", args[0]);
405         else if (si.si_status != 0)
406                 ERROR("child for %s terminated with error code %d in zwrite", args[0], si.si_status);
407         else
408                 return 0;
409         return -1;
410 }
411
412 /* read (extract) 'zipfile' in current directory */
413 int zread(const char *zipfile, unsigned long long maxsize)
414 {
415         int rc;
416         const char *args[6];
417
418         args[0] = "unzip";
419         args[1] = "-q";
420         args[2] = "-d";
421         args[3] = workdir;
422         args[4] = zipfile;
423         args[5] = NULL;
424
425         file_reset();
426         rc = zrun(args[0], args);
427         if (!rc)
428                 rc = fill_files();
429         return rc;
430 }
431
432 /* write (pack) content of the current directory in 'zipfile' */
433 int zwrite(const char *zipfile)
434 {
435         const char *args[6];
436
437         args[0] = "zip";
438         args[1] = "-q";
439         args[2] = "-r";
440         args[3] = zipfile;
441         args[4] = workdir;
442         args[5] = NULL;
443
444         return zrun(args[0], args);
445 }
446
447 #endif
448 /***********************************************************
449 *        TESTING
450 ***********************************************************/
451
452 #if defined(TEST_READ)
453 int main(int ac, char **av)
454 {
455         for(av++ ; *av ; av++)
456                 zread(*av, 0);
457         return 0;
458 }
459 #endif
460
461 #if defined(TEST_WRITE)
462 int main(int ac, char **av)
463 {
464         for(av++ ; *av ; av++)
465                 zwrite(*av);
466         return 0;
467 }
468 #endif
469