initial commit
[src/app-framework-main.git] / wgtpkg-zip.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17 #define _BSD_SOURCE /* see readdir */
18
19 #include <limits.h>
20 #include <zip.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <dirent.h>
27 #include <string.h>
28 #include <syslog.h>
29 #include <unistd.h>
30
31 #include "wgtpkg.h"
32
33
34 #if !defined(MODE_OF_FILE_CREATION)
35 #define MODE_OF_FILE_CREATION 0640
36 #endif
37 #if !defined(MODE_OF_DIRECTORY_CREATION)
38 #define MODE_OF_DIRECTORY_CREATION 0750
39 #endif
40
41
42 int create_directory(char *file, int mode)
43 {
44         int rc;
45         char *last = strrchr(file, '/');
46         if (last != NULL)
47                 *last = 0;
48         rc = mkdir(file, mode);
49         if (rc) {
50                 if (errno == EEXIST)
51                         rc = 0;
52                 else if (errno == ENOENT) {
53                         rc = create_directory(file, mode);
54                         if (!rc)
55                                 rc = mkdir(file, mode);
56                 }
57         }
58         if (rc)
59                 syslog(LOG_ERR, "can't create directory %s", file);
60         if (last != NULL)
61                 *last = '/';
62         return rc;
63 }
64
65 int create_file(char *file, int fmode, int dmode)
66 {
67         int fd = creat(file, fmode);
68         if (fd < 0 && errno == ENOENT) {
69                 if (!create_directory(file, dmode))
70                         fd = creat(file, fmode);
71         }
72         if (fd < 0)
73                 syslog(LOG_ERR, "can't create file %s", file);
74         return fd;
75 }
76
77 /* read (extract) 'zipfile' in current directory */
78 int zread(const char *zipfile, unsigned long long maxsize)
79 {
80         struct filedesc *fdesc;
81         int err, fd, len;
82         struct zip *zip;
83         zip_int64_t z64;
84         unsigned int count, index;
85         struct zip_file *zfile;
86         struct zip_stat zstat;
87         char buffer[32768];
88         ssize_t sizr, sizw;
89         size_t esize;
90
91         /* open the zip file */
92         zip = zip_open(zipfile, ZIP_CHECKCONS, &err);
93         if (!zip) {
94                 syslog(LOG_ERR, "Can't connect to file %s", zipfile);
95                 return -1;
96         }
97
98         z64 = zip_get_num_entries(zip, 0);
99         if (z64 < 0 || z64 > UINT_MAX) {
100                 syslog(LOG_ERR, "too many entries in %s", zipfile);
101                 goto error;
102         }
103         count = (unsigned int)z64;
104
105         /* records the files */
106         file_reset();
107         esize = 0;
108         for (index = 0 ; index < count ; index++) {
109                 err = zip_stat_index(zip, index, ZIP_FL_ENC_GUESS, &zstat);
110                 /* check the file name */
111                 if (zstat.name[0] == '/') {
112                         syslog(LOG_ERR, "absolute entry %s found in %s", zstat.name, zipfile);
113                         goto error;
114                 }
115                 len = strlen(zstat.name);
116                 if (len == 0) {
117                         syslog(LOG_ERR, "empty entry found in %s", zipfile);
118                         goto error;
119                 }
120                 if (zstat.size == 0) {
121                         /* directory name */
122                         if (zstat.name[len - 1] != '/') {
123                                 syslog(LOG_ERR, "bad directory name %s in %s", zstat.name, zipfile);
124                                 goto error;
125                         }
126                         /* record */
127                         fdesc = file_add_directory(zstat.name);
128                 } else {
129                         /* directory name */
130                         if (zstat.name[len - 1] == '/') {
131                                 syslog(LOG_ERR, "bad file name %s in %s", zstat.name, zipfile);
132                                 goto error;
133                         }
134                         /* get the size */
135                         esize += zstat.size;
136                         /* record */
137                         fdesc = file_add_file(zstat.name);
138                 }
139                 if (!fdesc)
140                         goto error;
141                 fdesc->zindex = index;
142         }
143
144         /* check the size */
145         if (maxsize && esize > maxsize) {
146                 syslog(LOG_ERR, "extracted size %zu greater than allowed size %llu", esize, maxsize);
147                 goto error;
148         }
149
150         /* unpack the recorded files */
151         assert(count == file_count());
152         for (index = 0 ; index < count ; index++) {
153                 fdesc = file_of_index(index);
154                 assert(fdesc != NULL);
155                 err = zip_stat_index(zip, fdesc->zindex, ZIP_FL_ENC_GUESS, &zstat);
156                 assert(zstat.name[0] != '/');
157                 if (zstat.size == 0) {
158                         /* directory name */
159                         err = create_directory((char*)zstat.name, MODE_OF_DIRECTORY_CREATION);
160                         if (err && errno != EEXIST)
161                                 goto error;
162                 } else {
163                         /* file name */
164                         zfile = zip_fopen_index(zip, fdesc->zindex, 0);
165                         if (!zfile) {
166                                 syslog(LOG_ERR, "Can't open %s in %s", zstat.name, zipfile);
167                                 goto error;
168                         }
169                         fd = create_file((char*)zstat.name, MODE_OF_FILE_CREATION, MODE_OF_DIRECTORY_CREATION);
170                         if (fd < 0)
171                                 goto errorz;
172                         /* extract */
173                         z64 = zstat.size;
174                         while (z64) {
175                                 sizr = zip_fread(zfile, buffer, sizeof buffer);
176                                 if (sizr < 0) {
177                                         syslog(LOG_ERR, "error while reading %s in %s", zstat.name, zipfile);
178                                         goto errorzf;
179                                 }
180                                 sizw = write(fd, buffer, sizr);
181                                 if (sizw < 0) {
182                                         syslog(LOG_ERR, "error while writing %s", zstat.name);
183                                         goto errorzf;
184                                 }
185                                 z64 -= sizw;
186                         }
187                         close(fd);
188                         zip_fclose(zfile);
189                 }
190         }
191
192         zip_close(zip);
193         return 0;
194
195 errorzf:
196         close(fd);
197 errorz:
198         zip_fclose(zfile);
199 error:
200         zip_close(zip);
201         return -1;
202 }
203
204 struct zws {
205         struct zip *zip;
206         char name[PATH_MAX];
207         char buffer[32768];
208 };
209
210 static int zwr(struct zws *zws, int offset)
211 {
212         int len, err;
213         DIR *dir;
214         struct dirent *ent;
215         zip_int64_t z64;
216         struct zip_source *zsrc;
217
218         if (offset == 0)
219                 dir = opendir(".");
220         else {
221                 dir = opendir(zws->name);
222                 zws->name[offset++] = '/';
223         }
224         if (!dir) {
225                 syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, zws->name);
226                 return -1;
227         }
228
229         ent = readdir(dir);
230         while (ent != NULL) {
231                 len = strlen(ent->d_name);
232                 if (ent->d_name[0] == '.' && (len == 1 || 
233                         (ent->d_name[1] == '.' && len == 2)))
234                         ;
235                 else if (offset + len >= sizeof(zws->name)) {
236                         closedir(dir);
237                         syslog(LOG_ERR, "name too long in zwr");
238                         errno = ENAMETOOLONG;
239                         return -1;
240                 } else {
241                         memcpy(zws->name + offset, ent->d_name, 1+len);
242                         switch (ent->d_type) {
243                         case DT_DIR:
244                                 z64 = zip_dir_add(zws->zip, zws->name, ZIP_FL_ENC_UTF_8);
245                                 if (z64 < 0) {
246                                         syslog(LOG_ERR, "zip_dir_add of %s failed", zws->name);
247                                         closedir(dir);
248                                         return -1;
249                                 }
250                                 err = zwr(zws, offset + len);
251                                 if (err) {
252                                         closedir(dir);
253                                         return -1;
254                                 }
255                                 break;
256                         case DT_REG:
257                                 zsrc = zip_source_file(zws->zip, zws->name, 0, 0);
258                                 if (zsrc == NULL) {
259                                         syslog(LOG_ERR, "zip_source_file of %s failed", zws->name);
260                                         closedir(dir);
261                                         return -1;
262                                 }
263                                 z64 = zip_file_add(zws->zip, zws->name, zsrc, ZIP_FL_ENC_UTF_8);
264                                 if (z64 < 0) {
265                                         syslog(LOG_ERR, "zip_file_add of %s failed", zws->name);
266                                         zip_source_free(zsrc);
267                                         closedir(dir);
268                                         return -1;
269                                 }
270                                 break;
271                         default:
272                                 break;
273                         }
274                 }
275                 ent = readdir(dir);
276         }
277
278         closedir(dir);
279         return 0;
280 }
281
282 /* write (pack) content of the current directory in 'zipfile' */
283 int zwrite(const char *zipfile)
284 {
285         int err;
286         struct zws zws;
287
288         zws.zip = zip_open(zipfile, ZIP_CREATE|ZIP_TRUNCATE, &err);
289         if (!zws.zip) {
290                 syslog(LOG_ERR, "Can't open %s for write", zipfile);
291                 return -1;
292         }
293
294         err = zwr(&zws, 0);
295         zip_close(zws.zip);
296         return err;
297 }
298
299
300 #if defined(TEST_READ)
301 int main(int ac, char **av)
302 {
303         for(av++ ; *av ; av++)
304                 zread(*av);
305         return 0;
306 }
307 #endif
308
309 #if defined(TEST_WRITE)
310 int main(int ac, char **av)
311 {
312         for(av++ ; *av ; av++)
313                 zwrite(*av);
314         return 0;
315 }
316 #endif
317