This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "".
The branch, caterpillar has been updated via f87bb5146fb25f7cd62246500cb4f7e625375654 (commit) from 987d61cab96bbc7bb6a144d8b9f5250c076c1328 (commit)
Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below.
- Log ----------------------------------------------------------------- commit f87bb5146fb25f7cd62246500cb4f7e625375654 Author: Balakrishna Garapati balakrishna.garapati@linaro.org Date: Wed Nov 29 15:08:04 2017 +0100
linux-dpdk: crypto support for cipher & auth only feature
Signed-off-by: Balakrishna Garapati balakrishna.garapati@linaro.org Reviewed-by: Bill Fischofer bill.fischofer@linaro.org Signed-off-by: Yi He yi.he@linaro.org
diff --git a/platform/linux-dpdk/odp_crypto.c b/platform/linux-dpdk/odp_crypto.c index ea014c8e..6bffa112 100644 --- a/platform/linux-dpdk/odp_crypto.c +++ b/platform/linux-dpdk/odp_crypto.c @@ -31,6 +31,14 @@ #define MAX_SESSIONS 2048 #define NB_MBUF 8192
+enum crypto_chain_order { + CRYPTO_CHAIN_ONLY_CIPHER, + CRYPTO_CHAIN_ONLY_AUTH, + CRYPTO_CHAIN_CIPHER_AUTH, + CRYPTO_CHAIN_AUTH_CIPHER, + CRYPTO_CHAIN_NOT_SUPPORTED +}; + typedef struct crypto_session_entry_s crypto_session_entry_t; struct crypto_session_entry_s { struct crypto_session_entry_s *next; @@ -108,7 +116,8 @@ static int cipher_alg_odp_to_rte(odp_cipher_alg_t cipher_alg, }
static int auth_alg_odp_to_rte(odp_auth_alg_t auth_alg, - struct rte_crypto_sym_xform *auth_xform) + struct rte_crypto_sym_xform *auth_xform, + uint32_t auth_digest_len) { int rc = 0;
@@ -116,36 +125,46 @@ static int auth_alg_odp_to_rte(odp_auth_alg_t auth_alg, switch (auth_alg) { case ODP_AUTH_ALG_NULL: auth_xform->auth.algo = RTE_CRYPTO_AUTH_NULL; + auth_xform->auth.digest_length = auth_digest_len; break; - case ODP_AUTH_ALG_MD5_HMAC: #if ODP_DEPRECATED_API case ODP_AUTH_ALG_MD5_96: -#endif auth_xform->auth.algo = RTE_CRYPTO_AUTH_MD5_HMAC; auth_xform->auth.digest_length = 12; break; - case ODP_AUTH_ALG_SHA256_HMAC: +#endif + case ODP_AUTH_ALG_MD5_HMAC: + auth_xform->auth.algo = RTE_CRYPTO_AUTH_MD5_HMAC; + auth_xform->auth.digest_length = auth_digest_len; + break; #if ODP_DEPRECATED_API case ODP_AUTH_ALG_SHA256_128: -#endif auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA256_HMAC; auth_xform->auth.digest_length = 16; break; +#endif + case ODP_AUTH_ALG_SHA256_HMAC: + auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA256_HMAC; + auth_xform->auth.digest_length = auth_digest_len; + break; case ODP_AUTH_ALG_SHA1_HMAC: auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA1_HMAC; - auth_xform->auth.digest_length = 20; + auth_xform->auth.digest_length = auth_digest_len; break; case ODP_AUTH_ALG_SHA512_HMAC: auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA512_HMAC; - auth_xform->auth.digest_length = 64; + auth_xform->auth.digest_length = auth_digest_len; break; - case ODP_AUTH_ALG_AES_GCM: #if ODP_DEPRECATED_API case ODP_AUTH_ALG_AES128_GCM: -#endif auth_xform->auth.algo = RTE_CRYPTO_AUTH_AES_GCM; auth_xform->auth.digest_length = 16; break; +#endif + case ODP_AUTH_ALG_AES_GCM: + auth_xform->auth.algo = RTE_CRYPTO_AUTH_AES_GCM; + auth_xform->auth.digest_length = auth_digest_len; + break; default: rc = -1; } @@ -538,9 +557,9 @@ int odp_crypto_cipher_capability(odp_cipher_alg_t cipher, i = 0; cap = &dev_info.capabilities[i]; while (cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED) { - cap_cipher_algo = cap->sym.cipher.algo; if (cap->sym.xform_type == RTE_CRYPTO_SYM_XFORM_CIPHER) { + cap_cipher_algo = cap->sym.cipher.algo; if (cap_cipher_algo == cipher_xform.cipher.algo) break; } @@ -594,7 +613,7 @@ int odp_crypto_auth_capability(odp_auth_alg_t auth, enum rte_crypto_auth_algorithm cap_auth_algo; struct rte_crypto_sym_xform auth_xform;
- rc = auth_alg_odp_to_rte(auth, &auth_xform); + rc = auth_alg_odp_to_rte(auth, &auth_xform, 0);
/* Check result */ if (rc) @@ -615,7 +634,7 @@ int odp_crypto_auth_capability(odp_auth_alg_t auth, while (cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED) { cap_auth_algo = cap->sym.auth.algo; if (cap->sym.xform_type == - RTE_CRYPTO_SYM_XFORM_CIPHER) { + RTE_CRYPTO_SYM_XFORM_AUTH) { if (cap_auth_algo == auth_xform.auth.algo) break; } @@ -663,76 +682,103 @@ int odp_crypto_auth_capability(odp_auth_alg_t auth, return idx; }
-static int get_crypto_dev(struct rte_crypto_sym_xform *cipher_xform, - struct rte_crypto_sym_xform *auth_xform, +static int get_crypto_dev(struct rte_crypto_sym_xform *first_xform, + enum crypto_chain_order order, uint16_t iv_length, uint8_t *dev_id) { uint8_t cdev_id, id; const struct rte_cryptodev_capabilities *cap; + struct rte_crypto_sym_xform *auth_xform = NULL; + struct rte_crypto_sym_xform *cipher_xform = NULL; enum rte_crypto_cipher_algorithm cap_cipher_algo; enum rte_crypto_auth_algorithm cap_auth_algo; enum rte_crypto_cipher_algorithm app_cipher_algo; enum rte_crypto_auth_algorithm app_auth_algo;
+ switch (order) { + case CRYPTO_CHAIN_ONLY_CIPHER: + cipher_xform = first_xform; + break; + case CRYPTO_CHAIN_ONLY_AUTH: + auth_xform = first_xform; + break; + case CRYPTO_CHAIN_CIPHER_AUTH: + cipher_xform = first_xform; + auth_xform = first_xform->next; + break; + case CRYPTO_CHAIN_AUTH_CIPHER: + auth_xform = first_xform; + cipher_xform = first_xform->next; + break; + default: + return -1; + } + for (id = 0; id < global->enabled_crypto_devs; id++) { struct rte_cryptodev_info dev_info; int i = 0;
cdev_id = global->enabled_crypto_dev_ids[id]; rte_cryptodev_info_get(cdev_id, &dev_info); - app_cipher_algo = cipher_xform->cipher.algo; cap = &dev_info.capabilities[i]; - while (cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED) { - cap_cipher_algo = cap->sym.cipher.algo; + while (cipher_xform && cap->op != + RTE_CRYPTO_OP_TYPE_UNDEFINED) { if (cap->sym.xform_type == RTE_CRYPTO_SYM_XFORM_CIPHER) { + app_cipher_algo = cipher_xform->cipher.algo; + cap_cipher_algo = cap->sym.cipher.algo; if (cap_cipher_algo == app_cipher_algo) break; } - cap = &dev_info.capabilities[++i]; + cap = &dev_info.capabilities[++i]; }
if (cap->op == RTE_CRYPTO_OP_TYPE_UNDEFINED) continue;
- /* Check if key size is supported by the algorithm. */ - if (cipher_xform->cipher.key.length) { - if (is_valid_size(cipher_xform->cipher.key.length, - cap->sym.cipher.key_size.min, - cap->sym.cipher.key_size.max, - cap->sym.cipher.key_size. - increment) != 0) { - ODP_ERR("Unsupported cipher key length\n"); - return -1; - } - /* No size provided, use minimum size. */ - } else - cipher_xform->cipher.key.length = - cap->sym.cipher.key_size.min; + if (cipher_xform) { + /* Check if key size is supported by the algorithm. */ + if (cipher_xform->cipher.key.length) { + if (is_valid_size( + cipher_xform->cipher.key.length, + cap->sym.cipher.key_size.min, + cap->sym.cipher.key_size.max, + cap->sym.cipher.key_size. + increment) != 0) { + ODP_ERR("Invalid cipher key length\n"); + return -1; + } + /* No size provided, use minimum size. */ + } else + cipher_xform->cipher.key.length = + cap->sym.cipher.key_size.min;
- /* Check if iv length is supported by the algorithm. */ - if (iv_length) { + /* Check if iv length is supported by the algorithm. */ if (is_valid_size(iv_length, cap->sym.cipher.iv_size.min, cap->sym.cipher.iv_size.max, cap->sym.cipher.iv_size. increment) != 0) { - ODP_ERR("Unsupported iv length\n"); + ODP_ERR("Invalid iv length\n"); return -1; } }
+ if (cipher_xform && !auth_xform) { + memcpy(dev_id, &cdev_id, sizeof(cdev_id)); + return 0; + } + i = 0; - app_auth_algo = auth_xform->auth.algo; cap = &dev_info.capabilities[i]; - while (cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED) { - cap_auth_algo = cap->sym.auth.algo; + while (auth_xform && cap->op != RTE_CRYPTO_OP_TYPE_UNDEFINED) { if ((cap->sym.xform_type == - RTE_CRYPTO_SYM_XFORM_AUTH) & - (cap_auth_algo == app_auth_algo)) { - break; + RTE_CRYPTO_SYM_XFORM_AUTH)) { + app_auth_algo = auth_xform->auth.algo; + cap_auth_algo = cap->sym.auth.algo; + if (cap_auth_algo == app_auth_algo) + break; } - cap = &dev_info.capabilities[++i]; }
@@ -776,6 +822,43 @@ static int get_crypto_dev(struct rte_crypto_sym_xform *cipher_xform, return -1; }
+static enum crypto_chain_order set_chain_order( + odp_bool_t do_cipher_first, + struct rte_crypto_sym_xform **first_xform, + struct rte_crypto_sym_xform *auth_xform, + struct rte_crypto_sym_xform *cipher_xform) +{ + /* Process based on cipher */ + /* Derive order */ + if (do_cipher_first) { + if (auth_xform->auth.algo != RTE_CRYPTO_AUTH_NULL && + cipher_xform->cipher.algo != RTE_CRYPTO_CIPHER_NULL) { + *first_xform = cipher_xform; + (*first_xform)->next = auth_xform; + return CRYPTO_CHAIN_CIPHER_AUTH; + } + } else { + if (auth_xform->auth.algo != RTE_CRYPTO_AUTH_NULL && + cipher_xform->cipher.algo != RTE_CRYPTO_CIPHER_NULL) { + *first_xform = auth_xform; + (*first_xform)->next = cipher_xform; + return CRYPTO_CHAIN_AUTH_CIPHER; + } + } + + if (auth_xform->auth.algo == RTE_CRYPTO_AUTH_NULL) { + *first_xform = cipher_xform; + (*first_xform)->next = NULL; + return CRYPTO_CHAIN_ONLY_CIPHER; + } else if (cipher_xform->cipher.algo == RTE_CRYPTO_CIPHER_NULL) { + *first_xform = auth_xform; + (*first_xform)->next = NULL; + return CRYPTO_CHAIN_ONLY_AUTH; + } + + return CRYPTO_CHAIN_NOT_SUPPORTED; +} + int odp_crypto_session_create(odp_crypto_session_param_t *param, odp_crypto_session_t *session_out, odp_crypto_ses_create_err_t *status) @@ -784,8 +867,9 @@ int odp_crypto_session_create(odp_crypto_session_param_t *param, uint8_t cdev_id = 0; struct rte_crypto_sym_xform cipher_xform; struct rte_crypto_sym_xform auth_xform; - struct rte_crypto_sym_xform *first_xform; + struct rte_crypto_sym_xform *first_xform = NULL; struct rte_cryptodev_sym_session *session; + enum crypto_chain_order order = CRYPTO_CHAIN_NOT_SUPPORTED; crypto_session_entry_t *entry;
*session_out = ODP_CRYPTO_SESSION_INVALID; @@ -810,6 +894,16 @@ int odp_crypto_session_create(odp_crypto_session_param_t *param,
cipher_xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER; cipher_xform.next = NULL; + rc = cipher_alg_odp_to_rte(param->cipher_alg, &cipher_xform); + + /* Check result */ + if (rc) { + *status = ODP_CRYPTO_SES_CREATE_ERR_INV_CIPHER; + /* remove the crypto_session_entry_t */ + memset(entry, 0, sizeof(*entry)); + free_session(entry); + return -1; + }
if (param->cipher_key.length) { /* Cipher Data */ @@ -828,12 +922,24 @@ int odp_crypto_session_create(odp_crypto_session_param_t *param, param->cipher_key.data, param->cipher_key.length); } else { - cipher_xform.cipher.key.data = 0; cipher_xform.cipher.key.length = 0; + cipher_xform.cipher.key.data = 0; }
auth_xform.type = RTE_CRYPTO_SYM_XFORM_AUTH; auth_xform.next = NULL; + rc = auth_alg_odp_to_rte(param->auth_alg, + &auth_xform, + param->auth_digest_len); + + /* Check result */ + if (rc) { + *status = ODP_CRYPTO_SES_CREATE_ERR_INV_AUTH; + /* remove the crypto_session_entry_t */ + memset(entry, 0, sizeof(*entry)); + free_session(entry); + return -1; + }
if (param->auth_key.length) { /* Authentication Data */ @@ -857,46 +963,30 @@ int odp_crypto_session_create(odp_crypto_session_param_t *param,
/* Derive order */ - if (ODP_CRYPTO_OP_ENCODE == param->op) - entry->do_cipher_first = param->auth_cipher_text; - else - entry->do_cipher_first = !param->auth_cipher_text; - - /* Process based on cipher */ - /* Derive order */ - if (entry->do_cipher_first) { + if (ODP_CRYPTO_OP_ENCODE == param->op) { cipher_xform.cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT; auth_xform.auth.op = RTE_CRYPTO_AUTH_OP_GENERATE; - first_xform = &cipher_xform; - first_xform->next = &auth_xform; + entry->do_cipher_first = param->auth_cipher_text; } else { cipher_xform.cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT; auth_xform.auth.op = RTE_CRYPTO_AUTH_OP_VERIFY; - first_xform = &auth_xform; - first_xform->next = &cipher_xform; - } - - rc = cipher_alg_odp_to_rte(param->cipher_alg, &cipher_xform); - - /* Check result */ - if (rc) { - *status = ODP_CRYPTO_SES_CREATE_ERR_INV_CIPHER; - return -1; + entry->do_cipher_first = !param->auth_cipher_text; }
- rc = auth_alg_odp_to_rte(param->auth_alg, &auth_xform); - - /* Check result */ - if (rc) { - *status = ODP_CRYPTO_SES_CREATE_ERR_INV_AUTH; + order = set_chain_order(entry->do_cipher_first, + &first_xform, + &auth_xform, + &cipher_xform); + if (order == CRYPTO_CHAIN_NOT_SUPPORTED) { + ODP_ERR("Couldn't set chain order"); /* remove the crypto_session_entry_t */ memset(entry, 0, sizeof(*entry)); free_session(entry); return -1; }
- rc = get_crypto_dev(&cipher_xform, - &auth_xform, + rc = get_crypto_dev(first_xform, + order, param->iv.length, &cdev_id);
@@ -1269,15 +1359,13 @@ int odp_crypto_int(odp_packet_t pkt_in, op->sym->auth.aad.length = aad_len; }
- if (entry->iv.length == 0) { - ODP_ERR("Wrong IV length"); - goto err_op_free; - }
- op->sym->cipher.iv.data = rte_malloc("iv", entry->iv.length, 0); - if (op->sym->cipher.iv.data == NULL) { - ODP_ERR("Failed to allocate memory for IV"); - goto err_op_free; + if (entry->iv.length) { + op->sym->cipher.iv.data = rte_malloc("iv", entry->iv.length, 0); + if (op->sym->cipher.iv.data == NULL) { + ODP_ERR("Failed to allocate memory for IV"); + goto err_op_free; + } }
if (param->override_iv_ptr) { diff --git a/platform/linux-dpdk/test/wrapper-script.sh b/platform/linux-dpdk/test/wrapper-script.sh index 1fbb16f7..ee0ed9cd 100755 --- a/platform/linux-dpdk/test/wrapper-script.sh +++ b/platform/linux-dpdk/test/wrapper-script.sh @@ -1,6 +1,7 @@ #!/bin/bash
-export ODP_PLATFORM_PARAMS=${ODP_PLATFORM_PARAMS:--n 4 --vdev "crypto_openssl"} +export ODP_PLATFORM_PARAMS=${ODP_PLATFORM_PARAMS:--n 4 "--vdev \ +"crypto_openssl" --vdev "crypto_null""} # where to mount huge pages export HUGEPAGEDIR=${HUGEPAGEDIR:-/mnt/huge}
diff --git a/platform/linux-generic/odp_ipsec_sad.c b/platform/linux-generic/odp_ipsec_sad.c index f0b5b9e4..7ac3a65c 100644 --- a/platform/linux-generic/odp_ipsec_sad.c +++ b/platform/linux-generic/odp_ipsec_sad.c @@ -303,6 +303,7 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param) case ODP_CIPHER_ALG_3DES_CBC: ipsec_sa->esp_iv_len = 8; ipsec_sa->esp_block_len = 8; + crypto_param.iv.length = 8; break; #if ODP_DEPRECATED_API case ODP_CIPHER_ALG_AES128_CBC: @@ -310,6 +311,7 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param) case ODP_CIPHER_ALG_AES_CBC: ipsec_sa->esp_iv_len = 16; ipsec_sa->esp_block_len = 16; + crypto_param.iv.length = 16; break; #if ODP_DEPRECATED_API case ODP_CIPHER_ALG_AES128_GCM: diff --git a/test/performance/odp_crypto.c b/test/performance/odp_crypto.c index 0cbc2754..f5722067 100644 --- a/test/performance/odp_crypto.c +++ b/test/performance/odp_crypto.c @@ -429,6 +429,7 @@ create_session_from_config(odp_crypto_session_t *session, odp_crypto_session_param_init(¶ms); memcpy(¶ms, &config->session, sizeof(odp_crypto_session_param_t)); params.op = ODP_CRYPTO_OP_ENCODE; + params.auth_cipher_text = 1; params.pref_mode = ODP_CRYPTO_SYNC;
/* Lookup the packet pool */ diff --git a/test/validation/api/ipsec/ipsec.c b/test/validation/api/ipsec/ipsec.c index a8fdf2b1..824ac6ef 100644 --- a/test/validation/api/ipsec/ipsec.c +++ b/test/validation/api/ipsec/ipsec.c @@ -119,7 +119,8 @@ static void pktio_stop(odp_pktio_t pktio) int ipsec_check(odp_bool_t ah, odp_cipher_alg_t cipher, uint32_t cipher_bits, - odp_auth_alg_t auth) + odp_auth_alg_t auth, + uint32_t auth_bits) { odp_ipsec_capability_t capa; odp_crypto_cipher_capability_t cipher_capa[MAX_ALG_CAPA]; @@ -220,52 +221,65 @@ int ipsec_check(odp_bool_t ah, }
if (!found) { - fprintf(stderr, "Unsupported key length\n"); + fprintf(stderr, "Unsupported cipher key length\n"); return ODP_TEST_INACTIVE; }
+ found = false; num = odp_ipsec_auth_capability(auth, auth_capa, MAX_ALG_CAPA); if (num <= 0) { fprintf(stderr, "Wrong auth capabilities\n"); return ODP_TEST_INACTIVE; }
+ for (i = 0; i < num; i++) { + if (auth_capa[i].key_len == auth_bits / 8) { + found = 1; + break; + } + } + + if (!found) { + fprintf(stderr, "Unsupported auth key length\n"); + return ODP_TEST_INACTIVE; + } + return ODP_TEST_ACTIVE; }
int ipsec_check_ah_sha256(void) { - return ipsec_check_ah(ODP_AUTH_ALG_SHA256_HMAC); + return ipsec_check_ah(ODP_AUTH_ALG_SHA256_HMAC, 256); }
int ipsec_check_esp_null_sha256(void) { return ipsec_check_esp(ODP_CIPHER_ALG_NULL, 0, - ODP_AUTH_ALG_SHA256_HMAC); + ODP_AUTH_ALG_SHA256_HMAC, 256); }
int ipsec_check_esp_aes_cbc_128_null(void) { return ipsec_check_esp(ODP_CIPHER_ALG_AES_CBC, 128, - ODP_AUTH_ALG_NULL); + ODP_AUTH_ALG_NULL, 0); }
int ipsec_check_esp_aes_cbc_128_sha256(void) { return ipsec_check_esp(ODP_CIPHER_ALG_AES_CBC, 128, - ODP_AUTH_ALG_SHA256_HMAC); + ODP_AUTH_ALG_SHA256_HMAC, 256); }
int ipsec_check_esp_aes_gcm_128(void) { return ipsec_check_esp(ODP_CIPHER_ALG_AES_GCM, 128, - ODP_AUTH_ALG_AES_GCM); + ODP_AUTH_ALG_AES_GCM, 128); }
int ipsec_check_esp_aes_gcm_256(void) { return ipsec_check_esp(ODP_CIPHER_ALG_AES_GCM, 256, - ODP_AUTH_ALG_AES_GCM); + ODP_AUTH_ALG_AES_GCM, 256); }
void ipsec_sa_param_fill(odp_ipsec_sa_param_t *param, diff --git a/test/validation/api/ipsec/ipsec.h b/test/validation/api/ipsec/ipsec.h index d1c6854b..3a35ed61 100644 --- a/test/validation/api/ipsec/ipsec.h +++ b/test/validation/api/ipsec/ipsec.h @@ -74,16 +74,16 @@ void ipsec_check_out_in_one(const ipsec_test_part *part, int ipsec_check(odp_bool_t ah, odp_cipher_alg_t cipher, uint32_t cipher_bits, - odp_auth_alg_t auth); -#define ipsec_check_ah(auth) \ - ipsec_check(true, ODP_CIPHER_ALG_NULL, 0, auth) -#define ipsec_check_esp(cipher, cipher_bits, auth) \ - ipsec_check(false, cipher, cipher_bits, auth) + odp_auth_alg_t auth, + uint32_t auth_bits); +#define ipsec_check_ah(auth, auth_bits) \ + ipsec_check(true, ODP_CIPHER_ALG_NULL, 0, auth, auth_bits) +#define ipsec_check_esp(cipher, cipher_bits, auth, auth_bits) \ + ipsec_check(false, cipher, cipher_bits, auth, auth_bits) int ipsec_check_ah_sha256(void); int ipsec_check_esp_null_sha256(void); int ipsec_check_esp_aes_cbc_128_null(void); int ipsec_check_esp_aes_cbc_128_sha256(void); int ipsec_check_esp_aes_gcm_128(void); int ipsec_check_esp_aes_gcm_256(void); - #endif
-----------------------------------------------------------------------
Summary of changes: platform/linux-dpdk/odp_crypto.c | 246 ++++++++++++++++++++--------- platform/linux-dpdk/test/wrapper-script.sh | 3 +- platform/linux-generic/odp_ipsec_sad.c | 2 + test/performance/odp_crypto.c | 1 + test/validation/api/ipsec/ipsec.c | 30 +++- test/validation/api/ipsec/ipsec.h | 12 +- 6 files changed, 200 insertions(+), 94 deletions(-)
hooks/post-receive