1 /*
2 zip_source_win32file.c -- create data source from HANDLE (Win32)
3 Copyright (c) 1999-2017 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
35 #include <wchar.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "zipint.h"
40 #include "zipwin32.h"
41
42 static zip_int64_t _win32_read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
43 static int _win32_create_temp_file(_zip_source_win32_read_file_t *ctx);
44 static int _zip_filetime_to_time_t(FILETIME ft, time_t *t);
45 static int _zip_seek_win32_u(void *h, zip_uint64_t offset, int whence, zip_error_t *error);
46 static int _zip_seek_win32(void *h, zip_int64_t offset, int whence, zip_error_t *error);
47 static int _zip_win32_error_to_errno(unsigned long win32err);
48 static int _zip_stat_win32(void *h, zip_stat_t *st, _zip_source_win32_read_file_t *ctx);
49
50 ZIP_EXTERN zip_source_t *
zip_source_win32handle(zip_t * za,HANDLE h,zip_uint64_t start,zip_int64_t len)51 zip_source_win32handle(zip_t *za, HANDLE h, zip_uint64_t start, zip_int64_t len)
52 {
53 if (za == NULL)
54 return NULL;
55
56 return zip_source_win32handle_create(h, start, len, &za->error);
57 }
58
59
60 ZIP_EXTERN zip_source_t *
zip_source_win32handle_create(HANDLE h,zip_uint64_t start,zip_int64_t length,zip_error_t * error)61 zip_source_win32handle_create(HANDLE h, zip_uint64_t start, zip_int64_t length, zip_error_t *error)
62 {
63 if (h == INVALID_HANDLE_VALUE || length < -1) {
64 zip_error_set(error, ZIP_ER_INVAL, 0);
65 return NULL;
66 }
67
68 return _zip_source_win32_handle_or_name(NULL, h, start, length, 1, NULL, NULL, error);
69 }
70
71
72 zip_source_t *
_zip_source_win32_handle_or_name(const void * fname,HANDLE h,zip_uint64_t start,zip_int64_t len,int closep,const zip_stat_t * st,_zip_source_win32_file_ops_t * ops,zip_error_t * error)73 _zip_source_win32_handle_or_name(const void *fname, HANDLE h, zip_uint64_t start, zip_int64_t len, int closep, const zip_stat_t *st, _zip_source_win32_file_ops_t *ops, zip_error_t *error)
74 {
75 _zip_source_win32_read_file_t *ctx;
76 zip_source_t *zs;
77
78 if (h == INVALID_HANDLE_VALUE && fname == NULL) {
79 zip_error_set(error, ZIP_ER_INVAL, 0);
80 return NULL;
81 }
82
83 if ((ctx = (_zip_source_win32_read_file_t *)malloc(sizeof(_zip_source_win32_read_file_t))) == NULL) {
84 zip_error_set(error, ZIP_ER_MEMORY, 0);
85 return NULL;
86 }
87
88 ctx->fname = NULL;
89 if (fname) {
90 if ((ctx->fname = ops->op_strdup(fname)) == NULL) {
91 zip_error_set(error, ZIP_ER_MEMORY, 0);
92 free(ctx);
93 return NULL;
94 }
95 }
96
97 ctx->ops = ops;
98 ctx->h = h;
99 ctx->start = start;
100 ctx->end = (len < 0 ? 0 : start + (zip_uint64_t)len);
101 ctx->closep = ctx->fname ? 1 : closep;
102 if (st) {
103 memcpy(&ctx->st, st, sizeof(ctx->st));
104 ctx->st.name = NULL;
105 ctx->st.valid &= ~ZIP_STAT_NAME;
106 }
107 else {
108 zip_stat_init(&ctx->st);
109 }
110
111 ctx->tmpname = NULL;
112 ctx->hout = INVALID_HANDLE_VALUE;
113
114 zip_error_init(&ctx->error);
115
116 ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
117 if (ctx->fname) {
118 HANDLE th;
119
120 th = ops->op_open(ctx);
121 if (th == INVALID_HANDLE_VALUE || GetFileType(th) == FILE_TYPE_DISK) {
122 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
123 }
124 if (th != INVALID_HANDLE_VALUE) {
125 CloseHandle(th);
126 }
127 }
128 else if (GetFileType(ctx->h) == FILE_TYPE_DISK) {
129 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
130 }
131
132 if ((zs = zip_source_function_create(_win32_read_file, ctx, error)) == NULL) {
133 free(ctx->fname);
134 free(ctx);
135 return NULL;
136 }
137
138 return zs;
139 }
140
141
142 static zip_int64_t
_win32_read_file(void * state,void * data,zip_uint64_t len,zip_source_cmd_t cmd)143 _win32_read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
144 {
145 _zip_source_win32_read_file_t *ctx;
146 char *buf;
147 zip_uint64_t n;
148 DWORD i;
149
150 ctx = (_zip_source_win32_read_file_t *)state;
151 buf = (char *)data;
152
153 switch (cmd) {
154 case ZIP_SOURCE_BEGIN_WRITE:
155 if (ctx->fname == NULL) {
156 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
157 return -1;
158 }
159 return _win32_create_temp_file(ctx);
160
161 case ZIP_SOURCE_COMMIT_WRITE: {
162 if (!CloseHandle(ctx->hout)) {
163 ctx->hout = INVALID_HANDLE_VALUE;
164 zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError()));
165 }
166 ctx->hout = INVALID_HANDLE_VALUE;
167 if (ctx->ops->op_rename_temp(ctx) < 0) {
168 zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError()));
169 return -1;
170 }
171 free(ctx->tmpname);
172 ctx->tmpname = NULL;
173 return 0;
174 }
175
176 case ZIP_SOURCE_CLOSE:
177 if (ctx->fname) {
178 CloseHandle(ctx->h);
179 ctx->h = INVALID_HANDLE_VALUE;
180 }
181 return 0;
182
183 case ZIP_SOURCE_ERROR:
184 return zip_error_to_data(&ctx->error, data, len);
185
186 case ZIP_SOURCE_FREE:
187 free(ctx->fname);
188 free(ctx->tmpname);
189 if (ctx->closep && ctx->h != INVALID_HANDLE_VALUE)
190 CloseHandle(ctx->h);
191 free(ctx);
192 return 0;
193
194 case ZIP_SOURCE_OPEN:
195 if (ctx->fname) {
196 if ((ctx->h = ctx->ops->op_open(ctx)) == INVALID_HANDLE_VALUE) {
197 zip_error_set(&ctx->error, ZIP_ER_OPEN, _zip_win32_error_to_errno(GetLastError()));
198 return -1;
199 }
200 }
201
202 if (ctx->closep && ctx->start > 0) {
203 if (_zip_seek_win32_u(ctx->h, ctx->start, SEEK_SET, &ctx->error) < 0) {
204 return -1;
205 }
206 }
207 ctx->current = ctx->start;
208 return 0;
209
210 case ZIP_SOURCE_READ:
211 if (ctx->end > 0) {
212 n = ctx->end - ctx->current;
213 if (n > len) {
214 n = len;
215 }
216 }
217 else {
218 n = len;
219 }
220
221 if (n > SIZE_MAX)
222 n = SIZE_MAX;
223
224 if (!ctx->closep) {
225 if (_zip_seek_win32_u(ctx->h, ctx->current, SEEK_SET, &ctx->error) < 0) {
226 return -1;
227 }
228 }
229
230 if (!ReadFile(ctx->h, buf, (DWORD)n, &i, NULL)) {
231 zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError()));
232 return -1;
233 }
234 ctx->current += i;
235
236 return (zip_int64_t)i;
237
238 case ZIP_SOURCE_REMOVE:
239 if (ctx->ops->op_remove(ctx->fname) < 0) {
240 zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_win32_error_to_errno(GetLastError()));
241 return -1;
242 }
243 return 0;
244
245 case ZIP_SOURCE_ROLLBACK_WRITE:
246 if (ctx->hout) {
247 CloseHandle(ctx->hout);
248 ctx->hout = INVALID_HANDLE_VALUE;
249 }
250 ctx->ops->op_remove(ctx->tmpname);
251 free(ctx->tmpname);
252 ctx->tmpname = NULL;
253 return 0;
254
255 case ZIP_SOURCE_SEEK: {
256 zip_int64_t new_current;
257 int need_seek;
258 zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
259
260 if (args == NULL)
261 return -1;
262
263 need_seek = ctx->closep;
264
265 switch (args->whence) {
266 case SEEK_SET:
267 new_current = args->offset;
268 break;
269
270 case SEEK_END:
271 if (ctx->end == 0) {
272 LARGE_INTEGER zero;
273 LARGE_INTEGER new_offset;
274
275 if (_zip_seek_win32(ctx->h, args->offset, SEEK_END, &ctx->error) < 0) {
276 return -1;
277 }
278 zero.QuadPart = 0;
279 if (!SetFilePointerEx(ctx->h, zero, &new_offset, FILE_CURRENT)) {
280 zip_error_set(&ctx->error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError()));
281 return -1;
282 }
283 new_current = new_offset.QuadPart;
284 need_seek = 0;
285 }
286 else {
287 new_current = (zip_int64_t)ctx->end + args->offset;
288 }
289 break;
290 case SEEK_CUR:
291 new_current = (zip_int64_t)ctx->current + args->offset;
292 break;
293
294 default:
295 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
296 return -1;
297 }
298
299 if (new_current < 0 || (zip_uint64_t)new_current < ctx->start || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end)) {
300 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
301 return -1;
302 }
303
304 ctx->current = (zip_uint64_t)new_current;
305
306 if (need_seek) {
307 if (_zip_seek_win32_u(ctx->h, ctx->current, SEEK_SET, &ctx->error) < 0) {
308 return -1;
309 }
310 }
311 return 0;
312 }
313
314 case ZIP_SOURCE_SEEK_WRITE: {
315 zip_source_args_seek_t *args;
316
317 args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
318 if (args == NULL) {
319 return -1;
320 }
321
322 if (_zip_seek_win32(ctx->hout, args->offset, args->whence, &ctx->error) < 0) {
323 return -1;
324 }
325 return 0;
326 }
327
328 case ZIP_SOURCE_STAT: {
329 if (len < sizeof(ctx->st))
330 return -1;
331
332 if (ctx->st.valid != 0)
333 memcpy(data, &ctx->st, sizeof(ctx->st));
334 else {
335 DWORD win32err;
336 zip_stat_t *st;
337 HANDLE h;
338 int success;
339
340 st = (zip_stat_t *)data;
341
342 if (ctx->h != INVALID_HANDLE_VALUE) {
343 h = ctx->h;
344 }
345 else {
346 h = ctx->ops->op_open(ctx);
347 if (h == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND) {
348 zip_error_set(&ctx->error, ZIP_ER_READ, ENOENT);
349 return -1;
350 }
351 }
352
353 success = _zip_stat_win32(h, st, ctx);
354 win32err = GetLastError();
355
356 /* We're done with the handle, so close it if we just opened it. */
357 if (h != ctx->h) {
358 CloseHandle(h);
359 }
360
361 if (success < 0) {
362 /* TODO: Is this the correct error to return in all cases? */
363 zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(win32err));
364 return -1;
365 }
366 }
367 return sizeof(ctx->st);
368 }
369
370 case ZIP_SOURCE_SUPPORTS:
371 return ctx->supports;
372
373 case ZIP_SOURCE_TELL:
374 return (zip_int64_t)ctx->current;
375
376 case ZIP_SOURCE_TELL_WRITE:
377 {
378 LARGE_INTEGER zero;
379 LARGE_INTEGER offset;
380
381 zero.QuadPart = 0;
382 if (!SetFilePointerEx(ctx->hout, zero, &offset, FILE_CURRENT)) {
383 zip_error_set(&ctx->error, ZIP_ER_TELL, _zip_win32_error_to_errno(GetLastError()));
384 return -1;
385 }
386
387 return offset.QuadPart;
388 }
389
390 case ZIP_SOURCE_WRITE:
391 {
392 DWORD ret;
393 if (!WriteFile(ctx->hout, data, (DWORD)len, &ret, NULL) || ret != len) {
394 zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError()));
395 return -1;
396 }
397
398 return (zip_int64_t)ret;
399 }
400
401 default:
402 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
403 return -1;
404 }
405 }
406
407
408 static int
_win32_create_temp_file(_zip_source_win32_read_file_t * ctx)409 _win32_create_temp_file(_zip_source_win32_read_file_t *ctx)
410 {
411 zip_uint32_t value;
412 /*
413 Windows has GetTempFileName(), but it closes the file after
414 creation, leaving it open to a horrible race condition. So
415 we reinvent the wheel.
416 */
417 int i;
418 HANDLE th = INVALID_HANDLE_VALUE;
419 void *temp = NULL;
420 SECURITY_INFORMATION si;
421 SECURITY_ATTRIBUTES sa;
422 PSECURITY_DESCRIPTOR psd = NULL;
423 PSECURITY_ATTRIBUTES psa = NULL;
424 DWORD len;
425 BOOL success;
426
427 /*
428 Read the DACL from the original file, so we can copy it to the temp file.
429 If there is no original file, or if we can't read the DACL, we'll use the
430 default security descriptor.
431 */
432 if (ctx->h != INVALID_HANDLE_VALUE && GetFileType(ctx->h) == FILE_TYPE_DISK) {
433 si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
434 len = 0;
435 success = GetUserObjectSecurity(ctx->h, &si, NULL, len, &len);
436 if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
437 if ((psd = (PSECURITY_DESCRIPTOR)malloc(len)) == NULL) {
438 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
439 return -1;
440 }
441 success = GetUserObjectSecurity(ctx->h, &si, psd, len, &len);
442 }
443 if (success) {
444 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
445 sa.bInheritHandle = FALSE;
446 sa.lpSecurityDescriptor = psd;
447 psa = &sa;
448 }
449 }
450
451 value = GetTickCount();
452 for (i = 0; i < 1024 && th == INVALID_HANDLE_VALUE; i++) {
453 th = ctx->ops->op_create_temp(ctx, &temp, value + i, psa);
454 if (th == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_EXISTS)
455 break;
456 }
457
458 if (th == INVALID_HANDLE_VALUE) {
459 free(temp);
460 free(psd);
461 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_win32_error_to_errno(GetLastError()));
462 return -1;
463 }
464
465 free(psd);
466 ctx->hout = th;
467 ctx->tmpname = temp;
468
469 return 0;
470 }
471
472
473 static int
_zip_seek_win32_u(HANDLE h,zip_uint64_t offset,int whence,zip_error_t * error)474 _zip_seek_win32_u(HANDLE h, zip_uint64_t offset, int whence, zip_error_t *error)
475 {
476 if (offset > ZIP_INT64_MAX) {
477 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
478 return -1;
479 }
480 return _zip_seek_win32(h, (zip_int64_t)offset, whence, error);
481 }
482
483
484 static int
_zip_seek_win32(HANDLE h,zip_int64_t offset,int whence,zip_error_t * error)485 _zip_seek_win32(HANDLE h, zip_int64_t offset, int whence, zip_error_t *error)
486 {
487 LARGE_INTEGER li;
488 DWORD method;
489
490 switch (whence) {
491 case SEEK_SET:
492 method = FILE_BEGIN;
493 break;
494 case SEEK_END:
495 method = FILE_END;
496 break;
497 case SEEK_CUR:
498 method = FILE_CURRENT;
499 break;
500 default:
501 zip_error_set(error, ZIP_ER_SEEK, EINVAL);
502 return -1;
503 }
504
505 li.QuadPart = (LONGLONG)offset;
506 if (!SetFilePointerEx(h, li, NULL, method)) {
507 zip_error_set(error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError()));
508 return -1;
509 }
510
511 return 0;
512 }
513
514
515 static int
_zip_win32_error_to_errno(DWORD win32err)516 _zip_win32_error_to_errno(DWORD win32err)
517 {
518 /*
519 Note: This list isn't exhaustive, but should cover common cases.
520 */
521 switch (win32err) {
522 case ERROR_INVALID_PARAMETER:
523 return EINVAL;
524 case ERROR_FILE_NOT_FOUND:
525 return ENOENT;
526 case ERROR_INVALID_HANDLE:
527 return EBADF;
528 case ERROR_ACCESS_DENIED:
529 return EACCES;
530 case ERROR_FILE_EXISTS:
531 return EEXIST;
532 case ERROR_TOO_MANY_OPEN_FILES:
533 return EMFILE;
534 case ERROR_DISK_FULL:
535 return ENOSPC;
536 default:
537 return 0;
538 }
539 }
540
541
542 static int
_zip_stat_win32(HANDLE h,zip_stat_t * st,_zip_source_win32_read_file_t * ctx)543 _zip_stat_win32(HANDLE h, zip_stat_t *st, _zip_source_win32_read_file_t *ctx)
544 {
545 FILETIME mtimeft;
546 time_t mtime;
547 LARGE_INTEGER size;
548 int regularp;
549
550 if (!GetFileTime(h, NULL, NULL, &mtimeft)) {
551 zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError()));
552 return -1;
553 }
554 if (_zip_filetime_to_time_t(mtimeft, &mtime) < 0) {
555 zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE);
556 return -1;
557 }
558
559 regularp = 0;
560 if (GetFileType(h) == FILE_TYPE_DISK) {
561 regularp = 1;
562 }
563
564 if (!GetFileSizeEx(h, &size)) {
565 zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError()));
566 return -1;
567 }
568
569 zip_stat_init(st);
570 st->mtime = mtime;
571 st->valid |= ZIP_STAT_MTIME;
572 if (ctx->end != 0) {
573 st->size = ctx->end - ctx->start;
574 st->valid |= ZIP_STAT_SIZE;
575 }
576 else if (regularp) {
577 st->size = (zip_uint64_t)size.QuadPart;
578 st->valid |= ZIP_STAT_SIZE;
579 }
580
581 return 0;
582 }
583
584
585 static int
_zip_filetime_to_time_t(FILETIME ft,time_t * t)586 _zip_filetime_to_time_t(FILETIME ft, time_t *t)
587 {
588 /*
589 Inspired by http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux
590 */
591 const zip_int64_t WINDOWS_TICK = 10000000LL;
592 const zip_int64_t SEC_TO_UNIX_EPOCH = 11644473600LL;
593 ULARGE_INTEGER li;
594 zip_int64_t secs;
595 time_t temp;
596
597 li.LowPart = ft.dwLowDateTime;
598 li.HighPart = ft.dwHighDateTime;
599 secs = (li.QuadPart / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
600
601 temp = (time_t)secs;
602 if (secs != (zip_int64_t)temp)
603 return -1;
604
605 *t = temp;
606 return 0;
607 }
608