static int collection_store_ex()

in apache2/persist_dbm.c [505:899]


static int collection_store_ex(int db_option, modsec_rec *msr, apr_table_t *col){
    msc_string *var_name = NULL, *var_key = NULL;
    unsigned char *blob = NULL;
    unsigned int blob_size = 0, blob_offset = 0;
    int rc;
    apr_sdbm_datum_t key;
    apr_sdbm_datum_t value;
    const apr_array_header_t *arr;
    apr_table_entry_t *te;
    int i;
    const apr_table_t *stored_col = NULL;
    const apr_table_t *orig_col = NULL;

    directory_config *dcfg = msr->dcfg1;
#ifdef MEMORY_DATABASE_ENABLE
    directory_config *root_dcfg = dcfg->root_config;

    //variable used for AGMDB
    struct agmdb_handler *ag_dbm = NULL;
    struct agmdb_handle_entry *new_handle;
#endif
    //variable used for apr_sdbm
    char *dbm_filename = NULL;
    apr_sdbm_t *apr_dbm = NULL;

    //---------------------------------
    //prepare collection name and key
    //---------------------------------
    var_name = (msc_string *)apr_table_get(col, "__name");
    if (var_name == NULL) {
        goto error;
    }

    var_key = (msc_string *)apr_table_get(col, "__key");
    if (var_key == NULL) {
        goto error;
    }

    if (msr->txcfg->data_dir == NULL) {
        msr_log(msr, 1, "collection_store: Unable to store collection (name \"%s\", key \"%s\"). Use "
            "SecDataDir to define data directory first.", log_escape_ex(msr->mp, var_name->value, var_name->value_len),
            log_escape_ex(msr->mp, var_key->value, var_key->value_len));
        goto error;
    }

    /* Delete IS_NEW on store. */
    apr_table_unset(col, "IS_NEW");

    /* Delete UPDATE_RATE on store to save space as it is calculated */
    apr_table_unset(col, "UPDATE_RATE");

    //---------------------------------
    // Update the timeout value. 
    //---------------------------------
    {
        msc_string *var = (msc_string *)apr_table_get(col, "TIMEOUT");
        if (var != NULL) {
            int timeout = atoi(var->value);
            var = (msc_string *)apr_table_get(col, "__expire_KEY");
            if (var != NULL) {
                var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()) + timeout));
                var->value_len = strlen(var->value);
            }
        }
    }

    //---------------------------------
    // LAST_UPDATE_TIME 
    //---------------------------------
    {
        msc_string *var = (msc_string *)apr_table_get(col, "LAST_UPDATE_TIME");
        if (var == NULL) {
            var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
            var->name = "LAST_UPDATE_TIME";
            var->name_len = strlen(var->name);
            apr_table_setn(col, var->name, (void *)var);
        }
        var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now())));
        var->value_len = strlen(var->value);
    }

    //---------------------------------
    // UPDATE_COUNTER 
    //---------------------------------
    {
        msc_string *var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER");
        int counter = 0;
        if (var == NULL) {
            var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
            var->name = "UPDATE_COUNTER";
            var->name_len = strlen(var->name);
            apr_table_setn(col, var->name, (void *)var);
        } else {
            counter = atoi(var->value);
        }
        var->value = apr_psprintf(msr->mp, "%d", counter + 1);
        var->value_len = strlen(var->value);
    }

    //---------------------------------
    //open database
    //---------------------------------
