/* * Copyright (c) 2011-2015, Emulex * All rights reserved. * * 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. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. * */ /** * @file * Implements the Emulex coreDump front end API */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define enable_compression 1 #define MAX_SEG_INST_CHUNK_SIZE (64*1024) /* Copyright string to show up in .a archive */ const char copyright[] = "Copyright (c) 2012 Emulex. All rights reserved"; void *myalloc(void *q, unsigned n, unsigned m) { return os_malloc(q, n*m); } void myfree(void *q, void *p) { os_free(q, p); } static alloc_func zalloc = myalloc; static free_func zfree = myfree; static char version[] = "0.1.0-$Rev: 23421 $"; typedef CDBC_CONTEXT ecd_context_t; static int32_t ecd_WriteSegmentHeader(struct CORE_DUMP_HEADER *core_dump_hdr, int compressed, int ver_hi, int ver_lo, int status, int seg_id, int seg_instance, int func_id, uint32_t durationMsec, uint32_t offset, uint32_t length); static int32_t ecd_WriteCoreDumpRecord(struct RECORD_CORE_DUMP *record_core_dump, int status, int successful_segments, ecd_fwState_t fwState, ecd_get_params_t *params, struct tm *startLocal, struct tm *endGMT, int utc_bias, char *coredump_str); static uint32_t ecd_MsecTime(void); static char *ecd_BuildCmdLine(int argc, char *argv[], void *dumpCtx); static int compressInit(z_stream *zInfo, void *opaque); static int compressData(z_stream *zInfo, uint8_t *abSrc, uint32_t nLenSrc, uint8_t *abDst, uint32_t nLenDst, uint8_t last); static char *better_strncpy(char *dest, char *src, int n); #define ECD_COREDUMP_FLAG_DEAD (1U << 1) #define ECD_COREDUMP_FLAG_LIVE (1U << 2) /** * @brief Helper function to accept write 8 bit data from byte code process * * @param ctx application context * @param offset buffer offset * @param data data to write * * @return none */ VOID os_dump_write8 (PVOID ctx, ULONG offset, ULONG data) { ecd_context_t *ecd = ctx; if ((offset + sizeof(uint8_t)) <= ecd->Length) { *((uint8_t*) (ecd->Buffer + offset - ecd->Offset)) = data; ecd->Written += sizeof(uint8_t); } else { ecd_log("%s: boundary check failed, offset=%d, len=%d\n", __func__, offset, ecd->Length); } } /** * @brief Helper function to accept write 16 bit data from byte code process * * @param ctx application context * @param offset buffer offset * @param data data to write * * @return none */ VOID os_dump_write16(PVOID ctx, ULONG offset, ULONG data) { ecd_context_t *ecd = ctx; if ((offset + sizeof(uint16_t)) <= ecd->Length) { *((uint16_t*) (ecd->Buffer + offset - ecd->Offset)) = data; ecd->Written += sizeof(uint16_t); } else { ecd_log("%s: boundary check failed, offset=%d, len=%d\n", __func__, offset, ecd->Length); } } /** * @brief Helper function to accept write 32 bit data from byte code process * * @param ctx application context * @param offset buffer offset * @param data data to write * * @return none */ VOID os_dump_write32(PVOID ctx, ULONG offset, ULONG data) { ecd_context_t *ecd = ctx; if ((offset + sizeof(uint32_t)) <= ecd->Length) { *((uint32_t*) (ecd->Buffer + offset - ecd->Offset)) = data; ecd->Written += sizeof(uint32_t); } else { ecd_log("%s: boundary check failed, offset=%d, len=%d\n", __func__, offset, ecd->Length); } } /** * @brief Get coredump data * * This is the exported interface that is invoked to fetch coredumpdata * * @param dumpCtx application provided context (passed to helper functions) * @param flags control flags * @param fwState state of firmware * @param dumpBuffer buffer into which the coredump data is placed * @param dumpBufferSize length of dumpBuffer * @param argc option count * @param argv options * * @return positive value is the amount of data written, a negative value * indicates an error */ int ecd_GetCoreDumpData(void *dumpCtx, ecd_fwState_t fwState, ecd_get_params_t *params, void *dumpBuffer, int dumpBufferSize, int argc, char *argv[]) { int i; int j; ecd_context_t ctx; CDBC_U32 cdbc_rc = COREDUMP_STATUS_SUCCESS; DUMP_TEMPLATE *template = NULL; PDUMP_TEMPLATE pt; char *cmdline; uint32_t templateCount = 0; uint32_t seg_success = 0; uint32_t seg_count = 0; uint32_t msecStartTime; time_t now; struct tm startGMT; struct tm startLocal; struct tm endLocal; uint32_t utc_bias; uint32_t seglen_cur, seglen_total; int compressed; int compress_len, compress_len_total; uint8_t *dumpBufferPointer = dumpBuffer; uint32_t flags; uint8_t family; uint8_t asic_rev; ecd_log("ELXCDLU: Version %s\n", version); /* Temporary override */ fwState = ECD_FW_STATE_UE; ecd_log("Override fwState to UE\n"); /* Default template is the BE3 template */ template = be3_template; templateCount = ARRAY_SIZE(be3_template); /* Parse options */ for (i = 0; i < argc; i ++) { if (strcmp(argv[i], "testtemplate") == 0) { template = test_template; templateCount = ARRAY_SIZE(test_template); } else if (strcmp(argv[i], "be3template") == 0) { template = be3_template; templateCount = ARRAY_SIZE(be3_template); } else if (strcmp(argv[i], "skytemplate") == 0) { template = sky_template; templateCount = ARRAY_SIZE(sky_template); } else if (strcmp(argv[i], "dead") == 0) { flags = ECD_COREDUMP_FLAG_DEAD; } else if (strcmp(argv[i], "live") == 0) { flags = ECD_COREDUMP_FLAG_LIVE; } } /* Temporary override */ flags = ECD_COREDUMP_FLAG_DEAD; ecd_log("Override dump type to DEAD\n"); if (template == NULL) { template = be3_template; templateCount = ARRAY_SIZE(be3_template); } if (template == be3_template) { ecd_log("Template: be3_template, %s dump\n", (flags & ECD_COREDUMP_FLAG_DEAD) ? "dead" : (flags & ECD_COREDUMP_FLAG_LIVE) ? "live" : "unknown"); } else if (template == sky_template) { ecd_log("Template: sky_template, %s dump\n", (flags & ECD_COREDUMP_FLAG_DEAD) ? "dead" : (flags & ECD_COREDUMP_FLAG_LIVE) ? "live" : "unknown"); } else if (template == test_template) { ecd_log("Template: test_template, %s dump\n", (flags & ECD_COREDUMP_FLAG_DEAD) ? "dead" : (flags & ECD_COREDUMP_FLAG_LIVE) ? "live" : "unknown"); } else { ecd_log("Template: unknown, %s dump\n", (flags & ECD_COREDUMP_FLAG_DEAD) ? "dead" : (flags & ECD_COREDUMP_FLAG_LIVE) ? "live" : "unknown"); } /* Check for valid arguments */ if (dumpCtx == NULL) { ecd_log("Error: dumpCtx is NULL)\n"); return ECD_ERR_INVALID_ARGUMENT; } /* Validate the SLIF4_INTF register value */ if (((params->sli_intf >> SLI4_INTF_VALID_SHIFT) & SLI4_INTF_VALID_MASK) != SLI4_INTF_VALID) { ecd_log("SLI_INTF value %08X not valid\n", params->sli_intf); return ECD_ERR_INVALID_ARGUMENT; } if (((params->sli_intf >> SLI4_INTF_SLI_REVISION_SHIFT) & SLI4_INTF_SLI_REVISION_MASK) != SLI4_INTF_SLI_REVISION) { ecd_log("SLI_INTF value %08X has invalid revision\n", params->sli_intf); return ECD_ERR_INVALID_ARGUMENT; } family = ((params->sli_intf >> SLI4_INTF_SLI_FAMILY_SHIFT) & SLI4_INTF_SLI_FAMILY_MASK); if (family == SLI4_INTF_SLI_FAMILY_USE_ASIC_ID) { switch((params->asic_id >> SLI4_ASIC_GEN_SHIFT) & SLI4_ASIC_GEN_MASK) { case SLI4_ASIC_GEN_BE2: family = SLI4_INTF_SLI_FAMILY_BE2; break; case SLI4_ASIC_GEN_BE3: family = SLI4_INTF_SLI_FAMILY_BE3; break; case SLI4_ASIC_GEN_SKYHAWK: family = SLI4_INTF_SLI_FAMILY_SKYHAWK; break; case SLI4_ASIC_GEN_LANCER: asic_rev = ((params->asic_id >> SLI4_ASIC_VER_SHIFT) & SLI4_ASIC_VER_MASK); if (asic_rev == 0) { family = SLI4_INTF_SLI_FAMILY_LANCER_A0; } else { family = SLI4_INTF_SLI_FAMILY_LANCER_A0; } break; default: // Do nothing, leave the family as 0x0f break; } } switch (family) { case SLI4_INTF_SLI_FAMILY_BE3: case SLI4_INTF_SLI_FAMILY_SKYHAWK: break; default: ecd_log("SLI_INTF %08X unsuported family\n", params->sli_intf); return ECD_ERR_INVALID_ARGUMENT; } switch(fwState) { case ECD_FW_STATE_RUNNING: case ECD_FW_STATE_UE: case ECD_FW_STATE_FAILED: break; default: ecd_log("Error: invalid fwState: %d\n", fwState); return ECD_ERR_INVALID_ARGUMENT; } if (dumpBuffer == NULL) { ecd_log("Error: dumpBuffer is NULL\n"); return ECD_ERR_INVALID_ARGUMENT; } if (dumpBufferSize < ECD_MIN_BUFFER_SIZE) { ecd_log("Error: dumpBufferSize %d too small must be at least %d\n", dumpBufferSize, ECD_MIN_BUFFER_SIZE); return ECD_ERR_INVALID_ARGUMENT; } /* Initiatlize our context */ memset(&ctx, 0, sizeof(ctx)); ctx.osContext = dumpCtx; /* Mark starting time */ time(&now); gmtime_r(&now, &startGMT); localtime_r(&now, &startLocal); /* loop each segment execute byte code to capture dump data */ for (i = 0, pt = template; i < templateCount; i ++, pt ++) { /* if pbyte_code is NULL then skip */ if (pt->pbyte_code == NULL) { continue; } /* if request for for a DEAD dump and this segment is not so enabled, then skip */ if (flags & ECD_COREDUMP_FLAG_DEAD) if (!pt->dead_mode) continue; /* if request for for a LIVE dump and this segment is not so enabled, then skip */ if (flags & ECD_COREDUMP_FLAG_LIVE) if (!pt->live_mode) continue; /* Iterate through segment instances */ for (j = 0; j <= pt->max_instance; j ++) { uint8_t *buffer; uint8_t *bytecode_buffer; uint32_t buffer_len; uint32_t seglen_remain; struct CORE_DUMP_HEADER *segHdr; uint8_t instance_done = 0; z_stream zInfo; /* allocate space for byte code */ bytecode_buffer = os_malloc(dumpCtx, pt->bc_length * sizeof(uint32_t)); if (bytecode_buffer == NULL) { ecd_log("Error: bytecode buffer allocation failured\n"); return ECD_ERR_ALLOCATION; } /* start with maxium segment data size; zero counts */ seglen_remain = pt->length; seglen_total = 0; compress_len_total = 0; compressed = 0; /* Save pointer to segment header and skip over it */ segHdr = (struct CORE_DUMP_HEADER *) dumpBufferPointer; dumpBufferPointer += sizeof(*segHdr); dumpBufferSize -= sizeof(*segHdr); /* start with offset from template */ ctx.Offset = pt->offset; ctx.Length = 0; msecStartTime = ecd_MsecTime(); /* initialize compression; compression will be performed at the * segment instance level */ if (compressInit(&zInfo, dumpCtx)) { ecd_log("Error: failed to initialize compression lib\n"); os_free(dumpCtx, bytecode_buffer); return ECD_ERR_ALLOCATION; } /* segments can be big, handle breaking up segments into multiple chunks */ while (seglen_remain && !instance_done) { void *output_buf = NULL; uint32_t output_buf_len = 0; buffer_len = MIN(seglen_remain, MAX_SEG_INST_CHUNK_SIZE); /* Allocate buffer for uncompressed segment instance data */ buffer = os_malloc(dumpCtx, buffer_len); if (buffer == NULL) { ecd_log("Error: uncompressed buffer allocation failured\n"); os_free(dumpCtx, bytecode_buffer); os_free(dumpCtx, buffer); return ECD_ERR_ALLOCATION; } /* copy byte code each time! */ memcpy(bytecode_buffer, pt->pbyte_code, (pt->bc_length * sizeof(uint32_t))); /* prep context for segment */ ctx.Written = 0; ctx.Length += buffer_len; /* cummulative counter for segment instance */ ctx.Buffer = buffer; /* Fetch data from byte code processor */ cdbc_rc = cdbc_get_dump(&ctx, ctx.Offset, j, buffer_len, bytecode_buffer, NULL); // update length to the amount of data written by dump seglen_cur = ctx.Written; ctx.Offset += ctx.Written; compress_len = 0; if (cdbc_rc == COREDUMP_STATUS_SUCCESS) { if (seglen_cur) { if (enable_compression && pt->compress) { /* Attempt to compress this buffer (if there is any data) * if this is first call to compress data, give it output buffer. * Subsequent calls will pass in NULL to continue to use the same * output buffer. */ if (seglen_remain == pt->length) { output_buf = dumpBufferPointer; output_buf_len = dumpBufferSize; } /* Set the last flag to 1 if segment remaining equals to buffer len * or current segment length is less than buffer length. * Otherwise set it to 0 */ compress_len = compressData(&zInfo, buffer, seglen_cur, output_buf, output_buf_len, (seglen_remain == buffer_len || seglen_cur < buffer_len)); if (compress_len >= 0) { /* compression either in progress (return 0) or complete * (return > 0) */ compressed = 1; compress_len_total += compress_len; } else { /* compression failed, exit out */ ecd_log("seg=[%x/%x] failed compression seglen_cur=%d off=%d\n", i, j, seglen_cur, ctx.Offset); os_free(dumpCtx, bytecode_buffer); os_free(dumpCtx, buffer); return ECD_ERR_BUFFER_OVERRUN; } } else { /* If compression not supported, copy the original buffer */ if (seglen_cur > dumpBufferSize) { ecd_log("Error: seg[%x/%x] will not fit, seglen=%d remain=%d\n", i, j, seglen_cur, dumpBufferSize); os_free(dumpCtx, bytecode_buffer); os_free(dumpCtx, buffer); return ECD_ERR_BUFFER_OVERRUN; } memcpy(dumpBufferPointer, buffer, seglen_cur); compress_len_total += seglen_cur; } /* if segment is compressed update dump ptr by compressed amount * (can be zero) */ dumpBufferPointer += (compressed ? compress_len : seglen_cur); dumpBufferSize -= (compressed ? compress_len : seglen_cur); seglen_total += seglen_cur; seglen_remain -= seglen_cur; } /* if didn't fill entire buffer, must be done */ if (seglen_cur < buffer_len) { //ecd_log("Warning: seg[%x/%x] buffer partially filled\n", i, j); instance_done = 1; } } else { ecd_log("Error: seg[%x/%x] failed: %08X\n", i, j, cdbc_rc); instance_done = 1; } os_free(dumpCtx, buffer); if (cdbc_rc != COREDUMP_STATUS_SUCCESS) { break; } } /* while (seglen_remain && !instance_done) */ if (cdbc_rc == COREDUMP_STATUS_SUCCESS) { seg_success++; } if (compressed) { /* if compressed, save the actual length after the data */ if (sizeof(uint32_t) > dumpBufferSize) { ecd_log("Error: seg[%x/%x] length will not fit, remain=%d\n", i, j, dumpBufferSize); os_free(dumpCtx, bytecode_buffer); return ECD_ERR_BUFFER_OVERRUN; } *((uint32_t*) (dumpBufferPointer)) = seglen_total; dumpBufferPointer += sizeof(uint32_t); dumpBufferSize -= sizeof(uint32_t); compress_len_total += sizeof(uint32_t); } /* done with segment; write segment header */ ecd_log("seg[%03x:%03x] max_inst %4d seg_len %8d max %8d compressed(%d) %8d bc_length %6d\n", i, j, pt->max_instance, seglen_total, pt->length, compressed, compress_len_total, pt->bc_length); if (sizeof(*segHdr) > dumpBufferSize) { ecd_log("Error: seg[%x/%x] header will not fit, seghdr=%d remain=%d\n", sizeof(*segHdr), dumpBufferSize); os_free(dumpCtx, bytecode_buffer); return ECD_ERR_BUFFER_OVERRUN; } ecd_WriteSegmentHeader(segHdr, compressed, pt->ver_hi, pt->ver_lo, cdbc_rc, i, j, params->instance, ecd_MsecTime() - msecStartTime, pt->offset, compress_len_total); seg_count++; os_free(dumpCtx, bytecode_buffer); } /* for (j = 0; j <= pt->max_instance; j ++) { */ } /* Mark completion time */ time(&now); localtime_r(&now, &endLocal); /* Compute the utc_bias */ utc_bias = (mktime(&startLocal) - mktime(&startGMT)) / 60; if (startLocal.tm_isdst) utc_bias += 60; ecd_log("%d/%d ok\n", seg_success, seg_count); /* Write the core dump file record */ cmdline = ecd_BuildCmdLine(argc, argv, dumpCtx); ecd_WriteCoreDumpRecord((struct RECORD_CORE_DUMP*)dumpBufferPointer, 0, /* even if some segments failed, status = 0 */ seg_count, fwState, params, &startLocal, &endLocal, utc_bias, cmdline); dumpBufferPointer += sizeof(struct RECORD_CORE_DUMP); dumpBufferSize -= sizeof(struct RECORD_CORE_DUMP); os_free(dumpCtx, cmdline); return dumpBufferPointer - (uint8_t*) dumpBuffer; } /** * @brief populate segment header * * Fill the segment header with required information * * @param core_dump_hdr pointer to header to fill * @param status status value to use * @param seg_id segment index to use * @param seg_instance segment instance to use * @param func_id PCI function * @param durationMsec duration in msec * @param offset segment offset value * @param length segment length * * @return ECD_SUCCESS * * @note */ static int32_t ecd_WriteSegmentHeader(struct CORE_DUMP_HEADER *core_dump_hdr, int compressed, int ver_hi, int ver_lo, int status, int seg_id, int seg_instance, int func_id, uint32_t durationMsec, uint32_t offset, uint32_t length) { memset(core_dump_hdr, 0, sizeof(*core_dump_hdr)); strncpy(core_dump_hdr->signature, "sEgM", 4); core_dump_hdr->component_id = COREDUMP_COMPONENT_BYTECODE; core_dump_hdr->segment_id = seg_id; core_dump_hdr->flag = compressed; core_dump_hdr->fun_id = func_id; core_dump_hdr->high_version = ver_hi; core_dump_hdr->low_version = ver_lo; core_dump_hdr->offset = offset; core_dump_hdr->length = length; core_dump_hdr->status = status; core_dump_hdr->duration = durationMsec; core_dump_hdr->data_offset = 64; core_dump_hdr->instance = seg_instance; return ECD_SUCCESS; } /** * @brief populate coredump file header * * The coredump file header is populated with the requested information * * @param record_core_dump pointer to header to fill * @param status status value * @param successful_segments number of segments processed * @param chip_state chip state * @param sli_intf sli_intf value * @param low_version version low * @param high_version version high * @param startGMT starting GMT * @param endGMT ending GMT * @param utc_bias utc bias * @param coredump_str command line string * */ static int32_t ecd_WriteCoreDumpRecord(struct RECORD_CORE_DUMP *record_core_dump, int status, int successful_segments, ecd_fwState_t fwState, ecd_get_params_t *params, struct tm *startGMT, struct tm *endGMT, int utc_bias, char *coredump_str) { memset(record_core_dump, 0, sizeof(struct RECORD_CORE_DUMP)); strncpy(record_core_dump->signature, "cOrE", 4); record_core_dump->flag = 0; record_core_dump->low_version = 0; record_core_dump->high_version = 1; record_core_dump->asic_state = fwState; better_strncpy(record_core_dump->system_name, params->system_name, sizeof(record_core_dump->system_name)); record_core_dump->year = startGMT->tm_year + 1900; record_core_dump->month = startGMT->tm_mon + 1; record_core_dump->day = startGMT->tm_mday; record_core_dump->hour = startGMT->tm_hour; record_core_dump->minute = startGMT->tm_min; record_core_dump->second = startGMT->tm_sec; record_core_dump->utc_bias = utc_bias; better_strncpy(record_core_dump->commandline, coredump_str, sizeof(record_core_dump->commandline)); record_core_dump->total_segements = successful_segments; record_core_dump->os_version_major = params->os_major; record_core_dump->os_version_minor = params->os_minor; better_strncpy(record_core_dump->os_name, params->os_name, sizeof(record_core_dump->os_name)); record_core_dump->end_year = endGMT->tm_year + 1900; record_core_dump->end_month = endGMT->tm_mon + 1; record_core_dump->end_day = endGMT->tm_mday; record_core_dump->end_hour = endGMT->tm_hour; record_core_dump->end_minute = endGMT->tm_min; record_core_dump->end_second = endGMT->tm_sec; record_core_dump->end_utc_bias = utc_bias; record_core_dump->sli_intf = params->sli_intf; record_core_dump->asic_id = params->asic_id; record_core_dump->coredump_status = status; record_core_dump->ioctl_low_version = params->ioctl_low_version; record_core_dump->ioctl_high_version = params->ioctl_high_version; return ECD_SUCCESS; } /** * @brief Return starting millisecond time * * Computes and returns a time in milliseconds * * @return time in milliseconds */ static uint32_t ecd_MsecTime(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec*1000) + (tv.tv_usec / 1000); } /** * @brief Builds a string from an argc/argv * * Allocates and returns a string populated with the command line arguments * * @param argc argument count * @param argv arguments * @param dumpCtx pointer to dump context * * @return pointer to allocated string or NULL */ static char *ecd_BuildCmdLine(int argc, char *argv[], void *dumpCtx) { uint32_t length = 0; int i; char *buf; for (i = 0; i < argc; i ++) // add length of argument, and one more for a space character length += (strlen(argv[i]) + 1); // Add one more for the terminating null length ++; buf = os_malloc(dumpCtx, length); if (buf) { strcpy(buf, ""); for (i = 0; i < argc; i ++) { strcat(buf, argv[i]); if (i != (argc-1)) strcat(buf, " "); } } return buf; } /** * @brief Initialize compression stream * * This function initializes a zlib compression stream * * @param zInfo The z_stream object containing the context of * the compression * @param opaque Pointer to opaque object to be passed to * zmalloc and zfree. * * @return 0 for success; non-zero otherwise */ static int compressInit(z_stream *zInfo, void *opaque) { int rc = 0; memset(zInfo, 0, sizeof(*zInfo)); /* overload zalloc and zfree (malloc and free calls by zlib) */ zInfo->opaque = (void *)opaque; zInfo->zalloc = zalloc; zInfo->zfree = zfree; rc = deflateInit(zInfo, Z_DEFAULT_COMPRESSION ); // zlib function return rc; } /** * @brief compress data * * This function provides a wrapper into the zlib compression * API * * @param zInfo The z_stream object containing the context of * the compression * @param abSrc Source buffer containing uncompressed data * @param nLenSrc Source buffer length * @param abDst Destination buffer that will contain the * compressed output. This will be non-NULL for the * first call to setup the output buffer and will * be set to NULL to continue writing to the * previous output buffer for subsequent calls. * @param nLenDst Destination buffer length * * @return The total amount of data written to the output * buffer when compression is complete; 0 indicates in * the middle of processing input buffer; a negative * value indicates an error */ static int compressData(z_stream *zInfo, uint8_t *abSrc, uint32_t nLenSrc, uint8_t *abDst, uint32_t nLenDst, uint8_t last) { int nErr=0; int nRet=-1; zInfo->avail_in = nLenSrc; zInfo->next_in = abSrc; if (abDst) { zInfo->next_out = abDst; zInfo->avail_out = nLenDst; } nErr = deflate(zInfo, (last ? Z_FINISH : Z_NO_FLUSH)); // zlib function if (last) { if (nErr == Z_STREAM_END) { /* compression complete, return total bytes output */ nRet = zInfo->total_out; } else { ecd_log("%s expected stream end\n", __func__); } deflateEnd(zInfo); // zlib function } else { if (nErr == Z_OK) { /* compression not done yet, return 0 */ nRet = 0; } else if ((nErr == Z_BUF_ERROR) && (zInfo->avail_out == 0)) { /* if we run out of output buffer, no error recovery, just fail */ ecd_log("%s Error: ran out of output buffers\n", __func__); } else { ecd_log("%s unexpected error=%d\n", __func__, nErr); } } //ecd_log("%s returning=%d lenDst=%d avail_out=%d\n", __func__, nRet, nLenDst, zInfo->avail_out); return nRet; } /** * @brief Handle strncpy in a better way * * If the stock strncpy() function, if the 'src' is longer than 'n', the resulting dest string * has no trailing NULL * * @param dest destination pointer * @param src source pointer * @param n maximum characters to copy * * @return pointer to dest */ static char *better_strncpy(char *dest, char *src, int n) { strncpy(dest, src, n); dest[n-1] = 0; return dest; }