Win32 zip_source implementation using function pointers instead of #include to abstract character types
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e9eda2e..524f23d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt
@@ -139,6 +139,10 @@ zip_source_supports.c zip_source_tell.c zip_source_tell_write.c + zip_source_win32a.c + zip_source_win32file.c + zip_source_win32utf8.c + zip_source_win32w.c zip_source_window.c zip_source_write.c zip_source_zip.c
diff --git a/lib/zip.h b/lib/zip.h index 25afa98..b2743bb 100644 --- a/lib/zip.h +++ b/lib/zip.h
@@ -398,6 +398,16 @@ ZIP_EXTERN int zip_source_stat(zip_source_t *, zip_stat_t *); ZIP_EXTERN zip_int64_t zip_source_tell(zip_source_t *); ZIP_EXTERN zip_int64_t zip_source_tell_write(zip_source_t *); +#ifdef _WIN32 +ZIP_EXTERN zip_source_t *zip_source_win32a(zip_t *, const char *, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *zip_source_win32a_create(const char *, zip_uint64_t, zip_int64_t, zip_error_t *); +ZIP_EXTERN zip_source_t *zip_source_win32file(zip_t *, void *, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *zip_source_win32file_create(void *, zip_uint64_t, zip_int64_t, zip_error_t *); +ZIP_EXTERN zip_source_t *zip_source_win32utf8(zip_t *, const char *, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *zip_source_win32utf8_create(const char *, zip_uint64_t, zip_int64_t, zip_error_t *); +ZIP_EXTERN zip_source_t *zip_source_win32w(zip_t *, const wchar_t *, zip_uint64_t, zip_int64_t); +ZIP_EXTERN zip_source_t *zip_source_win32w_create(const wchar_t *, zip_uint64_t, zip_int64_t, zip_error_t *); +#endif ZIP_EXTERN zip_int64_t zip_source_write(zip_source_t *, const void *, zip_uint64_t); ZIP_EXTERN zip_source_t *zip_source_zip(zip_t *, zip_t *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_int64_t); ZIP_EXTERN int zip_stat(zip_t *, const char *, zip_flags_t, zip_stat_t *);
diff --git a/lib/zip_source_win32a.c b/lib/zip_source_win32a.c new file mode 100644 index 0000000..1ccbd5c --- /dev/null +++ b/lib/zip_source_win32a.c
@@ -0,0 +1,142 @@ +/* +zip_source_win32a.c -- create data source from Windows file (ANSI) +Copyright (C) 1999-2014 Dieter Baron and Thomas Klausner + +This file is part of libzip, a library to manipulate ZIP archives. +The authors can be contacted at <libzip@nih.at> + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. +3. The names of the authors may not be used to endorse or promote +products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include <errno.h> +#include <stdio.h> +#include <windows.h> + +#include "zipint.h" + +static void * _win32_strdup_a(const void *str); +static HANDLE _win32_open_a(_zip_source_win32_read_file_t *ctx); +static HANDLE _win32_create_temp_a(_zip_source_win32_read_file_t *ctx, void **temp, zip_uint32_t value); +static int _win32_rename_temp_a(_zip_source_win32_read_file_t *ctx); +static int _win32_discard_source_a(_zip_source_win32_read_file_t *ctx); +static int _win32_discard_temp_a(_zip_source_win32_read_file_t *ctx); +static HANDLE _win32_create_temp_file_a(const char *fname, char *temp, int len); + + +static _zip_source_win32_file_ops_t win32_ops_a = { + .op_strdup = _win32_strdup_a, + .op_open = _win32_open_a, + .op_create_temp = _win32_create_temp_a, + .op_rename_temp = _win32_rename_temp_a, + .op_discard_source = _win32_discard_source_a, + .op_discard_temp = _win32_discard_temp_a +}; + +ZIP_EXTERN zip_source_t * +zip_source_win32a(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) +{ + if (za == NULL) + return NULL; + + return zip_source_win32a_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32a_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) +{ + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return _zip_source_win32_handle_or_name(fname, INVALID_HANDLE_VALUE, start, length, 1, NULL, &win32_ops_a, error); +} + + +void * +_win32_strdup_a(const void *str) +{ + return strdup((const char *)str); +} + + +HANDLE +_win32_open_a(_zip_source_win32_read_file_t *ctx) +{ + return CreateFileA(ctx->fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +} + + +HANDLE +_win32_create_temp_a(_zip_source_win32_read_file_t *ctx, void **temp, zip_uint32_t value) +{ + int len; + + len = strlen((const char *)ctx->fname) + 10; + if (*temp == NULL) { + if ((*temp = (char *)malloc(sizeof(char) * len)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return INVALID_HANDLE_VALUE; + } + } + if (sprintf((char *)*temp, "%s.%08x", (const char *)ctx->fname, value) != len - 1) { + return INVALID_HANDLE_VALUE; + } + + return CreateFileA((const char *)*temp, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY, NULL); +} + + +int +_win32_rename_temp_a(_zip_source_win32_read_file_t *ctx) +{ + if (!MoveFileExA(ctx->tmpname, ctx->fname, MOVEFILE_REPLACE_EXISTING)) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + return 0; +} + + +int +_win32_discard_source_a(_zip_source_win32_read_file_t *ctx) +{ + if (!DeleteFileA(ctx->fname)) { + zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + return 0; +} + + +int +_win32_discard_temp_a(_zip_source_win32_read_file_t *ctx) +{ + DeleteFileA(ctx->tmpname); + return 0; +} \ No newline at end of file
diff --git a/lib/zip_source_win32file.c b/lib/zip_source_win32file.c new file mode 100644 index 0000000..0067fad --- /dev/null +++ b/lib/zip_source_win32file.c
@@ -0,0 +1,551 @@ +/* +zip_source_win32file.c -- create data source from HANDLE (Win32) +Copyright (C) 1999-2014 Dieter Baron and Thomas Klausner + +This file is part of libzip, a library to manipulate ZIP archives. +The authors can be contacted at <libzip@nih.at> + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. +3. The names of the authors may not be used to endorse or promote +products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include <windows.h> +#include <wchar.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "zipint.h" + +static zip_int64_t _win32_read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd); +static int _win32_create_temp_file(_zip_source_win32_read_file_t *ctx); +static int _zip_filetime_to_time_t(FILETIME ft, time_t *t); +static int _zip_seek_win32_u(void *h, zip_uint64_t offset, int whence, zip_error_t *error, unsigned long *win32errptr); +static int _zip_seek_win32(void *h, zip_int64_t offset, int whence, zip_error_t *error, unsigned long *win32errptr); +static int _zip_stat_win32(void *h, zip_stat_t *st, _zip_source_win32_read_file_t *ctx); + +ZIP_EXTERN zip_source_t * +zip_source_win32file(zip_t *za, HANDLE h, zip_uint64_t start, zip_int64_t len) +{ + if (za == NULL) + return NULL; + + return zip_source_win32file_create(h, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32file_create(HANDLE h, zip_uint64_t start, zip_int64_t length, zip_error_t *error) +{ + if (h == INVALID_HANDLE_VALUE || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return _zip_source_win32_handle_or_name(NULL, h, start, length, 1, NULL, NULL, error); +} + +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) +{ + _zip_source_win32_read_file_t *ctx; + zip_source_t *zs; + + if (h == INVALID_HANDLE_VALUE && fname == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((ctx = (_zip_source_win32_read_file_t *)malloc(sizeof(_zip_source_win32_read_file_t))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + ctx->fname = NULL; + if (fname) { + if ((ctx->fname = ops->op_strdup(fname)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(ctx); + return NULL; + } + } + + ctx->ops = ops; + ctx->h = h; + ctx->start = start; + ctx->end = (len < 0 ? 0 : start + (zip_uint64_t)len); + ctx->closep = ctx->fname ? 1 : closep; + if (st) { + memcpy(&ctx->st, st, sizeof(ctx->st)); + ctx->st.name = NULL; + ctx->st.valid &= ~ZIP_STAT_NAME; + } + else { + zip_stat_init(&ctx->st); + } + + ctx->tmpname = NULL; + ctx->hout = INVALID_HANDLE_VALUE; + + zip_error_init(&ctx->error); + + ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1); + if (ctx->fname) { + HANDLE th; + + th = ops->op_open(ctx); + if (th != INVALID_HANDLE_VALUE && GetFileType(th) == FILE_TYPE_DISK) { + ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; + } + if (th != INVALID_HANDLE_VALUE) { + CloseHandle(th); + } + } + else if (GetFileType(ctx->h) == FILE_TYPE_DISK) { + ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE; + } + + if ((zs = zip_source_function_create(_win32_read_file, ctx, error)) == NULL) { + free(ctx); + return NULL; + } + + return zs; +} + +zip_int64_t +_win32_read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) +{ + _zip_source_win32_read_file_t *ctx; + char *buf; + zip_uint64_t n; + DWORD i; + + ctx = (_zip_source_win32_read_file_t *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_BEGIN_WRITE: + if (ctx->fname == NULL) { + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } + return _win32_create_temp_file(ctx); + + case ZIP_SOURCE_COMMIT_WRITE: { + if (!CloseHandle(ctx->hout)) { + ctx->hout = INVALID_HANDLE_VALUE; + zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + } + ctx->hout = INVALID_HANDLE_VALUE; + return ctx->ops->op_rename_temp(ctx); + } + + case ZIP_SOURCE_CLOSE: + if (ctx->fname) { + CloseHandle(ctx->h); + ctx->h = INVALID_HANDLE_VALUE; + } + return 0; + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + free(ctx->fname); + free(ctx->tmpname); + if (ctx->closep && ctx->h != INVALID_HANDLE_VALUE) + CloseHandle(ctx->h); + free(ctx); + return 0; + + case ZIP_SOURCE_OPEN: + if (ctx->fname) { + if ((ctx->h = ctx->ops->op_open(ctx)) == INVALID_HANDLE_VALUE) { + zip_error_set(&ctx->error, ZIP_ER_OPEN, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + } + + if (ctx->closep && ctx->start > 0) { + if (_zip_seek_win32_u(ctx->h, ctx->start, SEEK_SET, &ctx->error, &ctx->win32err) < 0) { + return -1; + } + } + ctx->current = ctx->start; + return 0; + + case ZIP_SOURCE_READ: + if (ctx->end > 0) { + n = ctx->end - ctx->current; + if (n > len) { + n = len; + } + } + else { + n = len; + } + + if (n > SIZE_MAX) + n = SIZE_MAX; + + if (!ctx->closep) { + if (_zip_seek_win32_u(ctx->h, ctx->current, SEEK_SET, &ctx->error, &ctx->win32err) < 0) { + return -1; + } + } + + if (!ReadFile(ctx->h, buf, (DWORD)n, &i, NULL)) { + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + ctx->current += i; + + return (zip_int64_t)i; + + case ZIP_SOURCE_REMOVE: + return ctx->ops->op_discard_source(ctx); + + case ZIP_SOURCE_ROLLBACK_WRITE: + if (ctx->hout) { + CloseHandle(ctx->hout); + ctx->hout = INVALID_HANDLE_VALUE; + } + ctx->ops->op_discard_temp(ctx); + free(ctx->tmpname); + ctx->tmpname = NULL; + return 0; + + case ZIP_SOURCE_SEEK: { + zip_int64_t new_current; + int need_seek; + zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); + + if (args == NULL) + return -1; + + need_seek = ctx->closep; + + switch (args->whence) { + case SEEK_SET: + new_current = args->offset; + break; + + case SEEK_END: + if (ctx->end == 0) { + LARGE_INTEGER zero; + LARGE_INTEGER new_offset; + + if (_zip_seek_win32(ctx->h, args->offset, SEEK_END, &ctx->error, &ctx->win32err) < 0) { + return -1; + } + zero.QuadPart = 0; + if (!SetFilePointerEx(ctx->h, zero, &new_offset, FILE_CURRENT)) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + new_current = new_offset.QuadPart; + need_seek = 0; + } + else { + new_current = (zip_int64_t)ctx->end + args->offset; + } + break; + case SEEK_CUR: + new_current = (zip_int64_t)ctx->current + args->offset; + break; + + default: + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (new_current < 0 || (zip_uint64_t)new_current < ctx->start || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + ctx->current = (zip_uint64_t)new_current; + + if (need_seek) { + if (_zip_seek_win32_u(ctx->h, ctx->current, SEEK_SET, &ctx->error, &ctx->win32err) < 0) { + return -1; + } + } + return 0; + } + + case ZIP_SOURCE_SEEK_WRITE: { + zip_source_args_seek_t *args; + + args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); + if (args == NULL) { + return -1; + } + + if (_zip_seek_win32(ctx->hout, args->offset, args->whence, &ctx->error, &ctx->win32err) < 0) { + return -1; + } + return 0; + } + + case ZIP_SOURCE_STAT: { + if (len < sizeof(ctx->st)) + return -1; + + if (ctx->st.valid != 0) + memcpy(data, &ctx->st, sizeof(ctx->st)); + else { + DWORD win32err; + zip_stat_t *st; + HANDLE h; + int success; + + st = (zip_stat_t *)data; + + if (ctx->h != INVALID_HANDLE_VALUE) { + h = ctx->h; + } + else { + h = ctx->ops->op_open(ctx); + } + + success = _zip_stat_win32(h, st, ctx); + win32err = GetLastError(); + + /* We're done with the handle, so close it if we just opened it. */ + if (h != ctx->h) { + CloseHandle(h); + } + + if (success < 0) { + /* TODO: Is this the correct error to return in all cases? */ + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_set_win32_error(win32err, &ctx->win32err)); + return -1; + } + } + return sizeof(ctx->st); + } + + case ZIP_SOURCE_SUPPORTS: + return ctx->supports; + + case ZIP_SOURCE_TELL: + return (zip_int64_t)ctx->current; + + case ZIP_SOURCE_TELL_WRITE: + { + LARGE_INTEGER zero; + LARGE_INTEGER offset; + + zero.QuadPart = 0; + if (!SetFilePointerEx(ctx->hout, zero, &offset, FILE_CURRENT)) { + zip_error_set(&ctx->error, ZIP_ER_TELL, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + + return offset.QuadPart; + } + + case ZIP_SOURCE_WRITE: + { + DWORD ret; + if (!WriteFile(ctx->hout, data, (DWORD)len, &ret, NULL) || ret != len) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + + return (zip_int64_t)ret; + } + + default: + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } +} + +int +_win32_create_temp_file(_zip_source_win32_read_file_t *ctx) +{ + /* + Windows has GetTempFileName(), but it closes the file after + creation, leaving it open to a horrible race condition. So + we reinvent the wheel. + */ + int i; + HANDLE th = INVALID_HANDLE_VALUE; + void *temp = NULL; + + zip_uint32_t value = GetTickCount(); + for (i = 0; i < 1024 && th == INVALID_HANDLE_VALUE; i++) { + th = ctx->ops->op_create_temp(ctx, &temp, value + i); + if (th == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_EXISTS) + break; + } + + if (th == INVALID_HANDLE_VALUE) { + free(temp); + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + + ctx->hout = th; + ctx->tmpname = temp; + + return 0; +} + +int +_zip_seek_win32_u(HANDLE h, zip_uint64_t offset, int whence, zip_error_t *error, DWORD *win32errptr) +{ + if (offset > ZIP_INT64_MAX) { + zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW); + return -1; + } + return _zip_seek_win32(h, (zip_int64_t)offset, whence, error, win32errptr); +} + + +int +_zip_seek_win32(HANDLE h, zip_int64_t offset, int whence, zip_error_t *error, DWORD *win32errptr) +{ + LARGE_INTEGER li; + DWORD method; + + switch (whence) { + case SEEK_SET: + method = FILE_BEGIN; + break; + case SEEK_END: + method = FILE_END; + break; + case SEEK_CUR: + method = FILE_CURRENT; + break; + default: + zip_error_set(error, ZIP_ER_SEEK, EINVAL); + return -1; + } + + li.QuadPart = (LONGLONG)offset; + if (!SetFilePointerEx(h, li, NULL, method)) { + zip_error_set(error, ZIP_ER_SEEK, _zip_set_win32_error(GetLastError(), win32errptr)); + return -1; + } + + return 0; +} + + +int +_zip_stat_win32(HANDLE h, zip_stat_t *st, _zip_source_win32_read_file_t *ctx) +{ + FILETIME mtimeft; + time_t mtime; + LARGE_INTEGER size; + int regularp; + + if (!GetFileTime(h, NULL, NULL, &mtimeft) || _zip_filetime_to_time_t(mtimeft, &mtime) < 0) { + return -1; + } + + regularp = 0; + if (GetFileType(h) == FILE_TYPE_DISK) { + regularp = 1; + } + + if (!GetFileSizeEx(h, &size)) { + return -1; + } + + zip_stat_init(st); + st->mtime = mtime; + st->valid |= ZIP_STAT_MTIME; + if (ctx->end != 0) { + st->size = ctx->end - ctx->start; + st->valid |= ZIP_STAT_SIZE; + } + else if (regularp) { + st->size = (zip_uint64_t)size.QuadPart; + st->valid |= ZIP_STAT_SIZE; + } + + return 0; +} + + +int +_zip_set_win32_error(DWORD win32err, DWORD *win32errptr) +{ + /* + Note: This list isn't exhaustive, but should cover common cases. + */ + if (win32errptr != NULL) { + *win32errptr = win32err; + } + switch (win32err) { + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_FILE_NOT_FOUND: + return ENOENT; + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_FILE_EXISTS: + return EEXIST; + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + case ERROR_DISK_FULL: + return ENOSPC; + default: + return 0; + } +} + +int +_zip_filetime_to_time_t(FILETIME ft, time_t *t) +{ + /* + Inspired by http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux + */ + const zip_int64_t WINDOWS_TICK = 10000000LL; + const zip_int64_t SEC_TO_UNIX_EPOCH = 11644473600LL; + ULARGE_INTEGER li; + zip_int64_t secs; + time_t temp; + + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + secs = (li.QuadPart / WINDOWS_TICK - SEC_TO_UNIX_EPOCH); + + temp = (time_t)secs; + if (secs != (zip_int64_t)temp) + return -1; + + *t = temp; + return 0; +}
diff --git a/lib/zip_source_win32utf8.c b/lib/zip_source_win32utf8.c new file mode 100644 index 0000000..2b82852 --- /dev/null +++ b/lib/zip_source_win32utf8.c
@@ -0,0 +1,80 @@ +/* +zip_source_win32utf8.c -- create data source from Windows file (UTF-8) +Copyright (C) 1999-2014 Dieter Baron and Thomas Klausner + +This file is part of libzip, a library to manipulate ZIP archives. +The authors can be contacted at <libzip@nih.at> + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. +3. The names of the authors may not be used to endorse or promote +products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include <errno.h> +#include <stdio.h> +#include <windows.h> + +#include "zipint.h" + + +ZIP_EXTERN zip_source_t * +zip_source_win32utf8(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) +{ + if (za == NULL) + return NULL; + + return zip_source_win32utf8_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32utf8_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) +{ + int size; + wchar_t *wfname; + zip_source_t *source; + + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + /* Convert fname from UTF-8 to Windows-friendly UTF-16. */ + size = MultiByteToWideChar(CP_UTF8, WC_ERR_INVALID_CHARS, fname, -1, NULL, 0); + if (size == 0) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + if ((wfname = (wchar_t *)malloc(sizeof(wchar_t) * size)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + MultiByteToWideChar(CP_UTF8, WC_ERR_INVALID_CHARS, fname, -1, wfname, size); + + source = zip_source_win32w_create(wfname, start, length, error); + + free(wfname); + return source; +}
diff --git a/lib/zip_source_win32w.c b/lib/zip_source_win32w.c new file mode 100644 index 0000000..e35f293 --- /dev/null +++ b/lib/zip_source_win32w.c
@@ -0,0 +1,141 @@ +/* +zip_source_win32w.c -- create data source from Windows file (Unicode) +Copyright (C) 1999-2014 Dieter Baron and Thomas Klausner + +This file is part of libzip, a library to manipulate ZIP archives. +The authors can be contacted at <libzip@nih.at> + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. +3. The names of the authors may not be used to endorse or promote +products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include <errno.h> +#include <stdio.h> +#include <windows.h> + +#include "zipint.h" + +static void *_win32_strdup_w(const void *str); +static HANDLE _win32_open_w(_zip_source_win32_read_file_t *ctx); +static HANDLE _win32_create_temp_w(_zip_source_win32_read_file_t *ctx, void **temp, zip_uint32_t value); +static int _win32_rename_temp_w(_zip_source_win32_read_file_t *ctx); +static int _win32_discard_source_w(_zip_source_win32_read_file_t *ctx); +static int _win32_discard_temp_w(_zip_source_win32_read_file_t *ctx); +static HANDLE _win32_create_temp_file_w(const wchar_t *fname, wchar_t *temp, int len); + +static _zip_source_win32_file_ops_t win32_ops_w = { + .op_strdup = _win32_strdup_w, + .op_open = _win32_open_w, + .op_create_temp = _win32_create_temp_w, + .op_rename_temp = _win32_rename_temp_w, + .op_discard_source = _win32_discard_source_w, + .op_discard_temp = _win32_discard_temp_w +}; + +ZIP_EXTERN zip_source_t * +zip_source_win32w(zip_t *za, const wchar_t *fname, zip_uint64_t start, zip_int64_t len) +{ + if (za == NULL) + return NULL; + + return zip_source_win32w_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32w_create(const wchar_t *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) +{ + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return _zip_source_win32_handle_or_name(fname, INVALID_HANDLE_VALUE, start, length, 1, NULL, &win32_ops_w, error); +} + + +void * +_win32_strdup_w(const void *str) +{ + return _wcsdup((const wchar_t *)str); +} + + +HANDLE +_win32_open_w(_zip_source_win32_read_file_t *ctx) +{ + return CreateFileW(ctx->fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +} + + +HANDLE +_win32_create_temp_w(_zip_source_win32_read_file_t *ctx, void **temp, zip_uint32_t value) +{ + int len; + + len = wcslen((const wchar_t *)ctx->fname) + 10; + if (*temp == NULL) { + if ((*temp = (char *)malloc(sizeof(char) * len)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return INVALID_HANDLE_VALUE; + } + } + if (swprintf((wchar_t *)*temp, len, L"%s.%08x", (const wchar_t *)ctx->fname, value) != len - 1) { + return INVALID_HANDLE_VALUE; + } + + return CreateFileW((const wchar_t *)*temp, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY, NULL); +} + + +int +_win32_rename_temp_w(_zip_source_win32_read_file_t *ctx) +{ + if (!MoveFileExW(ctx->tmpname, ctx->fname, MOVEFILE_REPLACE_EXISTING)) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + return 0; +} + + +int +_win32_discard_source_w(_zip_source_win32_read_file_t *ctx) +{ + if (!DeleteFileW(ctx->fname)) { + zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_set_win32_error(GetLastError(), &ctx->win32err)); + return -1; + } + return 0; +} + + +int +_win32_discard_temp_w(_zip_source_win32_read_file_t *ctx) +{ + DeleteFileW(ctx->tmpname); + return 0; +}
diff --git a/lib/zipint.h b/lib/zipint.h index edfc455..1d38885 100644 --- a/lib/zipint.h +++ b/lib/zipint.h
@@ -392,6 +392,51 @@ #define ZIP_SOURCE_IS_OPEN_WRITING(src) ((src)->write_state == ZIP_SOURCE_WRITE_OPEN) #define ZIP_SOURCE_IS_LAYERED(src) ((src)->src != NULL) +#ifdef _WIN32 + +/* context for Win32 source */ + +struct _zip_source_win32_file_ops; + +struct _zip_source_win32_read_file { + zip_error_t error; /* last error information */ + DWORD win32err; /* last Win32 error */ + zip_int64_t supports; + + /* operations */ + struct _zip_source_win32_file_ops *ops; + + /* reading */ + void *fname; /* name of file to read from - ANSI (char *) or Unicode (wchar_t *) */ + void *h; /* HANDLE for file to read from */ + int closep; /* whether to close f on ZIP_CMD_FREE */ + struct zip_stat st; /* stat information passed in */ + zip_uint64_t start; /* start offset of data to read */ + zip_uint64_t end; /* end offset of data to read, 0 for up to EOF */ + zip_uint64_t current; /* current offset */ + + /* writing */ + void *tmpname; /* name of temp file - ANSI (char *) or Unicode (wchar_t *) */ + void *hout; /* HANDLE for output file */ +}; + +typedef struct _zip_source_win32_read_file _zip_source_win32_read_file_t; + +/* internal operations for Win32 source */ + +struct _zip_source_win32_file_ops { + void *(*op_strdup)(const void *str); + void *(*op_open)(_zip_source_win32_read_file_t *ctx); + void *(*op_create_temp)(_zip_source_win32_read_file_t *ctx, void **temp, zip_uint32_t value); + int (*op_rename_temp)(_zip_source_win32_read_file_t *ctx); + int (*op_discard_source)(_zip_source_win32_read_file_t *ctx); + int (*op_discard_temp)(_zip_source_win32_read_file_t *ctx); +}; + +typedef struct _zip_source_win32_file_ops _zip_source_win32_file_ops_t; + +#endif + /* entry in zip archive directory */ struct zip_entry { @@ -563,4 +608,9 @@ void _zip_unchange_data(zip_entry_t *); int _zip_write(zip_t *za, const void *data, zip_uint64_t length); +#ifdef _WIN32 +int _zip_set_win32_error(unsigned long win32err, unsigned long *win32errptr); +zip_source_t *_zip_source_win32_handle_or_name(const void *fname, void *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); +#endif + #endif /* zipint.h */