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 *
zip_source_filep(zip_t * za,FILE * file,zip_uint64_t start,zip_int64_t len)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 *
zip_source_filep_create(FILE * file,zip_uint64_t start,zip_int64_t length,zip_error_t * error)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 *
_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)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
create_temp_output(struct read_file * ctx)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
read_file(void * state,void * data,zip_uint64_t len,zip_source_cmd_t cmd)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
_zip_fseek_u(FILE * f,zip_uint64_t offset,int whence,zip_error_t * error)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
_zip_fseek(FILE * f,zip_int64_t offset,int whence,zip_error_t * error)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 }
504