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