#ifdef MEMORY_DATABASE_ENABLE
    if(db_option == DB_OPT_AGMDB){
        ag_dbm = dcfg_searchAGMDBhandler(var_name->value, (struct agmdb_handle_entry*)(msr->dcfg1->agmdb_handles));
        if(ag_dbm == NULL) {
            //Create the DB
            root_dcfg = msr->dcfg1->root_config;
#ifdef _WIN32           
            dbm_filename = apr_pstrcat(root_dcfg->mp, "Global\\", root_dcfg->data_dir, "/", var_name->value, NULL);
#else
            dbm_filename = apr_pstrcat(root_dcfg->mp, root_dcfg->data_dir, "/", var_name->value, NULL);
#endif
            if(root_dcfg == NULL){
                msr_log(msr, 1, "collection_retrieve_ex_agmdb: Cannot find root_config in msr->dcfg1.");
                goto error;
            }
            new_handle = (struct agmdb_handle_entry *)apr_pcalloc(root_dcfg->mp, sizeof(struct agmdb_handle_entry));
            new_handle->col_name = (char*)apr_pcalloc(root_dcfg->mp, var_name->value_len);
            new_handle->handle = apr_pcalloc(root_dcfg->mp, sizeof(struct agmdb_handler));
            strcpy((char*)(new_handle->col_name), var_name->value);
            
            rc = AGMDB_openDB(new_handle->handle, dbm_filename, strlen(dbm_filename), MAXIMUM_AGMDB_ENTRY_NUM);
            if(AGMDB_isError(rc)){
                msr_log(msr, 1, "collection_retrieve_ex_agmdb: Failed to create DBM name: %s. Error info: %s", dbm_filename, AGMDB_getErrorInfo(rc));
                goto error;
            }
            ag_dbm = new_handle->handle;
            dcfg_insertAGMDBhandler(root_dcfg, new_handle);
        }
    }
    else{
#endif
        // ENH: lowercase the var name in the filename
        dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL);
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "collection_store_ex_origin: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, var_name->value),
                    log_escape(msr->mp, dbm_filename));
        }
        
#ifdef GLOBAL_COLLECTION_LOCK
        /* Need to lock to pull in the stored data again and apply deltas. */
	rc = waf_get_exclusive_lock(msr->modsecurity->dbm_lock);
	if (waf_lock_is_error(rc)) {
            msr_log(msr, 1, "collection_store: Failed to lock proc mutex: %s",
                    get_apr_error(msr->mp, rc));
            goto error;
        }
#endif
        
        rc = apr_sdbm_open(&apr_dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
            CREATEMODE, msr->mp);
        if (rc != APR_SUCCESS) {
#ifdef GLOBAL_COLLECTION_LOCK
	        waf_free_exclusive_lock(msr->modsecurity->dbm_lock);
#endif
            msr_log(msr, 1, "collection_store_ex_origin: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
                get_apr_error(msr->mp, rc));
            apr_dbm = NULL;
            goto error;
        }
#ifdef MEMORY_DATABASE_ENABLE
    }
#endif

    //---------------------------------
    //Lock and prepare to get the original collection
    //---------------------------------
#ifdef MEMORY_DATABASE_ENABLE
    if(db_option == DB_OPT_AGMDB){
        rc = AGMDB_getExclusiveLock(ag_dbm);
        if (AGMDB_isError(rc)) {
#ifdef _WIN32
            int lasterr = (int)GetLastError();
            msr_log(msr, 1, "collection_store: Failed to getExclusiveLock, lasterr = %d. Error info: %s", lasterr, AGMDB_getErrorInfo(rc));
#else
            msr_log(msr, 1, "collection_store: Failed to getExclusiveLock, errno = %d. Error info: %s", errno, AGMDB_getErrorInfo(rc));
#endif
            goto error;
        }
    }
    else{
#endif 
#ifndef GLOBAL_COLLECTION_LOCK
        /* Need to lock to pull in the stored data again and apply deltas. */
        rc = apr_sdbm_lock(apr_dbm, APR_FLOCK_EXCLUSIVE);
        if (rc != APR_SUCCESS) {
            msr_log(msr, 1, "collection_store_ex_origin: Failed to exclusivly lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
                get_apr_error(msr->mp, rc));
            goto error;
        }
#endif
#ifdef MEMORY_DATABASE_ENABLE
    }
