/* Decode the possibly-zlib compressed string of length INLEN that is in IN, into OUT. We expect an integer is prepended to IN that specifies the original size, and that if encoded size == original size, that the remaining data is not compressed. In that case, we will simply return pointer into IN as data pointer for OUT, COPYLESS_ALLOWED has been set. The, the caller is expected not to modify the contents of OUT. An error is returned if the decoded length exceeds the given LIMIT. */ static svn_error_t * zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out, apr_size_t limit) { apr_size_t len; apr_uint64_t size; const unsigned char *oldplace = in; /* First thing in the string is the original length. */ in = svn__decode_uint(&size, in, in + inLen); len = (apr_size_t)size; if (in == NULL || len != size) return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL, _("Decompression of zlib compressed data failed: no size")); if (len > limit) return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL, _("Decompression of zlib compressed data failed: " "size too large")); /* We need to subtract the size of the encoded original length off the * still remaining input length. */ inLen -= (in - oldplace); if (inLen == len) { svn_stringbuf_ensure(out, len); memcpy(out->data, in, len); out->data[len] = 0; out->len = len; return SVN_NO_ERROR; } else { unsigned long zlen = len; int zerr; svn_stringbuf_ensure(out, len); zerr = uncompress((unsigned char *)out->data, &zlen, in, inLen); if (zerr != Z_OK) return svn_error_trace(svn_error__wrap_zlib( zerr, "uncompress", _("Decompression of svndiff data failed"))); /* Zlib should not produce something that has a different size than the original length we stored. */ if (zlen != len) return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL, _("Size of uncompressed data " "does not match stored original length")); out->data[zlen] = 0; out->len = zlen; } return SVN_NO_ERROR; }
svn_error_t * svn_fs_x__parse_properties(apr_hash_t **properties, const svn_string_t *content, apr_pool_t *result_pool) { const apr_byte_t *p = (const apr_byte_t *)content->data; const apr_byte_t *end = p + content->len; apr_uint64_t count; *properties = apr_hash_make(result_pool); /* Extract the number of properties we are expected to read. */ p = svn__decode_uint(&count, p, end); /* Read all the properties we find. Because prop-name and prop-value are nicely NUL-terminated sub-strings of CONTENT, we can simply reference them there. I.e. there is no need to copy them around. */ while (p < end) { apr_uint64_t value_len; svn_string_t *value; const char *key = (const char *)p; /* Note that this may never overflow / segfault because CONTENT itself is NUL-terminated. */ apr_size_t key_len = strlen(key); p += key_len + 1; if (key[key_len]) return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, "Property name not NUL terminated"); if (p >= end) return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, "Property value missing"); p = svn__decode_uint(&value_len, p, end); if (value_len >= (end - p)) return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, "Property value too long"); value = apr_pcalloc(result_pool, sizeof(*value)); value->data = (const char *)p; value->len = (apr_size_t)value_len; if (p[value->len]) return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, "Property value not NUL terminated"); p += value->len + 1; apr_hash_set(*properties, key, key_len, value); } /* Check that we read the expected number of properties. */ if ((apr_uint64_t)apr_hash_count(*properties) != count) return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, "Property count mismatch"); return SVN_NO_ERROR; }