static 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_win32_error_to_errno(GetLastError())); } ctx->hout = INVALID_HANDLE_VALUE; if (ctx->ops->op_rename_temp(ctx) < 0) { zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError())); return -1; } free(ctx->tmpname); ctx->tmpname = NULL; return 0; } 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_win32_error_to_errno(GetLastError())); return -1; } } if (ctx->closep && ctx->start > 0) { if (_zip_seek_win32_u(ctx->h, ctx->start, SEEK_SET, &ctx->error) < 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) < 0) { return -1; } } if (!ReadFile(ctx->h, buf, (DWORD)n, &i, NULL)) { zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError())); return -1; } ctx->current += i; return (zip_int64_t)i; case ZIP_SOURCE_REMOVE: if (ctx->ops->op_remove(ctx->fname) < 0) { zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_win32_error_to_errno(GetLastError())); return -1; } return 0; case ZIP_SOURCE_ROLLBACK_WRITE: if (ctx->hout) { CloseHandle(ctx->hout); ctx->hout = INVALID_HANDLE_VALUE; } ctx->ops->op_remove(ctx->tmpname); 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) < 0) { return -1; } zero.QuadPart = 0; if (!SetFilePointerEx(ctx->h, zero, &new_offset, FILE_CURRENT)) { zip_error_set(&ctx->error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError())); 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) < 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) < 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); if (h == INVALID_HANDLE_VALUE) { win32err = GetLastError(); if (win32err == ERROR_FILE_NOT_FOUND || win32err == ERROR_PATH_NOT_FOUND) { zip_error_set(&ctx->error, ZIP_ER_READ, ENOENT); return -1; } } } 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_win32_error_to_errno(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_win32_error_to_errno(GetLastError())); 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_win32_error_to_errno(GetLastError())); return -1; } return (zip_int64_t)ret; } default: zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); return -1; } }
static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { struct read_file *ctx; char *buf; zip_uint64_t n; size_t i; ctx = (struct read_file *)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 create_temp_output(ctx); case ZIP_SOURCE_COMMIT_WRITE: { mode_t mask; if (fclose(ctx->fout) < 0) { ctx->fout = NULL; zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); } ctx->fout = NULL; if (rename(ctx->tmpname, ctx->fname) < 0) { zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); return -1; } mask = umask(022); umask(mask); /* not much we can do if chmod fails except make the whole commit fail */ (void)chmod(ctx->fname, 0666&~mask); free(ctx->tmpname); ctx->tmpname = NULL; return 0; } case ZIP_SOURCE_CLOSE: if (ctx->fname) { fclose(ctx->f); ctx->f = NULL; } 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->f) fclose(ctx->f); free(ctx); return 0; case ZIP_SOURCE_OPEN: if (ctx->fname) { if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) { zip_error_set(&ctx->error, ZIP_ER_OPEN, errno); return -1; } } if (ctx->start > 0) { if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 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 ((i=fread(buf, 1, (size_t)n, ctx->f)) == 0) { if (ferror(ctx->f)) { zip_error_set(&ctx->error, ZIP_ER_READ, errno); return -1; } } ctx->current += i; return (zip_int64_t)i; case ZIP_SOURCE_REMOVE: if (remove(ctx->fname) < 0) { zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno); return -1; } return 0; case ZIP_SOURCE_ROLLBACK_WRITE: if (ctx->fout) { fclose(ctx->fout); ctx->fout = NULL; } (void)remove(ctx->tmpname); 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 = 1; switch (args->whence) { case SEEK_SET: new_current = args->offset; break; case SEEK_END: if (ctx->end == 0) { if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) { return -1; } if ((new_current = ftello(ctx->f)) < 0) { zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); return -1; } 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_fseek_u(ctx->f, ctx->current, SEEK_SET, &ctx->error) < 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_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 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 { zip_stat_t *st; struct stat fst; int err; if (ctx->f) err = fstat(fileno(ctx->f), &fst); else err = stat(ctx->fname, &fst); if (err != 0) { zip_error_set(&ctx->error, ZIP_ER_READ, errno); return -1; } st = (zip_stat_t *)data; zip_stat_init(st); st->mtime = fst.st_mtime; st->valid |= ZIP_STAT_MTIME; if (ctx->end != 0) { st->size = ctx->end - ctx->start; st->valid |= ZIP_STAT_SIZE; } else if ((fst.st_mode&S_IFMT) == S_IFREG) { st->size = (zip_uint64_t)fst.st_size; st->valid |= ZIP_STAT_SIZE; } } 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: { off_t ret = ftello(ctx->fout); if (ret < 0) { zip_error_set(&ctx->error, ZIP_ER_TELL, errno); return -1; } return ret; } case ZIP_SOURCE_WRITE: { size_t ret; clearerr(ctx->fout); ret = fwrite(data, 1, len, ctx->fout); if (ret != len || ferror(ctx->fout)) { zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); return -1; } return (zip_int64_t)ret; } default: zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); return -1; } }
static zip_int64_t source_hole_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command) { hole_t *ctx = (hole_t *)ud; switch (command) { case ZIP_SOURCE_BEGIN_WRITE: ctx->out = buffer_new(); return 0; case ZIP_SOURCE_CLOSE: return 0; case ZIP_SOURCE_COMMIT_WRITE: if (buffer_to_file(ctx->out, ctx->fname, &ctx->error) < 0) { return -1; } buffer_free(ctx->in); ctx->in = ctx->out; ctx->out = NULL; return 0; case ZIP_SOURCE_ERROR: return zip_error_to_data(&ctx->error, data, length); case ZIP_SOURCE_FREE: hole_free(ctx); return 0; case ZIP_SOURCE_OPEN: ctx->in->offset = 0; return 0; case ZIP_SOURCE_READ: return buffer_read(ctx->in, data, length, &ctx->error); case ZIP_SOURCE_REMOVE: buffer_free(ctx->in); ctx->in = buffer_new(); buffer_free(ctx->out); ctx->out = NULL; (void)remove(ctx->fname); return 0; case ZIP_SOURCE_ROLLBACK_WRITE: buffer_free(ctx->out); ctx->out = NULL; return 0; case ZIP_SOURCE_SEEK: return buffer_seek(ctx->in, data, length, &ctx->error); case ZIP_SOURCE_SEEK_WRITE: return buffer_seek(ctx->out, data, length, &ctx->error); case ZIP_SOURCE_STAT: { zip_stat_t *st = ZIP_SOURCE_GET_ARGS(zip_stat_t, data, length, &ctx->error); if (st == NULL) { return -1; } /* TODO: return ENOENT if fname doesn't exist */ st->valid |= ZIP_STAT_SIZE; st->size = ctx->in->size; return 0; } case ZIP_SOURCE_TELL: return (zip_int64_t)ctx->in->offset; case ZIP_SOURCE_TELL_WRITE: return (zip_int64_t)ctx->out->offset; case ZIP_SOURCE_WRITE: return buffer_write(ctx->out, data, length, &ctx->error); case ZIP_SOURCE_SUPPORTS: return zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_CLOSE, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_STAT, ZIP_SOURCE_TELL, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1); default: zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); return -1; } }
static zip_int64_t crc_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { struct crc_context *ctx; zip_int64_t n; ctx = (struct crc_context *)_ctx; switch (cmd) { case ZIP_SOURCE_OPEN: ctx->position = 0; return 0; case ZIP_SOURCE_READ: if ((n = zip_source_read(src, data, len)) < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } if (n == 0) { if (ctx->crc_position == ctx->position) { ctx->crc_complete = 1; ctx->size = ctx->position; if (ctx->validate) { struct zip_stat st; if (zip_source_stat(src, &st) < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } if ((st.valid & ZIP_STAT_CRC) && st.crc != ctx->crc) { zip_error_set(&ctx->error, ZIP_ER_CRC, 0); return -1; } if ((st.valid & ZIP_STAT_SIZE) && st.size != ctx->size) { zip_error_set(&ctx->error, ZIP_ER_INCONS, 0); return -1; } } } } else if (!ctx->crc_complete && ctx->position <= ctx->crc_position) { zip_uint64_t i, nn; for (i = ctx->crc_position - ctx->position; i < (zip_uint64_t)n; i += nn) { nn = ZIP_MIN(UINT_MAX, (zip_uint64_t)n-i); ctx->crc = (zip_uint32_t)crc32(ctx->crc, (const Bytef *)data+i, (uInt)nn); ctx->crc_position += nn; } } ctx->position += (zip_uint64_t)n; return n; case ZIP_SOURCE_CLOSE: return 0; case ZIP_SOURCE_STAT: { zip_stat_t *st; st = (zip_stat_t *)data; if (ctx->crc_complete) { /* TODO: Set comp_size, comp_method, encryption_method? After all, this only works for uncompressed data. */ st->size = ctx->size; st->crc = ctx->crc; st->comp_size = ctx->size; st->comp_method = ZIP_CM_STORE; st->encryption_method = ZIP_EM_NONE; st->valid |= ZIP_STAT_SIZE|ZIP_STAT_CRC|ZIP_STAT_COMP_SIZE|ZIP_STAT_COMP_METHOD|ZIP_STAT_ENCRYPTION_METHOD;; } return 0; } case ZIP_SOURCE_ERROR: return zip_error_to_data(&ctx->error, data, len); case ZIP_SOURCE_FREE: free(ctx); return 0; case ZIP_SOURCE_SUPPORTS: { zip_int64_t mask = zip_source_supports(src); if (mask < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } return mask & ~zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, -1); } case ZIP_SOURCE_SEEK: { zip_int64_t new_position; zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); if (args == NULL) { return -1; } if (zip_source_seek(src, args->offset, args->whence) < 0 || (new_position = zip_source_tell(src)) < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } ctx->position = (zip_uint64_t)new_position; return 0; } case ZIP_SOURCE_TELL: return (zip_int64_t)ctx->position; default: zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); return -1; } }