#endif

    //---------------------------------
    //get the origin record
    //---------------------------------
    orig_col = (const apr_table_t *)apr_table_get(msr->collections_original, var_name->value);
    if (orig_col != NULL) {
        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "collection_store: Re-retrieving collection prior to store: %s",
                apr_psprintf(msr->mp, "%.*s", var_name->value_len, var_name->value));
        }
#ifdef MEMORY_DATABASE_ENABLE
        if(db_option == DB_OPT_AGMDB)
            stored_col = (const apr_table_t *)collection_retrieve_ex(db_option, ag_dbm, msr, var_name->value, var_key->value, var_key->value_len);
        else
#endif
            stored_col = (const apr_table_t *)collection_retrieve_ex(db_option, apr_dbm, msr, var_name->value, var_key->value, var_key->value_len);
    }

    //---------------------------------
    //pack the collection
    //---------------------------------
    /* Merge deltas and calculate the size first. */
    blob_size = 3 + 2;
    arr = apr_table_elts(col);
    te = (apr_table_entry_t *)arr->elts;
    for (i = 0; i < arr->nelts; i++) {
        msc_string *var = (msc_string *)te[i].val;
        int len;

        /* If there is an original value, then apply the delta
         * to the latest stored value */
        if (stored_col != NULL) {
            const msc_string *orig_var = (const msc_string *)apr_table_get(orig_col, var->name);
            if (orig_var != NULL) {
                const msc_string *stored_var = (const msc_string *)apr_table_get(stored_col, var->name);
                if (stored_var != NULL) {
                    int origval = atoi(orig_var->value);
                    int ourval = atoi(var->value);
                    int storedval = atoi(stored_var->value);
                    int delta = ourval - origval;
                    int newval = storedval + delta;

                    if (newval < 0) newval = 0; /* Counters never go below zero. */

                    var->value = apr_psprintf(msr->mp, "%d", newval);
                    var->value_len = strlen(var->value);

                    if (msr->txcfg->debuglog_level >= 9) {
                        msr_log(msr, 9, "collection_store: Delta applied for %s.%s %d->%d (%d): %d + (%d) = %d [%s,%d]",
                        log_escape_ex(msr->mp, var_name->value, var_name->value_len),
                        log_escape_ex(msr->mp, var->name, var->name_len),
                        origval, ourval, delta, storedval, delta, newval, var->value, var->value_len);
                    }
                }
            }
        }

        len = var->name_len + 1;
        if (len >= 65536) len = 65536;
        blob_size += len + 2;

        len = var->value_len + 1;
        if (len >= 65536) len = 65536;
        blob_size += len + 2;
    }

    /* Now generate the binary object. */
#ifdef MEMORY_DATABASE_ENABLE
    if(db_option == DB_OPT_AGMDB){
        blob = apr_pcalloc(msr->mp, blob_size);
        if (blob == NULL) {
            if (ag_dbm != NULL) {
                rc = AGMDB_freeExclusiveLock(ag_dbm);
                if(AGMDB_isError(rc))
                    msr_log(msr, 1, "collection_stror_ex: Fail to free exclusive lock. Error info: %s", AGMDB_getErrorInfo(rc));
            }
            msr_log(msr, 1, "collection_store_ex_agdb: fail to create blob");            
            return -1;
        }
    }
    else{
#endif
        blob = apr_pcalloc(msr->mp, blob_size);
        if (blob == NULL) {
            if (apr_dbm != NULL) {
#ifdef GLOBAL_COLLECTION_LOCK
                apr_sdbm_close(apr_dbm);
	            waf_free_exclusive_lock(msr->modsecurity->dbm_lock);
#else
                apr_sdbm_unlock(apr_dbm);
                apr_sdbm_close(apr_dbm);
#endif
            }
            return -1;
        }
#ifdef MEMORY_DATABASE_ENABLE
    }
