Imported Upstream version 1.3.2
[platform/upstream/libzip.git] / lib / zip_source_win32handle.c
1 /*
2 zip_source_win32file.c -- create data source from HANDLE (Win32)
3 Copyright (C) 1999-2016 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 *
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 *
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 *
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
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) {
348                     win32err = GetLastError();
349                     if (win32err == ERROR_FILE_NOT_FOUND || win32err == ERROR_PATH_NOT_FOUND) {
350                         zip_error_set(&ctx->error, ZIP_ER_READ, ENOENT);
351                         return -1;
352                     }
353                 }
354             }
355
356             success = _zip_stat_win32(h, st, ctx);
357             win32err = GetLastError();
358
359             /* We're done with the handle, so close it if we just opened it. */
360             if (h != ctx->h) {
361                 CloseHandle(h);
362             }
363
364             if (success < 0) {
365                 /* TODO: Is this the correct error to return in all cases? */
366                 zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(win32err));
367                 return -1;
368             }
369         }
370         return sizeof(ctx->st);
371     }
372
373     case ZIP_SOURCE_SUPPORTS:
374         return ctx->supports;
375
376     case ZIP_SOURCE_TELL:
377         return (zip_int64_t)ctx->current;
378
379     case ZIP_SOURCE_TELL_WRITE:
380     {
381         LARGE_INTEGER zero;
382         LARGE_INTEGER offset;
383
384         zero.QuadPart = 0;
385         if (!SetFilePointerEx(ctx->hout, zero, &offset, FILE_CURRENT)) {
386             zip_error_set(&ctx->error, ZIP_ER_TELL, _zip_win32_error_to_errno(GetLastError()));
387             return -1;
388         }
389
390         return offset.QuadPart;
391     }
392
393     case ZIP_SOURCE_WRITE:
394     {
395         DWORD ret;
396         if (!WriteFile(ctx->hout, data, (DWORD)len, &ret, NULL) || ret != len) {
397             zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError()));
398             return -1;
399         }
400
401         return (zip_int64_t)ret;
402     }
403
404     default:
405         zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
406         return -1;
407     }
408 }
409
410
411 static int
412 _win32_create_temp_file(_zip_source_win32_read_file_t *ctx)
413 {
414     zip_uint32_t value;
415     /*
416     Windows has GetTempFileName(), but it closes the file after
417     creation, leaving it open to a horrible race condition. So
418     we reinvent the wheel.
419     */
420     int i;
421     HANDLE th = INVALID_HANDLE_VALUE;
422     void *temp = NULL;
423     SECURITY_INFORMATION si;
424     SECURITY_ATTRIBUTES sa;
425     PSECURITY_DESCRIPTOR psd = NULL;
426     PSECURITY_ATTRIBUTES psa = NULL;
427     DWORD len;
428     BOOL success;
429
430     /*
431     Read the DACL from the original file, so we can copy it to the temp file.
432     If there is no original file, or if we can't read the DACL, we'll use the
433     default security descriptor.
434     */
435     if (ctx->h != INVALID_HANDLE_VALUE && GetFileType(ctx->h) == FILE_TYPE_DISK) {
436         si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
437         len = 0;
438         success = GetUserObjectSecurity(ctx->h, &si, NULL, len, &len);
439         if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
440             if ((psd = (PSECURITY_DESCRIPTOR)malloc(len)) == NULL) {
441                 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
442                 return -1;
443             }
444             success = GetUserObjectSecurity(ctx->h, &si, psd, len, &len);
445         }
446         if (success) {
447             sa.nLength = sizeof(SECURITY_ATTRIBUTES);
448             sa.bInheritHandle = FALSE;
449             sa.lpSecurityDescriptor = psd;
450             psa = &sa;
451         }
452     }
453
454     value = GetTickCount();
455     for (i = 0; i < 1024 && th == INVALID_HANDLE_VALUE; i++) {
456         th = ctx->ops->op_create_temp(ctx, &temp, value + i, psa);
457         if (th == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_EXISTS)
458             break;
459     }
460
461     if (th == INVALID_HANDLE_VALUE) {
462         free(temp);
463         free(psd);
464         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_win32_error_to_errno(GetLastError()));
465         return -1;
466     }
467
468     free(psd);
469     ctx->hout = th;
470     ctx->tmpname = temp;
471
472     return 0;
473 }
474
475
476 static int
477 _zip_seek_win32_u(HANDLE h, zip_uint64_t offset, int whence, zip_error_t *error)
478 {
479     if (offset > ZIP_INT64_MAX) {
480         zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
481         return -1;
482     }
483     return _zip_seek_win32(h, (zip_int64_t)offset, whence, error);
484 }
485
486
487 static int
488 _zip_seek_win32(HANDLE h, zip_int64_t offset, int whence, zip_error_t *error)
489 {
490     LARGE_INTEGER li;
491     DWORD method;
492
493     switch (whence) {
494     case SEEK_SET:
495         method = FILE_BEGIN;
496         break;
497     case SEEK_END:
498         method = FILE_END;
499         break;
500     case SEEK_CUR:
501         method = FILE_CURRENT;
502         break;
503     default:
504         zip_error_set(error, ZIP_ER_SEEK, EINVAL);
505         return -1;
506     }
507
508     li.QuadPart = (LONGLONG)offset;
509     if (!SetFilePointerEx(h, li, NULL, method)) {
510         zip_error_set(error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError()));
511         return -1;
512     }
513
514     return 0;
515 }
516
517
518 static int
519 _zip_win32_error_to_errno(DWORD win32err)
520 {
521     /*
522     Note: This list isn't exhaustive, but should cover common cases.
523     */
524     switch (win32err) {
525     case ERROR_INVALID_PARAMETER:
526         return EINVAL;
527     case ERROR_FILE_NOT_FOUND:
528         return ENOENT;
529     case ERROR_INVALID_HANDLE:
530         return EBADF;
531     case ERROR_ACCESS_DENIED:
532         return EACCES;
533     case ERROR_FILE_EXISTS:
534         return EEXIST;
535     case ERROR_TOO_MANY_OPEN_FILES:
536         return EMFILE;
537     case ERROR_DISK_FULL:
538         return ENOSPC;
539     default:
540         return 0;
541     }
542 }
543
544
545 static int
546 _zip_stat_win32(HANDLE h, zip_stat_t *st, _zip_source_win32_read_file_t *ctx)
547 {
548     FILETIME mtimeft;
549     time_t mtime;
550     LARGE_INTEGER size;
551     int regularp;
552
553     if (!GetFileTime(h, NULL, NULL, &mtimeft)) {
554         zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError()));
555         return -1;
556     }
557     if (_zip_filetime_to_time_t(mtimeft, &mtime) < 0) {
558         zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE);
559         return -1;
560     }
561
562     regularp = 0;
563     if (GetFileType(h) == FILE_TYPE_DISK) {
564         regularp = 1;
565     }
566
567     if (!GetFileSizeEx(h, &size)) {
568         zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError()));
569         return -1;
570     }
571
572     zip_stat_init(st);
573     st->mtime = mtime;
574     st->valid |= ZIP_STAT_MTIME;
575     if (ctx->end != 0) {
576         st->size = ctx->end - ctx->start;
577         st->valid |= ZIP_STAT_SIZE;
578     }
579     else if (regularp) {
580         st->size = (zip_uint64_t)size.QuadPart;
581         st->valid |= ZIP_STAT_SIZE;
582     }
583
584     return 0;
585 }
586
587
588 static int
589 _zip_filetime_to_time_t(FILETIME ft, time_t *t)
590 {
591     /*
592     Inspired by http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux
593     */
594     const zip_int64_t WINDOWS_TICK = 10000000LL;
595     const zip_int64_t SEC_TO_UNIX_EPOCH = 11644473600LL;
596     ULARGE_INTEGER li;
597     zip_int64_t secs;
598     time_t temp;
599
600     li.LowPart = ft.dwLowDateTime;
601     li.HighPart = ft.dwHighDateTime;
602     secs = (li.QuadPart / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
603
604     temp = (time_t)secs;
605     if (secs != (zip_int64_t)temp)
606         return -1;
607
608     *t = temp;
609     return 0;
610 }