root/ext/zip/lib/zip_source_win32handle.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. zip_source_win32handle
  2. zip_source_win32handle_create
  3. _zip_source_win32_handle_or_name
  4. _win32_read_file
  5. _win32_create_temp_file
  6. _zip_seek_win32_u
  7. _zip_seek_win32
  8. _zip_win32_error_to_errno
  9. _zip_stat_win32
  10. _zip_filetime_to_time_t

   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 && 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
 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
 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
 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
 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
 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
 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 }

/* [<][>][^][v][top][bottom][index][help] */