2 zip_source_win32file.c -- create data source from HANDLE (Win32)
3 Copyright (C) 1999-2016 Dieter Baron and Thomas Klausner
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <libzip@nih.at>
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
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
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
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.
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);
50 ZIP_EXTERN zip_source_t *
51 zip_source_win32handle(zip_t *za, HANDLE h, zip_uint64_t start, zip_int64_t len)
56 return zip_source_win32handle_create(h, start, len, &za->error);
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)
63 if (h == INVALID_HANDLE_VALUE || length < -1) {
64 zip_error_set(error, ZIP_ER_INVAL, 0);
68 return _zip_source_win32_handle_or_name(NULL, h, start, length, 1, NULL, NULL, 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)
75 _zip_source_win32_read_file_t *ctx;
78 if (h == INVALID_HANDLE_VALUE && fname == NULL) {
79 zip_error_set(error, ZIP_ER_INVAL, 0);
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);
90 if ((ctx->fname = ops->op_strdup(fname)) == NULL) {
91 zip_error_set(error, ZIP_ER_MEMORY, 0);
100 ctx->end = (len < 0 ? 0 : start + (zip_uint64_t)len);
101 ctx->closep = ctx->fname ? 1 : closep;
103 memcpy(&ctx->st, st, sizeof(ctx->st));
105 ctx->st.valid &= ~ZIP_STAT_NAME;
108 zip_stat_init(&ctx->st);
112 ctx->hout = INVALID_HANDLE_VALUE;
114 zip_error_init(&ctx->error);
116 ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
120 th = ops->op_open(ctx);
121 if (th == INVALID_HANDLE_VALUE || GetFileType(th) == FILE_TYPE_DISK) {
122 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
124 if (th != INVALID_HANDLE_VALUE) {
128 else if (GetFileType(ctx->h) == FILE_TYPE_DISK) {
129 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
132 if ((zs = zip_source_function_create(_win32_read_file, ctx, error)) == NULL) {
143 _win32_read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
145 _zip_source_win32_read_file_t *ctx;
150 ctx = (_zip_source_win32_read_file_t *)state;
154 case ZIP_SOURCE_BEGIN_WRITE:
155 if (ctx->fname == NULL) {
156 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
159 return _win32_create_temp_file(ctx);
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()));
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()));
176 case ZIP_SOURCE_CLOSE:
179 ctx->h = INVALID_HANDLE_VALUE;
183 case ZIP_SOURCE_ERROR:
184 return zip_error_to_data(&ctx->error, data, len);
186 case ZIP_SOURCE_FREE:
189 if (ctx->closep && ctx->h != INVALID_HANDLE_VALUE)
194 case ZIP_SOURCE_OPEN:
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()));
202 if (ctx->closep && ctx->start > 0) {
203 if (_zip_seek_win32_u(ctx->h, ctx->start, SEEK_SET, &ctx->error) < 0) {
207 ctx->current = ctx->start;
210 case ZIP_SOURCE_READ:
212 n = ctx->end - ctx->current;
225 if (_zip_seek_win32_u(ctx->h, ctx->current, SEEK_SET, &ctx->error) < 0) {
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()));
236 return (zip_int64_t)i;
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()));
245 case ZIP_SOURCE_ROLLBACK_WRITE:
247 CloseHandle(ctx->hout);
248 ctx->hout = INVALID_HANDLE_VALUE;
250 ctx->ops->op_remove(ctx->tmpname);
255 case ZIP_SOURCE_SEEK: {
256 zip_int64_t new_current;
258 zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
263 need_seek = ctx->closep;
265 switch (args->whence) {
267 new_current = args->offset;
273 LARGE_INTEGER new_offset;
275 if (_zip_seek_win32(ctx->h, args->offset, SEEK_END, &ctx->error) < 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()));
283 new_current = new_offset.QuadPart;
287 new_current = (zip_int64_t)ctx->end + args->offset;
291 new_current = (zip_int64_t)ctx->current + args->offset;
295 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
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);
304 ctx->current = (zip_uint64_t)new_current;
307 if (_zip_seek_win32_u(ctx->h, ctx->current, SEEK_SET, &ctx->error) < 0) {
314 case ZIP_SOURCE_SEEK_WRITE: {
315 zip_source_args_seek_t *args;
317 args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
322 if (_zip_seek_win32(ctx->hout, args->offset, args->whence, &ctx->error) < 0) {
328 case ZIP_SOURCE_STAT: {
329 if (len < sizeof(ctx->st))
332 if (ctx->st.valid != 0)
333 memcpy(data, &ctx->st, sizeof(ctx->st));
340 st = (zip_stat_t *)data;
342 if (ctx->h != INVALID_HANDLE_VALUE) {
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);
356 success = _zip_stat_win32(h, st, ctx);
357 win32err = GetLastError();
359 /* We're done with the handle, so close it if we just opened it. */
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));
370 return sizeof(ctx->st);
373 case ZIP_SOURCE_SUPPORTS:
374 return ctx->supports;
376 case ZIP_SOURCE_TELL:
377 return (zip_int64_t)ctx->current;
379 case ZIP_SOURCE_TELL_WRITE:
382 LARGE_INTEGER offset;
385 if (!SetFilePointerEx(ctx->hout, zero, &offset, FILE_CURRENT)) {
386 zip_error_set(&ctx->error, ZIP_ER_TELL, _zip_win32_error_to_errno(GetLastError()));
390 return offset.QuadPart;
393 case ZIP_SOURCE_WRITE:
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()));
401 return (zip_int64_t)ret;
405 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
412 _win32_create_temp_file(_zip_source_win32_read_file_t *ctx)
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.
421 HANDLE th = INVALID_HANDLE_VALUE;
423 SECURITY_INFORMATION si;
424 SECURITY_ATTRIBUTES sa;
425 PSECURITY_DESCRIPTOR psd = NULL;
426 PSECURITY_ATTRIBUTES psa = NULL;
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.
435 if (ctx->h != INVALID_HANDLE_VALUE && GetFileType(ctx->h) == FILE_TYPE_DISK) {
436 si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
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);
444 success = GetUserObjectSecurity(ctx->h, &si, psd, len, &len);
447 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
448 sa.bInheritHandle = FALSE;
449 sa.lpSecurityDescriptor = psd;
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)
461 if (th == INVALID_HANDLE_VALUE) {
464 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_win32_error_to_errno(GetLastError()));
477 _zip_seek_win32_u(HANDLE h, zip_uint64_t offset, int whence, zip_error_t *error)
479 if (offset > ZIP_INT64_MAX) {
480 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
483 return _zip_seek_win32(h, (zip_int64_t)offset, whence, error);
488 _zip_seek_win32(HANDLE h, zip_int64_t offset, int whence, zip_error_t *error)
501 method = FILE_CURRENT;
504 zip_error_set(error, ZIP_ER_SEEK, EINVAL);
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()));
519 _zip_win32_error_to_errno(DWORD win32err)
522 Note: This list isn't exhaustive, but should cover common cases.
525 case ERROR_INVALID_PARAMETER:
527 case ERROR_FILE_NOT_FOUND:
529 case ERROR_INVALID_HANDLE:
531 case ERROR_ACCESS_DENIED:
533 case ERROR_FILE_EXISTS:
535 case ERROR_TOO_MANY_OPEN_FILES:
537 case ERROR_DISK_FULL:
546 _zip_stat_win32(HANDLE h, zip_stat_t *st, _zip_source_win32_read_file_t *ctx)
553 if (!GetFileTime(h, NULL, NULL, &mtimeft)) {
554 zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError()));
557 if (_zip_filetime_to_time_t(mtimeft, &mtime) < 0) {
558 zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE);
563 if (GetFileType(h) == FILE_TYPE_DISK) {
567 if (!GetFileSizeEx(h, &size)) {
568 zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError()));
574 st->valid |= ZIP_STAT_MTIME;
576 st->size = ctx->end - ctx->start;
577 st->valid |= ZIP_STAT_SIZE;
580 st->size = (zip_uint64_t)size.QuadPart;
581 st->valid |= ZIP_STAT_SIZE;
589 _zip_filetime_to_time_t(FILETIME ft, time_t *t)
592 Inspired by http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux
594 const zip_int64_t WINDOWS_TICK = 10000000LL;
595 const zip_int64_t SEC_TO_UNIX_EPOCH = 11644473600LL;
600 li.LowPart = ft.dwLowDateTime;
601 li.HighPart = ft.dwHighDateTime;
602 secs = (li.QuadPart / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
605 if (secs != (zip_int64_t)temp)