Mercurial > embed
comparison libzip/lib/zip_source_filep.c @ 20:3b18afe43c9d
libzip: reimport version 1.1.3
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 29 Jun 2016 09:24:55 +0200 |
parents | 2306f4b04790 |
children | 056ee6b5913e |
comparison
equal
deleted
inserted
replaced
19:07f2cdc6e430 | 20:3b18afe43c9d |
---|---|
1 /* | |
2 zip_source_filep.c -- create data source from FILE * | |
3 Copyright (C) 1999-2015 Dieter Baron and Thomas Klausner | |
4 | |
5 This file is part of libzip, a library to manipulate ZIP archives. | |
6 The authors can be contacted at <libzip@nih.at> | |
7 | |
8 Redistribution and use in source and binary forms, with or without | |
9 modification, are permitted provided that the following conditions | |
10 are met: | |
11 1. Redistributions of source code must retain the above copyright | |
12 notice, this list of conditions and the following disclaimer. | |
13 2. Redistributions in binary form must reproduce the above copyright | |
14 notice, this list of conditions and the following disclaimer in | |
15 the documentation and/or other materials provided with the | |
16 distribution. | |
17 3. The names of the authors may not be used to endorse or promote | |
18 products derived from this software without specific prior | |
19 written permission. | |
20 | |
21 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS | |
22 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY | |
25 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | |
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | |
29 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN | |
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 */ | |
33 | |
34 #include <sys/stat.h> | |
35 #include <stdio.h> | |
36 #include <stdlib.h> | |
37 #include <string.h> | |
38 | |
39 #include "zipint.h" | |
40 | |
41 #ifdef HAVE_UNISTD_H | |
42 #include <unistd.h> | |
43 #endif | |
44 | |
45 #ifdef _WIN32 | |
46 /* WIN32 needs <fcntl.h> for _O_BINARY */ | |
47 #include <fcntl.h> | |
48 #endif | |
49 | |
50 /* Windows sys/types.h does not provide these */ | |
51 #ifndef S_ISREG | |
52 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) | |
53 #endif | |
54 #if defined(S_IXUSR) && defined(S_IRWXG) && defined(S_IRWXO) | |
55 #define _SAFE_MASK (S_IXUSR | S_IRWXG | S_IRWXO) | |
56 #elif defined(_S_IWRITE) | |
57 #define _SAFE_MASK (_S_IWRITE) | |
58 #else | |
59 #error do not know safe values for umask, please report this | |
60 #endif | |
61 | |
62 #ifdef _MSC_VER | |
63 /* MSVC doesn't have mode_t */ | |
64 typedef int mode_t; | |
65 #endif | |
66 | |
67 struct read_file { | |
68 zip_error_t error; /* last error information */ | |
69 zip_int64_t supports; | |
70 | |
71 /* reading */ | |
72 char *fname; /* name of file to read from */ | |
73 FILE *f; /* file to read from */ | |
74 struct zip_stat st; /* stat information passed in */ | |
75 zip_uint64_t start; /* start offset of data to read */ | |
76 zip_uint64_t end; /* end offset of data to read, 0 for up to EOF */ | |
77 zip_uint64_t current; /* current offset */ | |
78 | |
79 /* writing */ | |
80 char *tmpname; | |
81 FILE *fout; | |
82 }; | |
83 | |
84 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd); | |
85 static int create_temp_output(struct read_file *ctx); | |
86 static int _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error); | |
87 static int _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error); | |
88 | |
89 | |
90 ZIP_EXTERN zip_source_t * | |
91 zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len) | |
92 { | |
93 if (za == NULL) | |
94 return NULL; | |
95 | |
96 return zip_source_filep_create(file, start, len, &za->error); | |
97 } | |
98 | |
99 | |
100 ZIP_EXTERN zip_source_t * | |
101 zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error) | |
102 { | |
103 if (file == NULL || length < -1) { | |
104 zip_error_set(error, ZIP_ER_INVAL, 0); | |
105 return NULL; | |
106 } | |
107 | |
108 return _zip_source_file_or_p(NULL, file, start, length, NULL, error); | |
109 } | |
110 | |
111 | |
112 zip_source_t * | |
113 _zip_source_file_or_p(const char *fname, FILE *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_error_t *error) | |
114 { | |
115 struct read_file *ctx; | |
116 zip_source_t *zs; | |
117 | |
118 if (file == NULL && fname == NULL) { | |
119 zip_error_set(error, ZIP_ER_INVAL, 0); | |
120 return NULL; | |
121 } | |
122 | |
123 if ((ctx=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) { | |
124 zip_error_set(error, ZIP_ER_MEMORY, 0); | |
125 return NULL; | |
126 } | |
127 | |
128 ctx->fname = NULL; | |
129 if (fname) { | |
130 if ((ctx->fname=strdup(fname)) == NULL) { | |
131 zip_error_set(error, ZIP_ER_MEMORY, 0); | |
132 free(ctx); | |
133 return NULL; | |
134 } | |
135 } | |
136 ctx->f = file; | |
137 ctx->start = start; | |
138 ctx->end = (len < 0 ? 0 : start+(zip_uint64_t)len); | |
139 if (st) { | |
140 memcpy(&ctx->st, st, sizeof(ctx->st)); | |
141 ctx->st.name = NULL; | |
142 ctx->st.valid &= ~ZIP_STAT_NAME; | |
143 } | |
144 else { | |
145 zip_stat_init(&ctx->st); | |
146 } | |
147 | |
148 ctx->tmpname = NULL; | |
149 ctx->fout = NULL; | |
150 | |
151 zip_error_init(&ctx->error); | |
152 | |
153 ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1); | |
154 if (ctx->fname) { | |
155 struct stat sb; | |
156 | |
157 if (stat(ctx->fname, &sb) < 0 || S_ISREG(sb.st_mode)) { | |
158 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; | |
159 } | |
160 } | |
161 else if (fseeko(ctx->f, 0, SEEK_CUR) == 0) { | |
162 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE; | |
163 } | |
164 | |
165 if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) { | |
166 free(ctx->fname); | |
167 free(ctx); | |
168 return NULL; | |
169 } | |
170 | |
171 return zs; | |
172 } | |
173 | |
174 | |
175 static int | |
176 create_temp_output(struct read_file *ctx) | |
177 { | |
178 char *temp; | |
179 int tfd; | |
180 mode_t mask; | |
181 FILE *tfp; | |
182 | |
183 if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) { | |
184 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); | |
185 return -1; | |
186 } | |
187 sprintf(temp, "%s.XXXXXX", ctx->fname); | |
188 | |
189 mask = umask(_SAFE_MASK); | |
190 if ((tfd=mkstemp(temp)) == -1) { | |
191 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); | |
192 umask(mask); | |
193 free(temp); | |
194 return -1; | |
195 } | |
196 umask(mask); | |
197 | |
198 if ((tfp=fdopen(tfd, "r+b")) == NULL) { | |
199 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); | |
200 close(tfd); | |
201 (void)remove(temp); | |
202 free(temp); | |
203 return -1; | |
204 } | |
205 | |
206 #ifdef _WIN32 | |
207 /* | |
208 According to Pierre Joye, Windows in some environments per | |
209 default creates text files, so force binary mode. | |
210 */ | |
211 _setmode(_fileno(tfp), _O_BINARY ); | |
212 #endif | |
213 | |
214 ctx->fout = tfp; | |
215 ctx->tmpname = temp; | |
216 | |
217 return 0; | |
218 } | |
219 | |
220 | |
221 static zip_int64_t | |
222 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) | |
223 { | |
224 struct read_file *ctx; | |
225 char *buf; | |
226 zip_uint64_t n; | |
227 size_t i; | |
228 | |
229 ctx = (struct read_file *)state; | |
230 buf = (char *)data; | |
231 | |
232 switch (cmd) { | |
233 case ZIP_SOURCE_BEGIN_WRITE: | |
234 if (ctx->fname == NULL) { | |
235 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); | |
236 return -1; | |
237 } | |
238 return create_temp_output(ctx); | |
239 | |
240 case ZIP_SOURCE_COMMIT_WRITE: { | |
241 mode_t mask; | |
242 | |
243 if (fclose(ctx->fout) < 0) { | |
244 ctx->fout = NULL; | |
245 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); | |
246 } | |
247 ctx->fout = NULL; | |
248 if (rename(ctx->tmpname, ctx->fname) < 0) { | |
249 zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); | |
250 return -1; | |
251 } | |
252 mask = umask(022); | |
253 umask(mask); | |
254 /* not much we can do if chmod fails except make the whole commit fail */ | |
255 (void)chmod(ctx->fname, 0666&~mask); | |
256 free(ctx->tmpname); | |
257 ctx->tmpname = NULL; | |
258 return 0; | |
259 } | |
260 | |
261 case ZIP_SOURCE_CLOSE: | |
262 if (ctx->fname) { | |
263 fclose(ctx->f); | |
264 ctx->f = NULL; | |
265 } | |
266 return 0; | |
267 | |
268 case ZIP_SOURCE_ERROR: | |
269 return zip_error_to_data(&ctx->error, data, len); | |
270 | |
271 case ZIP_SOURCE_FREE: | |
272 free(ctx->fname); | |
273 free(ctx->tmpname); | |
274 if (ctx->f) | |
275 fclose(ctx->f); | |
276 free(ctx); | |
277 return 0; | |
278 | |
279 case ZIP_SOURCE_OPEN: | |
280 if (ctx->fname) { | |
281 if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) { | |
282 zip_error_set(&ctx->error, ZIP_ER_OPEN, errno); | |
283 return -1; | |
284 } | |
285 } | |
286 | |
287 if (ctx->start > 0) { | |
288 if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) { | |
289 return -1; | |
290 } | |
291 } | |
292 ctx->current = ctx->start; | |
293 return 0; | |
294 | |
295 case ZIP_SOURCE_READ: | |
296 if (ctx->end > 0) { | |
297 n = ctx->end-ctx->current; | |
298 if (n > len) { | |
299 n = len; | |
300 } | |
301 } | |
302 else { | |
303 n = len; | |
304 } | |
305 | |
306 if (n > SIZE_MAX) | |
307 n = SIZE_MAX; | |
308 | |
309 if ((i=fread(buf, 1, (size_t)n, ctx->f)) == 0) { | |
310 if (ferror(ctx->f)) { | |
311 zip_error_set(&ctx->error, ZIP_ER_READ, errno); | |
312 return -1; | |
313 } | |
314 } | |
315 ctx->current += i; | |
316 | |
317 return (zip_int64_t)i; | |
318 | |
319 case ZIP_SOURCE_REMOVE: | |
320 if (remove(ctx->fname) < 0) { | |
321 zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno); | |
322 return -1; | |
323 } | |
324 return 0; | |
325 | |
326 case ZIP_SOURCE_ROLLBACK_WRITE: | |
327 if (ctx->fout) { | |
328 fclose(ctx->fout); | |
329 ctx->fout = NULL; | |
330 } | |
331 (void)remove(ctx->tmpname); | |
332 free(ctx->tmpname); | |
333 ctx->tmpname = NULL; | |
334 return 0; | |
335 | |
336 case ZIP_SOURCE_SEEK: { | |
337 zip_int64_t new_current; | |
338 int need_seek; | |
339 zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); | |
340 | |
341 if (args == NULL) | |
342 return -1; | |
343 | |
344 need_seek = 1; | |
345 | |
346 switch (args->whence) { | |
347 case SEEK_SET: | |
348 new_current = args->offset; | |
349 break; | |
350 | |
351 case SEEK_END: | |
352 if (ctx->end == 0) { | |
353 if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) { | |
354 return -1; | |
355 } | |
356 if ((new_current = ftello(ctx->f)) < 0) { | |
357 zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); | |
358 return -1; | |
359 } | |
360 need_seek = 0; | |
361 } | |
362 else { | |
363 new_current = (zip_int64_t)ctx->end + args->offset; | |
364 } | |
365 break; | |
366 case SEEK_CUR: | |
367 new_current = (zip_int64_t)ctx->current + args->offset; | |
368 break; | |
369 | |
370 default: | |
371 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); | |
372 return -1; | |
373 } | |
374 | |
375 if (new_current < 0 || (zip_uint64_t)new_current < ctx->start || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end)) { | |
376 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); | |
377 return -1; | |
378 } | |
379 | |
380 ctx->current = (zip_uint64_t)new_current; | |
381 | |
382 if (need_seek) { | |
383 if (_zip_fseek_u(ctx->f, ctx->current, SEEK_SET, &ctx->error) < 0) { | |
384 return -1; | |
385 } | |
386 } | |
387 return 0; | |
388 } | |
389 | |
390 case ZIP_SOURCE_SEEK_WRITE: { | |
391 zip_source_args_seek_t *args; | |
392 | |
393 args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); | |
394 if (args == NULL) { | |
395 return -1; | |
396 } | |
397 | |
398 if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) { | |
399 return -1; | |
400 } | |
401 return 0; | |
402 } | |
403 | |
404 case ZIP_SOURCE_STAT: { | |
405 if (len < sizeof(ctx->st)) | |
406 return -1; | |
407 | |
408 if (ctx->st.valid != 0) | |
409 memcpy(data, &ctx->st, sizeof(ctx->st)); | |
410 else { | |
411 zip_stat_t *st; | |
412 struct stat fst; | |
413 int err; | |
414 | |
415 if (ctx->f) | |
416 err = fstat(fileno(ctx->f), &fst); | |
417 else | |
418 err = stat(ctx->fname, &fst); | |
419 | |
420 if (err != 0) { | |
421 zip_error_set(&ctx->error, ZIP_ER_READ, errno); | |
422 return -1; | |
423 } | |
424 | |
425 st = (zip_stat_t *)data; | |
426 | |
427 zip_stat_init(st); | |
428 st->mtime = fst.st_mtime; | |
429 st->valid |= ZIP_STAT_MTIME; | |
430 if (ctx->end != 0) { | |
431 st->size = ctx->end - ctx->start; | |
432 st->valid |= ZIP_STAT_SIZE; | |
433 } | |
434 else if ((fst.st_mode&S_IFMT) == S_IFREG) { | |
435 st->size = (zip_uint64_t)fst.st_size; | |
436 st->valid |= ZIP_STAT_SIZE; | |
437 } | |
438 } | |
439 return sizeof(ctx->st); | |
440 } | |
441 | |
442 case ZIP_SOURCE_SUPPORTS: | |
443 return ctx->supports; | |
444 | |
445 case ZIP_SOURCE_TELL: | |
446 return (zip_int64_t)ctx->current; | |
447 | |
448 case ZIP_SOURCE_TELL_WRITE: | |
449 { | |
450 off_t ret = ftello(ctx->fout); | |
451 | |
452 if (ret < 0) { | |
453 zip_error_set(&ctx->error, ZIP_ER_TELL, errno); | |
454 return -1; | |
455 } | |
456 return ret; | |
457 } | |
458 | |
459 case ZIP_SOURCE_WRITE: | |
460 { | |
461 size_t ret; | |
462 | |
463 clearerr(ctx->fout); | |
464 ret = fwrite(data, 1, len, ctx->fout); | |
465 if (ret != len || ferror(ctx->fout)) { | |
466 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); | |
467 return -1; | |
468 } | |
469 | |
470 return (zip_int64_t)ret; | |
471 } | |
472 | |
473 default: | |
474 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); | |
475 return -1; | |
476 } | |
477 } | |
478 | |
479 | |
480 static int | |
481 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error) | |
482 { | |
483 if (offset > ZIP_INT64_MAX) { | |
484 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW); | |
485 return -1; | |
486 } | |
487 return _zip_fseek(f, (zip_int64_t)offset, whence, error); | |
488 } | |
489 | |
490 | |
491 static int | |
492 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error) | |
493 { | |
494 if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) { | |
495 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW); | |
496 return -1; | |
497 } | |
498 if (fseeko(f, (off_t)offset, whence) < 0) { | |
499 zip_error_set(error, ZIP_ER_SEEK, errno); | |
500 return -1; | |
501 } | |
502 return 0; | |
503 } |