#endif

    blob[0] = 0x49;
    blob[1] = 0x52;
    blob[2] = 0x01;

    blob_offset = 3;
    arr = apr_table_elts(col);
    te = (apr_table_entry_t *)arr->elts;
    for (i = 0; i < arr->nelts; i++) {
        msc_string *var = (msc_string *)te[i].val;
        int len;

        len = var->name_len + 1;
        if (len >= 65536) len = 65536;

        blob[blob_offset + 0] = (len & 0xff00) >> 8;
        blob[blob_offset + 1] = len & 0x00ff;
        memcpy(blob + blob_offset + 2, var->name, len - 1);
        blob[blob_offset + 2 + len - 1] = '\0';
        blob_offset += 2 + len;

        len = var->value_len + 1;
        if (len >= 65536) len = 65536;

        blob[blob_offset + 0] = (len & 0xff00) >> 8;
        blob[blob_offset + 1] = len & 0x00ff;
        memcpy(blob + blob_offset + 2, var->value, len - 1);
        blob[blob_offset + 2 + len - 1] = '\0';
        blob_offset += 2 + len;

        if (msr->txcfg->debuglog_level >= 9) {
            msr_log(msr, 9, "collection_store: Wrote variable: name \"%s\", value \"%s\".",
                log_escape_ex(msr->mp, var->name, var->name_len),
                log_escape_ex(msr->mp, var->value, var->value_len));
        }
    }

    blob[blob_offset] = 0;
    blob[blob_offset + 1] = 0;

    //---------------------------------
    // And, finally, store it. 
    //---------------------------------
    key.dptr = var_key->value;
    key.dsize = var_key->value_len + 1;

    value.dptr = (char *)blob;
    value.dsize = blob_size;

#ifdef MEMORY_DATABASE_ENABLE
    if(db_option == DB_OPT_AGMDB) {
        rc = AGMDB_set(ag_dbm, var_key->value, var_key->value_len, (char*)blob, blob_size);
        if(AGMDB_isError(rc)) {
            msr_log(msr, 1, "collection_store_ex_agmdb: Failed to write to database key: %s. Error info: %s.", var_key->value, AGMDB_getErrorInfo(rc));
        }

        rc = AGMDB_freeExclusiveLock(ag_dbm);
        if(AGMDB_isError(rc)){
            msr_log(msr, 1, "collection_store_ex_agmdb: Failed to free exclusive lock. Error info: %s.", AGMDB_getErrorInfo(rc));
            return -1;
        }
    }
    else {
#endif
        rc = apr_sdbm_store(apr_dbm, key, value, APR_SDBM_REPLACE);
        if (rc != APR_SUCCESS) {
            msr_log(msr, 1, "collection_store: Failed to write to DBM file \"%s\": %s", dbm_filename,
                    get_apr_error(msr->mp, rc));
            if (apr_dbm != NULL) {
#ifdef GLOBAL_COLLECTION_LOCK
                apr_sdbm_close(apr_dbm);
	            waf_free_exclusive_lock(msr->modsecurity->dbm_lock);
#else
                apr_sdbm_unlock(apr_dbm);
                apr_sdbm_close(apr_dbm);
#endif
            }

            return -1;
        }
#ifdef GLOBAL_COLLECTION_LOCK
        apr_sdbm_close(apr_dbm);
	    waf_free_exclusive_lock(msr->modsecurity->dbm_lock);
#else
        apr_sdbm_unlock(apr_dbm);
        apr_sdbm_close(apr_dbm);
#endif
#ifdef MEMORY_DATABASE_ENABLE
    }
#endif

    if (msr->txcfg->debuglog_level >= 4) {
        msr_log(msr, 4, "collection_store: Persisted collection (name \"%s\", key \"%s\").",
            log_escape_ex(msr->mp, var_name->value, var_name->value_len),
            log_escape_ex(msr->mp, var_key->value, var_key->value_len));
    }

    return 0;
    
error:
    return -1;
}