/*
* Copyright (c) 2019 Clementine Computing LLC.
*
* This file is part of PopuFare.
*
* PopuFare is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PopuFare is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with PopuFare. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../common/common_defs.h"
#include "passdb_slim.h"
int read_config( passdb_slim_config *cfg, char *config_fn );
int passdb_slim_manage_rider_banks(passdb_slim_context *ctx);
static void free_rider_node_list(rider_node *head)
{
rider_node *p = head;
rider_node *q;
while(p)
{
q = p;
p = p->next;
free(q);
}
}
#define FIND_IDX_IN_BUCKET(b, idx, p, q)\
{ \
p = b; \
q = NULL; \
\
while(p) \
{ \
if(p->idx == idx) \
{ \
break; \
} \
\
q = p; \
p = p->next; \
} \
} \
//---------------------------
#define ADD_TO_BUCKET(b, idx, p, q) \
{ \
p = (rider_node *) malloc( sizeof(rider_node) );\
\
if(p == NULL) return FAIL_MEM; \
\
p->next = NULL; \
p->idx = idx; \
\
if(q) \
{ \
q->next = p; \
} \
else \
{ \
b = p; \
} \
} \
//---------------------------
#define DEL_FROM_BUCKET(b, p, q) \
{ \
if(q) \
{ \
q->next = p->next; \
} \
else \
{ \
b = p->next; \
} \
\
free(p); \
} \
//---------------------------
//#define INDEX_MIDPOINT 500000
int make_rider_record_from_rider_one_cred( passdb_slim_context *ctx, rider_record *rr, rider_record_slim_one_cred *rr1 )
{
long long unsigned llu;
long unsigned a, b;
int retval;
memset(rr, 0, sizeof(rider_record));
if (rr1->id == ID_INVALID)
return WARN_NOTFOUND;
rr->seq = rr1->seq;
rr->id = rr1->id;
if (rr1->code < 4)
{
rr->rfid_value[0] = '\0';
snprintf(rr->magstripe_value, CREDENTIAL_LEN, "%i:%llu", rr1->code, rr1->credential );
}
else
{
llu = 0xffffffffL;
b = (long unsigned)(rr1->credential & llu);
llu = llu << 32;
a = (long unsigned)( (rr1->credential & llu) >> 32 );
rr->magstripe_value[0] = '\0';
snprintf(rr->rfid_value, CREDENTIAL_LEN, "%i:%lu:%lu", rr1->code, a, b );
}
retval = ruleparam_db_get( rr->rule_name, rr->rule_param, ctx->ruleparam_db, rr1->rule_param_bucket_id );
if (retval < 0)
{
fprintf(stderr, "ERROR: make_rider_record_from_rider_one_cred: no mapping for rr1->rule_param_bucket_id (%i), got %i\n",
rr1->rule_param_bucket_id, retval);
//DEBUG
fprintf(stderr, "ERROR: mrrfroc: ruleid %i, got %i rr1( seq:%llu, id:%llu, %u:%llu, %i)\n",
rr1->rule_param_bucket_id, retval,
rr1->seq, rr1->id,
(unsigned int)rr1->code, rr1->credential,
(unsigned int)rr1->rule_param_bucket_id
);
return retval;
}
return 0;
}
//--
int make_rider_record_from_rider_two_cred( passdb_slim_context *ctx, rider_record *rr, rider_record_slim_two_cred *rr2 )
{
int retval;
memset(rr, 0, sizeof(rider_record));
if (rr2->id == ID_INVALID)
return WARN_NOTFOUND;
rr->seq = rr2->seq;
rr->id = rr2->id;
snprintf(rr->magstripe_value,
CREDENTIAL_LEN,
"%i:%llu",
rr2->magstripe_code, rr2->magstripe );
snprintf(rr->rfid_value,
CREDENTIAL_LEN,
"%i:%lu:%lu",
rr2->rfid_code, rr2->rfid_site, rr2->rfid_val );
retval = ruleparam_db_get( rr->rule_name, rr->rule_param, ctx->ruleparam_db, rr2->rule_param_bucket_id );
if (retval < 0)
{
fprintf(stderr, "ERROR: make_rider_record_from_rider_two_cred: no mapping for rr2->rule_param_bucket_id (%i), got %i\n",
rr2->rule_param_bucket_id, retval);
//DEBUG
fprintf(stderr, "ERROR: mrrfrtc: ruleid %i, got %i rr2( seq:%llu, id:%llu, %u:%lu:%lu %u:%llu, %i)\n",
rr2->rule_param_bucket_id, retval,
rr2->seq, rr2->id,
(unsigned int)rr2->rfid_code, rr2->rfid_site, rr2->rfid_val,
(unsigned int)rr2->magstripe_code, rr2->magstripe,
(unsigned int)rr2->rule_param_bucket_id
);
return retval;
}
return 0;
}
//--
// UNDER DEVELOPMENT
//
//int make_rider_record_from_rider_spillover( passdb_slim_context *ctx, rider_record *rr, rider_record_slim_spillover *rr2 )
int make_rider_record_from_rider_spillover( passdb_slim_context *ctx, rider_record *rr, rider_record *rr_spillover )
{
int bucket_id;
memcpy( rr, rr_spillover, sizeof(rider_record) );
bucket_id = ruleparam_db_find( ctx->ruleparam_db, rr->rule_name, rr->rule_param );
if (bucket_id == RULEPARAM_DB_NOT_FOUND )
{
fprintf(stderr, "ERROR: make_rider_record_from_rider_spillover: no mapping for rule/param (%s,%s), got %i\n",
rr->rule_name,
rr->rule_param,
bucket_id);
return -1;
}
return 0;
}
//--
void populate_one_cred_rider_record(
rider_record_slim_one_cred *rr_one,
int pos,
void *rider_p )
{
void *p;
memset(rr_one, 0, sizeof(rider_record_slim_one_cred));
p = rider_p + (pos*RIDER_ONE_CRED_SIZE);
//rr_one->seq = (seq_t)( *((seq_t *)p) );
rr_one->seq = _ulli(p);
p += sizeof(seq_t);
//rr_one->id = (logical_card_id_t)( *((logical_card_id_t *)p) );
rr_one->id = _ulli(p);
p += sizeof(logical_card_id_t);
rr_one->code = (unsigned char)( *((unsigned char *)p) );
p += sizeof(unsigned char);
//rr_one->credential = (unsigned long long)( *((unsigned long long *)p) );
rr_one->credential = _ulli(p);
p += sizeof(unsigned long long);
//rr_one->rule_param_bucket_id = (unsigned short int)( *((unsigned short int *)p) );
rr_one->rule_param_bucket_id = _usi(p);
//p += sizeof(unsigned short int);
}
void populate_two_cred_rider_record( rider_record_slim_two_cred *rr_two, int pos, void *rider_p)
{
void *p;
memset(rr_two, 0, sizeof(rider_record_slim_two_cred));
p = rider_p + (pos*RIDER_TWO_CRED_SIZE);
//rr_two->seq = (seq_t)( *((seq_t *)p) );
rr_two->seq = _ulli(p);
p += sizeof(seq_t);
//rr_two->id = (logical_card_id_t)( *((logical_card_id_t *)p) );
rr_two->id = _ulli(p);
p += sizeof(logical_card_id_t);
rr_two->magstripe_code = (unsigned char)( *((unsigned char *)p) );
p += sizeof(unsigned char);
//rr_two->magstripe = (unsigned long long)( *((unsigned long long *)p) );
rr_two->magstripe = _ulli(p);
p += sizeof(unsigned long long);
rr_two->rfid_code = (unsigned char)( *((unsigned char *)p) );
p += sizeof(unsigned char);
//rr_two->rfid_site = (unsigned long)( *((unsigned long *)p) );
rr_two->rfid_site = _uli(p);
p += sizeof(unsigned long);
//rr_two->rfid_val = (unsigned long)( *((unsigned long *)p) );
rr_two->rfid_val = _uli(p);
p += sizeof(unsigned long);
//rr_two->rule_param_bucket_id = (unsigned short int)( *((unsigned short int *)p) );
rr_two->rule_param_bucket_id = _usi(p);
//p += sizeof(unsigned short int);
}
// ---
void populate_spillover_rider_record( rider_record *rr, int pos, void *rider_p)
{
memcpy(rr, rider_p + (pos*RIDER_SPILLOVER_SIZE), sizeof(rider_record) );
}
// ---
void *passdb_slim_get_record_address( passdb_slim_context *ctx, int idx )
{
int q,r;
if (idx < INDEX_MIDPOINT)
{
q = idx / ctx->n_one_cred_bank_size;
r = idx - (q * ctx->n_one_cred_bank_size);
if ( (q<0) || (q >= ctx->n_one_cred_bank) )
{
fprintf(stderr, "ERROR: passdb_slim_get_record_address, bank out of range (%i<0 or %i>%i) (%i,%i) from idx %i\n", q, q, ctx->n_one_cred_bank, q, r, idx);
return NULL;
}
return ctx->rider_one_cred_bank[q] + (r * RIDER_ONE_CRED_SIZE);
}
else if ( idx < (2*INDEX_MIDPOINT) )
{
q = (idx - INDEX_MIDPOINT) / ctx->n_two_cred_bank_size;
r = (idx - INDEX_MIDPOINT) - (q * ctx->n_two_cred_bank_size);
if ( (q<0) || (q >= ctx->n_two_cred_bank) )
{
fprintf(stderr, "ERROR: passdb_slim_get_record_address, bank out of range (%i<0 or %i>%i) (%i,%i) from idx %i\n", q,q, ctx->n_two_cred_bank, q, r, idx);
return NULL;
}
return ctx->rider_two_cred_bank[q] + (r * RIDER_TWO_CRED_SIZE);
}
else
{
q = (idx - (2*INDEX_MIDPOINT)) / ctx->n_spillover_bank_size;
r = (idx - (2*INDEX_MIDPOINT)) - (q * ctx->n_spillover_bank_size);
if ( (q<0) || (q >= ctx->n_spillover_bank) )
{
fprintf(stderr, "ERROR: passdb_slim_get_record_address, bank out of range (%i<0 or %i>%i) (%i,%i) from idx %i\n", q,q, ctx->n_spillover_bank, q, r, idx);
return NULL;
}
return ctx->rider_spillover_bank[q] + (r * RIDER_SPILLOVER_SIZE);
}
return NULL;
}
// Helper function that stores the bank and position of the index.
// That is, find the quotent and remainder of idx (suitably repositioned)
// with the number of entries in the appropriate bank.
// For example, if we have 251 entries per bank, with 3 banks and an index
// of 255, this will store 1 in bank and 4 in pos.
// You must multiply by the value of the record size and offset by the bank
// start poition to get the mmap'd memory location.
//
// If all you want is the memory record location, use the function
// passdb_slim_get_record_address.
//
// Return -1 if the bank number is invalid (less than 0 or greater than
// the maximum bank number)
//
int passdb_slim_get_cred_bank_and_pos( passdb_slim_context *ctx, int *bank, int *pos, int idx )
{
int q, r;
if (idx < INDEX_MIDPOINT)
{
q = idx / ctx->n_one_cred_bank_size ;
r = idx - (q * ctx->n_one_cred_bank_size);
if ( (q < 0) || (q >= ctx->n_one_cred_max_bank) )
{
fprintf(stderr, "ERROR: passdb_slim_get_cred_bank_and_pos, bank out of range (%i,%i) from idx %i\n", q, r, idx);
return -1;
}
}
else if (idx < (2*INDEX_MIDPOINT))
{
q = (idx - INDEX_MIDPOINT) / ctx->n_two_cred_bank_size ;
r = (idx - INDEX_MIDPOINT) - (q * ctx->n_two_cred_bank_size);
if ( (q < 0) || (q >= ctx->n_two_cred_max_bank) )
{
fprintf(stderr, "ERROR: passdb_slim_get_cred_bank_and_pos, bank out of range (%i,%i) from idx %i\n", q, r, idx);
return -1;
}
}
else
{
q = (idx - (2*INDEX_MIDPOINT)) / ctx->n_spillover_bank_size ;
r = (idx - (2*INDEX_MIDPOINT)) - (q * ctx->n_spillover_bank_size);
if ( (q < 0) || (q >= ctx->n_spillover_max_bank) )
{
fprintf(stderr, "ERROR: passdb_slim_get_cred_bank_and_pos, bank out of range (%i,%i) from idx %i\n", q, r, idx);
return -1;
}
}
*bank = q;
*pos = r;
return 0;
}
//--------------------------------
void make_rider_record( passdb_slim_context *ctx, rider_record *rr, int idx )
{
int r;
void *p;
rider_record_slim_one_cred rr1 = {0};
rider_record_slim_two_cred rr2 = {0};
p = passdb_slim_get_record_address( ctx, idx );
if (p == NULL)
{
fprintf(stderr, "make_rider_record: passdb_slim_get_record_address returned NULL! (idx %i)\n", idx);
return;
}
if (idx < INDEX_MIDPOINT )
{
populate_one_cred_rider_record( &rr1, 0, p );
r = make_rider_record_from_rider_one_cred( ctx, rr, &rr1);
}
else if (idx < (2*INDEX_MIDPOINT))
{
populate_two_cred_rider_record( &rr2, 0, p );
r = make_rider_record_from_rider_two_cred( ctx, rr, &rr2);
}
else
{
memcpy( rr, p , sizeof(rider_record) );
}
}
//----
int find_id_in_hash(passdb_slim_context *ctx, logical_card_id_t id)
{
rider_record rr = {0};
rider_node *p;
if(id <= 0)
return FAIL_PARAM;
p = ctx->logical_card_id_hash[id % ctx->hash_modulus];
while(p)
{
make_rider_record( ctx, &rr, p->idx );
if (rr.id == id)
return p->idx;
p = p->next;
}
return WARN_NOTFOUND;
}
int add_to_id_hash(passdb_slim_context *ctx, int idx)
{
rider_record rr = {0};
rider_node *p, *q;
unsigned int bucket;
if(idx < 0)
{
return FAIL_PARAM;
}
make_rider_record( ctx, &rr, idx );
if (rr.id == ID_INVALID)
{
return FAIL_PARAM;
}
bucket = rr.id % ctx->hash_modulus;
FIND_IDX_IN_BUCKET( ctx->logical_card_id_hash[bucket], idx, p, q )
if(p)
{
return FAIL_DUPKEY; //already exists!
}
ADD_TO_BUCKET( ctx->logical_card_id_hash[bucket], idx, p, q );
return 0;
}
int delete_from_id_hash(passdb_slim_context *ctx, int idx)
{
rider_record rr = {0};
rider_node *p, *q;
unsigned int bucket;
if(idx < 0) return FAIL_PARAM;
make_rider_record( ctx, &rr, idx );
bucket = rr.id % ctx->hash_modulus;
FIND_IDX_IN_BUCKET( ctx->logical_card_id_hash[bucket], idx, p, q)
if(p)
{
DEL_FROM_BUCKET( ctx->logical_card_id_hash[bucket], p, q )
return 0;
}
else
{
return WARN_NOTFOUND;
}
}
//##
int find_mag_in_hash(passdb_slim_context *ctx, char *mag)
{
rider_record rr = {0};
rider_node *p;
if(mag[0] == '\0') return FAIL_PARAM;
p = ctx->rider_mag_hash[stringhash(mag) % ctx->hash_modulus];
while(p)
{
make_rider_record( ctx, &rr, p->idx );
if(!strncmp(rr.magstripe_value, mag, CREDENTIAL_LEN))
return p->idx;
p = p->next;
}
return WARN_NOTFOUND;
}
int add_to_mag_hash(passdb_slim_context *ctx, int idx)
{
rider_record rr = {0};
rider_node *p, *q;
unsigned int bucket;
if(idx < 0) return 0; //if we have a non index or a non-value, return silently
make_rider_record( ctx, &rr, idx );
if (rr.magstripe_value[0] == '\0') return 0;
bucket = stringhash( rr.magstripe_value) % ctx->hash_modulus;
FIND_IDX_IN_BUCKET( ctx->rider_mag_hash[bucket], idx, p, q )
#ifndef ALLOW_CREDENTIAL_COLLISIONS
//On allowing hash collisions among credentials see comment tagged **STUPID** later in this file.
if(p)
{
return FAIL_DUPKEY; //already exists!
}
else
#endif
{
ADD_TO_BUCKET( ctx->rider_mag_hash[bucket], idx, p, q );
return 0;
}
}
int delete_from_mag_hash(passdb_slim_context *ctx, int idx)
{
rider_record rr = {0};
rider_node *p, *q;
unsigned int bucket;
if(idx < 0) return FAIL_PARAM;
make_rider_record( ctx, &rr, idx );
bucket = stringhash( rr.magstripe_value ) % ctx->hash_modulus;
FIND_IDX_IN_BUCKET( ctx->rider_mag_hash[bucket], idx, p, q)
if(p)
{
DEL_FROM_BUCKET( ctx->rider_mag_hash[bucket], p, q )
return 0;
}
else
{
return WARN_NOTFOUND;
}
}
//##
int find_rf_in_hash(passdb_slim_context *ctx, char *rfid)
{
rider_record rr = {0} ;
rider_node *p;
if(rfid[0] == '\0') return WARN_NOTFOUND;
p = ctx->rider_rf_hash[stringhash(rfid)% ctx->hash_modulus];
while(p)
{
make_rider_record( ctx, &rr, p->idx );
if(!strncmp( rr.rfid_value, rfid, CREDENTIAL_LEN))
return p->idx;
p = p->next;
}
return WARN_NOTFOUND;
}
int add_to_rf_hash(passdb_slim_context *ctx, int idx)
{
rider_record rr = {0};
rider_node *p, *q;
unsigned int bucket;
if(idx < 0) return 0; //if we have a non index or a non-value, return silently
make_rider_record( ctx, &rr, idx );
if(rr.rfid_value[0] == '\0') return 0;
bucket = stringhash(rr.rfid_value) % ctx->hash_modulus;
FIND_IDX_IN_BUCKET( ctx->rider_rf_hash[bucket], idx, p, q )
#ifndef ALLOW_CREDENTIAL_COLLISIONS
//On allowing hash collisions among credentials see comment tagged **STUPID** later in this file.
if(p)
{
return FAIL_DUPKEY; //already exists!
}
else
#endif
{
ADD_TO_BUCKET( ctx->rider_rf_hash[bucket], idx, p, q );
return 0;
}
}
int delete_from_rf_hash(passdb_slim_context *ctx, int idx)
{
rider_record rr = {0} ;
rider_node *p, *q;
unsigned int bucket;
if(idx < 0) return FAIL_PARAM;
make_rider_record( ctx, &rr, idx );
bucket = stringhash(rr.rfid_value) % ctx->hash_modulus;
FIND_IDX_IN_BUCKET( ctx->rider_rf_hash[bucket], idx, p, q)
if(p)
{
DEL_FROM_BUCKET( ctx->rider_rf_hash[bucket], p, q )
return 0;
}
else
{
return WARN_NOTFOUND;
}
}
//##
int build_hashes(passdb_slim_context *ctx)
{
rider_record rr = {0};
rider_node *p = ctx->activelist;
int retval;
while(p)
{
make_rider_record( ctx, &rr, p->idx );
ctx->ruleparam_db->seq = ctx->seq;
ruleparam_db_update( ctx->ruleparam_db, rr.rule_name, rr.rule_param , 1 );
retval = add_to_id_hash(ctx, p->idx);
if( !DB_OKAY(retval) )
{
fprintf(stderr, "Error (%d) indexing rider ID %llu at index %d!\n", retval, rr.id, p->idx);
return retval;
}
retval = add_to_mag_hash(ctx, p->idx);
if( !DB_OKAY(retval) )
{
fprintf(stderr, "Error (%d) indexing magstripe %s at index %d!\n", retval, rr.magstripe_value, p->idx);
return -1;
}
retval = add_to_rf_hash(ctx, p->idx);
if( !DB_OKAY(retval) )
{
fprintf(stderr, "Error (%d) indexing RFID %s at index %d!\n", retval, rr.rfid_value, p->idx);
return -1;
}
p = p->next;
}
// Write reference counts for debugging.
// Rules and params should have been recorded by
// ruleparam_db_update, but we'd like, at least for debugging
// purposes, to write out the reference counts. Do this
// here.
//
ruleparam_db_clean( ctx->ruleparam_db );
#ifdef PASSDB_CONSISTENCY_CHECK
retval = ruleparam_db_consistency_check( ctx->ruleparam_db );
if (retval < 0)
{
fprintf(stderr, "CONSISTENCY CHECK failed in build_hashes: got %i\n", retval);
}
#endif
ctx->ruleparam_db->seq = ctx->seq;
//ruleparam_db_save( ctx->ruleparam_db, ctx->ruleparam_db_fn );
ruleparam_db_save( ctx->ruleparam_db );
return 0;
}
// memory page size 4096
int format_new_passdb( char *fn, int sz, long pagesize )
{
int i,n;
int fd;
char *blank;
blank = calloc( pagesize, sizeof(char) );
if (!blank)
return FAIL_MEM;
fd = creat(fn, S_IRUSR | S_IWUSR);
if( fd < 0 )
{
fprintf(stderr, "Cannot create pass file %s!\n", fn);
free(blank);
return FAIL_DATABASE;
}
n = sz / pagesize;
if ( (sz % pagesize ) > 0 )
{
n++;
}
for(i = 0; i < n; i++)
{
if( write(fd, blank, pagesize) != pagesize )
{
fprintf(stderr, "Cannot write blank data to passes file %s!\n", fn);
free(blank);
close(fd);
return FAIL_DATABASE;
}
}
free(blank);
close(fd);
return 0;
}
int format_new_passdbs()
{
char fn[64];
int bank;
int r;
long pagesize;
passdb_slim_config cfg;
int sz;
int quotient, remainder;
ruleparam_db_ctx *ruleparam_ctx;
pagesize = sysconf(_SC_PAGE_SIZE);
r = read_config(&cfg, PASSDB_SLIM_CONFIG_FILE);
if (r != 0)
{
printf("WARNING: %s was not read, formatting default config file\n", PASSDB_SLIM_CONFIG_FILE);
make_default_config( PASSDB_SLIM_CONFIG_FILE );
r = read_config(&cfg, PASSDB_SLIM_CONFIG_FILE);
if (r!=0)
return r;
}
// Try to load the ruleparam. If it doesn't exist, try to create it.
// Else, just deallocate the dummy load.
//
r = ruleparam_db_load( &ruleparam_ctx , RULEPARAM_DB_FILE );
if (r < 0)
{
printf("WARNING: ruleparam database %s was not read, creating a blank new one\n", RULEPARAM_DB_FILE );
r = format_new_ruleparamdb( RULEPARAM_DB_FILE );
if (r < 0)
{
perror(RULEPARAM_DB_FILE);
return r;
}
}
else
{
ruleparam_db_free( ruleparam_ctx );
}
// For creation of banks
//
for (bank=0; bank 0)
sz += pagesize;
cfg.rider_one_file_bank_size = sz;
r = format_new_passdb( fn, sz, pagesize );
if (r != 0) return r;
}
//printf("cfg.n_two_cred_bank %i\n", cfg.n_two_cred_bank );
for (bank=0; bank 0)
sz += pagesize;
cfg.rider_two_file_bank_size = sz;
r = format_new_passdb( fn, sz, pagesize );
if (r!=0) return r;
}
for (bank=0; bank 0)
sz += pagesize;
cfg.rider_spillover_file_bank_size = sz;
r = format_new_passdb( fn, sz, pagesize );
if (r!=0) return r;
}
free( cfg.one_cred_db_base_fn );
free( cfg.two_cred_db_base_fn );
free( cfg.spillover_db_base_fn );
free( cfg.db_fn_suffix );
free( cfg.ruleparam_db_fn );
return 0;
}
int detach_from_passdb(passdb_slim_context *ctx)
{
int i;
int bank;
if(!ctx)
return FAIL_PARAM;
if (ctx->ruleparam_db)
{
ctx->ruleparam_db->seq = ctx->seq;
//ruleparam_db_save( ctx->ruleparam_db, ctx->ruleparam_db_fn );
ruleparam_db_save( ctx->ruleparam_db );
}
free_rider_node_list(ctx->freelist_one_cred);
free_rider_node_list(ctx->freelist_two_cred);
free_rider_node_list(ctx->freelist_spillover);
free_rider_node_list(ctx->activelist);
for(i=0; i < ctx->hash_modulus; i++)
{
free_rider_node_list(ctx->logical_card_id_hash[i]);
free_rider_node_list(ctx->rider_mag_hash[i]);
free_rider_node_list(ctx->rider_rf_hash[i]);
}
for (bank=0; bankn_one_cred_bank; bank++)
munmap( ctx->rider_one_cred_bank[bank], ctx->rider_one_file_bank_size );
for (bank=0; bankn_two_cred_bank; bank++)
munmap( ctx->rider_two_cred_bank[bank], ctx->rider_two_file_bank_size );
for (bank=0; bankn_spillover_bank; bank++)
munmap( ctx->rider_spillover_bank[bank], ctx->rider_two_file_bank_size );
free( ctx->logical_card_id_hash );
free( ctx->rider_mag_hash );
free( ctx->rider_rf_hash );
if (ctx->one_cred_db_bank_fn)
{
for (i=0; in_one_cred_max_bank; i++)
{
if (ctx->one_cred_db_bank_fn[i])
{
free(ctx->one_cred_db_bank_fn[i]);
}
}
free(ctx->one_cred_db_bank_fn);
}
if (ctx->two_cred_db_bank_fn)
{
for (i=0; in_two_cred_max_bank; i++)
{
if (ctx->two_cred_db_bank_fn[i])
{
free(ctx->two_cred_db_bank_fn[i]);
}
}
free(ctx->two_cred_db_bank_fn);
}
if (ctx->spillover_db_bank_fn)
{
for (i=0; in_spillover_max_bank; i++)
{
if (ctx->spillover_db_bank_fn[i])
{
free(ctx->spillover_db_bank_fn[i]);
}
}
free(ctx->spillover_db_bank_fn);
}
if (ctx->one_cred_db_base_fn)
free( ctx->one_cred_db_base_fn);
if (ctx->two_cred_db_base_fn)
free( ctx->two_cred_db_base_fn);
if (ctx->spillover_db_base_fn)
free( ctx->spillover_db_base_fn);
if (ctx->db_fn_suffix)
free( ctx->db_fn_suffix );
if (ctx->ruleparam_db_fn)
free( ctx->ruleparam_db_fn );
if (ctx->rider_one_cred_bank)
free( ctx->rider_one_cred_bank );
if (ctx->rider_two_cred_bank)
free( ctx->rider_two_cred_bank );
if (ctx->rider_spillover_bank)
free( ctx->rider_spillover_bank );
for (bank=0; bankn_one_cred_bank; bank++)
close(ctx->passes_one_cred_bank_fd[bank]);
for (bank=0; bankn_two_cred_bank; bank++)
close(ctx->passes_two_cred_bank_fd[bank]);
for (bank=0; bankn_spillover_bank; bank++)
close(ctx->passes_spillover_bank_fd[bank]);
if (ctx->passes_one_cred_bank_fd)
free(ctx->passes_one_cred_bank_fd);
if (ctx->passes_two_cred_bank_fd)
free(ctx->passes_two_cred_bank_fd);
if (ctx->passes_spillover_bank_fd)
free(ctx->passes_spillover_bank_fd);
if (ctx->ruleparam_db)
ruleparam_db_free( ctx->ruleparam_db );
memset(ctx, 0, sizeof(passdb_slim_context));
return 0;
}
int init_mmap_passfile( int *fd, void **rider_p, size_t *file_size, size_t rider_size, char *fn )
{
struct stat st;
void *t_rider_p;
int retval;
int t_fd;
//int i, k;
retval = stat(fn, &st);
if(retval)
{
fprintf(stderr, "Cannot find one credential passes file %s!\n", fn);
return FAIL_DATABASE;
}
t_fd = open( fn, O_RDWR | O_SYNC);
if(t_fd < 0)
{
fprintf(stderr, "Cannot open one credential passes file %s!\n", fn);
return FAIL_DATABASE;
}
t_rider_p = (void *) mmap( NULL, st.st_size , PROT_READ | PROT_WRITE, MAP_PRIVATE, t_fd, 0 );
/*
for (i=0; i 3)
{
k = 1;
}
else
k=0;
}
*/
if ( (t_rider_p == NULL) || (t_rider_p == MAP_FAILED) )
{
close(t_fd);
fprintf( stderr,
"Cannot mmap passes file! Try checking sysctl settings kernel.shmall and kernel.shmmax (return == %p errno == %d)\n",
t_rider_p, errno);
return FAIL_MEM;
}
else
{
//printf("operating in braindead file IO mode...\n");
}
*fd = t_fd;
*rider_p = t_rider_p;
*file_size = st.st_size ;
return 0;
}
int load_one_cred(passdb_slim_context *ctx)
{
int bank ;
int i, n;
rider_record_slim_one_cred rr_one;
seq_t maxseq = 0;
rider_node *freehead=NULL, *acthead=NULL, *q;
int numfree = 0, numact = 0;
n = ctx->n_one_cred_bank * ctx->n_one_cred_bank_size;
if (ctx->activelist) acthead = ctx->activelist;
if (ctx->freelist_one_cred) freehead = ctx->freelist_one_cred;
maxseq = ctx->seq;
//For all records in our flat file
//
for(i=0; i < n; i++)
{
bank = i / ctx->n_one_cred_bank_size;
populate_one_cred_rider_record( &rr_one, i - (bank * ctx->n_one_cred_bank_size), ctx->rider_one_cred_bank[bank] );
// Check the sequence number and update our latest tally if it is newer.
//
if(rr_one.seq > maxseq)
{
maxseq = rr_one.seq;
}
// If the record is not in use
//
if(rr_one.id == ID_INVALID)
{
// Add it to the freelist
//
q = (rider_node *) malloc( sizeof(rider_node) );
if(!q)
{
free_rider_node_list(freehead);
free_rider_node_list(acthead);
fprintf(stderr, "Malloc returned NULL loading riders!\n");
for (bank=0; bankn_one_cred_bank; bank++)
{
munmap( ctx->rider_one_cred_bank[bank], ctx->n_one_cred_bank_size * RIDER_ONE_CRED_SIZE );
}
fprintf(stderr, "FAIL_MEM: load_one_cred (1)\n");
return FAIL_MEM;
}
else
{
numfree++;
q->next = freehead;
q->idx = i;
freehead = q;
}
}
// Else it's an active record
//
else
{
// Add it to the active list
//
q = (rider_node *) malloc( sizeof(rider_node) );
if(!q)
{
free_rider_node_list(freehead);
free_rider_node_list(acthead);
fprintf(stderr, "Malloc returned NULL loading riders!\n");
for (bank=0; bankn_one_cred_bank; bank++)
{
munmap( ctx->rider_one_cred_bank[bank], ctx->n_one_cred_bank_size * RIDER_ONE_CRED_SIZE );
}
fprintf(stderr, "FAIL_MEM: load_one_cred (2)\n");
return FAIL_MEM;
}
else
{
numact++;
q->next = acthead;
q->idx = i;
acthead = q;
}
}
} // for bank
ctx->freelist_one_cred = freehead;
ctx->activelist = acthead;
ctx->seq = maxseq;
ctx->num_active += numact;
ctx->num_free += numfree;
ctx->n_one_cred = numact;
return 0;
}
int load_two_cred(passdb_slim_context *ctx)
{
int bank ;
int i, n;
rider_record_slim_two_cred rr_two;
seq_t maxseq = 0;
rider_node *freehead=NULL, *acthead=NULL, *q;
int numfree = 0, numact = 0;
n = ctx->n_two_cred_bank * ctx->n_two_cred_bank_size ;
if (ctx->freelist_two_cred) freehead = ctx->freelist_two_cred;
if (ctx->activelist) acthead = ctx->activelist;
maxseq = ctx->seq;
for(i=0; i < n; i++)
{
bank = i / ctx->n_two_cred_bank_size;
populate_two_cred_rider_record( &rr_two, i - (bank*ctx->n_two_cred_bank_size), ctx->rider_two_cred_bank[bank] );
//check the sequence number and update our "latest" tally if it is newer.
if(rr_two.seq > maxseq)
{
maxseq = rr_two.seq;
}
//if the record is not in use
if(rr_two.id == ID_INVALID)
{
//add it to the freelist
q = (rider_node *) malloc( sizeof(rider_node) );
if(!q)
{
free_rider_node_list(freehead);
free_rider_node_list(acthead);
fprintf(stderr, "Malloc returned NULL loading riders!\n");
for (bank=0; bankn_two_cred_bank; bank++)
{
munmap( ctx->rider_two_cred_bank[bank], ctx->n_two_cred_bank_size * RIDER_ONE_CRED_SIZE );
}
fprintf(stderr, "FAIL_MEM: load_two_cred (1)\n");
return FAIL_MEM;
}
else
{
numfree++;
q->next = freehead;
q->idx = i + INDEX_MIDPOINT;
freehead = q;
}
}
else
{
//add it to the active list
q = (rider_node *) malloc( sizeof(rider_node) );
if(!q)
{
free_rider_node_list(freehead);
free_rider_node_list(acthead);
fprintf(stderr, "Malloc returned NULL loading riders!\n");
for (bank=0; bankn_two_cred_bank; bank++)
{
munmap( ctx->rider_two_cred_bank[bank], ctx->n_two_cred_bank_size * RIDER_ONE_CRED_SIZE );
}
fprintf(stderr, "FAIL_MEM: load_two_cred (2)\n");
return FAIL_MEM;
}
else
{
numact++;
q->next = acthead;
q->idx = i + INDEX_MIDPOINT;
acthead = q;
}
}
}
ctx->activelist = acthead;
ctx->freelist_two_cred = freehead;
ctx->seq = maxseq;
ctx->num_active += numact;
ctx->num_free += numfree;
ctx->n_two_cred = numact;
return 0;
}
// UNDER DELEVEOPMENT
//
int load_spillover(passdb_slim_context *ctx)
{
int bank ;
int i, n;
rider_record rr_spillover;
seq_t maxseq = 0;
rider_node *freehead=NULL, *acthead=NULL, *q;
int numfree = 0, numact = 0;
n = ctx->n_spillover_bank * ctx->n_spillover_bank_size ;
if (ctx->freelist_spillover) freehead = ctx->freelist_spillover;
if (ctx->activelist) acthead = ctx->activelist;
maxseq = ctx->seq;
for(i=0; i < n; i++)
{
bank = i / ctx->n_spillover_bank_size;
populate_spillover_rider_record( &rr_spillover, i - (bank*ctx->n_spillover_bank_size), ctx->rider_spillover_bank[bank] );
//check the sequence number and update our "latest" tally if it is newer.
if(rr_spillover.seq > maxseq)
{
maxseq = rr_spillover.seq;
}
//if the record is not in use
if(rr_spillover.id == ID_INVALID)
{
//add it to the freelist
q = (rider_node *) malloc( sizeof(rider_node) );
if(!q)
{
free_rider_node_list(freehead);
free_rider_node_list(acthead);
fprintf(stderr, "Malloc returned NULL loading riders!\n");
for (bank=0; bankn_spillover_bank; bank++)
{
munmap( ctx->rider_spillover_bank[bank], ctx->n_spillover_bank_size * RIDER_ONE_CRED_SIZE );
}
fprintf(stderr, "FAIL_MEM: load_spillover (1)\n");
return FAIL_MEM;
}
else
{
numfree++;
q->next = freehead;
q->idx = i + (2*INDEX_MIDPOINT);
freehead = q;
}
}
else
{
//add it to the active list
q = (rider_node *) malloc( sizeof(rider_node) );
if(!q)
{
free_rider_node_list(freehead);
free_rider_node_list(acthead);
fprintf(stderr, "Malloc returned NULL loading riders!\n");
for (bank=0; bankn_spillover_bank; bank++)
{
munmap( ctx->rider_spillover_bank[bank], ctx->n_spillover_bank_size * RIDER_ONE_CRED_SIZE );
}
fprintf(stderr, "FAIL_MEM: load_spillover (2)\n");
return FAIL_MEM;
}
else
{
numact++;
q->next = acthead;
q->idx = i + (2*INDEX_MIDPOINT);
acthead = q;
}
}
}
ctx->activelist = acthead;
ctx->freelist_spillover = freehead;
ctx->seq = maxseq;
ctx->num_active += numact;
ctx->num_free += numfree;
ctx->n_spillover = numact;
return 0;
}
// Slow but sure way of deleting entries. Restarting from the beginning will
// be slower than if we exploited knowledge of the data structures, but makes
// this routine much more robust and simple to code. By restarting after
// every delete, we don't need any in depth knowledge about how to search
// or delete entries other than walking through the 'activelist' and using
// 'delete_rider' to delete the entry.
//
void passdb_slim_delete_seq_above( passdb_slim_context *ctx, seq_t cur_seq )
{
int dirty = 0;
rider_node *nod;
rider_record rr;
do {
dirty = 0;
nod = ctx->activelist;
while (nod)
{
make_rider_record( ctx, &rr, nod->idx );
if (rr.seq > cur_seq)
{
delete_rider( ctx, &rr, 1 );
dirty = 1;
break;
}
nod = nod->next;
}
} while (dirty);
}
int attach_to_passdb(passdb_slim_context *ctx)
{
int retval;
int numfree = 0, numact = 0;
int read_n_one_cred;
int read_n_two_cred;
int read_n_spillover;
int i;
int n;
seq_t ruleparam_seq = 0;
//--------
if(!ctx) //fail if we get passed a null pointer
return FAIL_PARAM;
//We also want to fail if we get passed a pointer to an active/in-use context...
//
if( ctx->rider_one_cred_bank ||
ctx->rider_two_cred_bank ||
ctx->rider_spillover_bank ||
ctx->activelist ||
ctx->freelist_one_cred || ctx->freelist_two_cred )
{
return FAIL_PARAM;
}
ctx->rider_one_cred_bank = NULL;
ctx->rider_two_cred_bank = NULL;
ctx->rider_spillover_bank = NULL;
ctx->activelist = NULL;
ctx->freelist_one_cred = NULL;
ctx->freelist_two_cred = NULL;
ctx->freelist_spillover = NULL;
ctx->num_active = 0;
ctx->num_free = 0;
ctx->seq = 0;
ctx->n_one_cred = 0;
ctx->n_two_cred = 0;
ctx->n_spillover = 0;
ctx->one_cred_db_bank_fn = NULL;
ctx->two_cred_db_bank_fn = NULL;
ctx->spillover_db_bank_fn = NULL;
ctx->ruleparam_db_fn = NULL;
ctx->one_cred_db_base_fn = NULL;
ctx->two_cred_db_base_fn = NULL;
ctx->spillover_db_base_fn = NULL;
ctx->db_fn_suffix = NULL;
retval =
init_context_from_config( ctx, PASSDB_SLIM_CONFIG_FILE );
if (retval != 0)
{
fprintf(stderr, "ERROR: init_context_from_config (%s) failed, can't proceed\n", PASSDB_SLIM_CONFIG_FILE );
return retval;
}
retval = ruleparam_db_load( &(ctx->ruleparam_db), ctx->ruleparam_db_fn );
if ( !ctx->ruleparam_db )
{
fprintf(stderr, "ruleparam_db_load failed (retval %i).\n", retval);
detach_from_passdb(ctx);
return retval;
}
ruleparam_seq = ctx->ruleparam_db->seq;
for (i=0; in_one_cred_bank; i++)
{
retval =
init_mmap_passfile( &(ctx->passes_one_cred_bank_fd[i]),
&(ctx->rider_one_cred_bank[i]),
&(ctx->rider_one_file_size),
RIDER_ONE_CRED_SIZE,
ctx->one_cred_db_bank_fn[i] );
if (retval != 0)
{
fprintf(stderr, "ERROR: attach_to_passdb one credential: %s\n", ctx->one_cred_db_bank_fn[i]);
return retval;
}
read_n_one_cred = ctx->rider_one_file_size / RIDER_ONE_CRED_SIZE ;
if (read_n_one_cred != ctx->n_one_cred_bank_size )
{
fprintf(stderr, "WARNING: n_one_cred in config file (%i) does not match n_one_cred (%i) in db file (%s). Using db file as truth\n",
ctx->n_one_cred_bank_size, read_n_one_cred, ctx->one_cred_db_bank_fn[i] );
}
}
for (i=0; in_two_cred_bank; i++)
{
retval =
init_mmap_passfile( &(ctx->passes_two_cred_bank_fd[i]),
&(ctx->rider_two_cred_bank[i]),
&(ctx->rider_two_file_size),
RIDER_TWO_CRED_SIZE,
ctx->two_cred_db_bank_fn[i] );
if (retval != 0)
{
fprintf(stderr, "ERROR: attach_to_passdb two credential: %s\n", ctx->two_cred_db_bank_fn[i]);
return retval;
}
read_n_two_cred = ctx->rider_two_file_size / RIDER_TWO_CRED_SIZE ;
if (read_n_two_cred != ctx->n_two_cred_bank_size )
{
fprintf(stderr, "WARNING: n_two_cred in config file (%i) does not match n_two_cred (%i) in db file (%s). Using db file as truth\n",
ctx->n_two_cred_bank_size, read_n_two_cred, ctx->two_cred_db_bank_fn[i] );
}
}
for (i=0; in_spillover_bank; i++)
{
retval =
init_mmap_passfile( &(ctx->passes_spillover_bank_fd[i]),
&(ctx->rider_spillover_bank[i]),
&(ctx->rider_spillover_file_size),
RIDER_SPILLOVER_SIZE,
ctx->spillover_db_bank_fn[i] );
if (retval != 0)
{
fprintf(stderr, "ERROR: attach_to_passdb spillover: %s\n", ctx->spillover_db_bank_fn[i]);
return retval;
}
read_n_spillover = ctx->rider_spillover_file_size / RIDER_SPILLOVER_SIZE ;
if (read_n_spillover != ctx->n_spillover_bank_size )
{
fprintf(stderr, "WARNING: n_spillover in config file (%i) does not match n_spillover (%i) in db file (%s). Using db file as truth\n",
ctx->n_spillover_bank_size, read_n_spillover, ctx->spillover_db_bank_fn[i] );
}
}
//-------------
retval = load_one_cred(ctx);
if (retval != 0)
{
fprintf(stderr, "load_one_cred failed.\n");
detach_from_passdb(ctx);
return retval;
}
retval = load_two_cred(ctx);
if (retval != 0)
{
fprintf(stderr, "load_two_cred failed.\n");
detach_from_passdb(ctx);
return retval;
}
retval = load_spillover(ctx);
if (retval != 0)
{
fprintf(stderr, "load_spillover failed.\n");
detach_from_passdb(ctx);
return retval;
}
retval = build_hashes(ctx);
if( DB_FAIL(retval) )
{
fprintf(stderr, "Building hashes failed.\n");
detach_from_passdb(ctx);
return retval;
}
if (ruleparam_seq > 0)
{
// ruleparam.db didn't get a chance to flush
// after a pass db update. We set the current sequence
// number to be the one we recorded from the old
// ruleparma.db so that we pick up updates from the
// lost caedential and we go through and delete
// the old entries with sequence numbers older than
// ruleparam_db->seq.
//
// To be safe, we ake the minimum of the two sequence numbers
// as truth.
//
if (ruleparam_seq < ctx->seq)
{
//DEBUG
fprintf(stderr, "WARNING: passdb.attach_to_passdb discr. in ruleparam_seq (%llu) and mem seq (%llu): removing newer seq numbers\n", ruleparam_seq, ctx->seq);
ctx->seq = ruleparam_seq;
passdb_slim_delete_seq_above( ctx, ctx->seq );
}
}
ctx->mmap_broken = 1;
numfree = ctx->num_free;
numact = ctx->num_active;
n = numfree + numact;
// DEBUG
//
printf("Loaded and indexed %d records (%d used, %d free); Newest seq = %llu\n", n, numact, numfree, ctx->seq);
return n;
}
static int convert_magstripe( unsigned char *magstripe_code, unsigned long long *magstripe, char *magstripe_value )
{
int a;
unsigned long long b;
int cpos=0;
for ( cpos=0;
(cpos < CREDENTIAL_LEN) && ( magstripe_value[cpos] != ':' );
cpos++);
if (cpos == CREDENTIAL_LEN)
{
return -1;
}
magstripe_value[cpos] = '\0';
a = atoi(magstripe_value);
b = (unsigned long long)atoll( magstripe_value + cpos + 1 );
magstripe_value[cpos] = ':';
if ((a < 0) || (a > 255))
{
return -1;
}
*magstripe_code = (unsigned char)a;
*magstripe = b;
return 0;
}
static int convert_rfid( unsigned char *rfid_code,
unsigned long *rfid_site,
unsigned long *rfid_val,
char *rfid_value )
{
int a;
unsigned long b, c;
int cpos0, cpos1;
for ( cpos0=0;
(cpos0 < CREDENTIAL_LEN) && (rfid_value[cpos0] != ':');
cpos0++);
if (cpos0 == CREDENTIAL_LEN)
return -1;
for ( cpos1=cpos0+1;
(cpos1 < CREDENTIAL_LEN) && (rfid_value[cpos1] != ':');
cpos1++);
if (cpos1 == CREDENTIAL_LEN)
return -1;
rfid_value[cpos0] = '\0';
rfid_value[cpos1] = '\0';
a = atoi(rfid_value);
b = (unsigned long )atol(rfid_value + cpos0 + 1);
c = (unsigned long )atol(rfid_value + cpos1 + 1);
rfid_value[cpos0] = ':';
rfid_value[cpos1] = ':';
if ((a < 0) || (a > 255))
return -1;
*rfid_code = (unsigned char)a;
*rfid_site = (unsigned long)b;
*rfid_val = (unsigned long)c;
return 0;
}
static int convert_single_credential( rider_record_slim_one_cred *rr1, rider_record *rec )
{
unsigned char magstripe_code, rfid_code;
unsigned long long magstripe;
unsigned long rfid_site, rfid_val;
unsigned long long tsite, tval;
int r;
r = convert_magstripe( &magstripe_code, &magstripe, rec->magstripe_value );
if ( (r == 0) &&
((magstripe_code != 0) || (magstripe != 0)) )
{
rr1->code = magstripe_code;
rr1->credential = magstripe;
return 0;
}
r = convert_rfid( &rfid_code, &rfid_site, &rfid_val, rec->rfid_value );
if ( (r == 0) &&
((rfid_code != 0) || (rfid_site != 0) || (rfid_val != 0)) )
{
rr1->code = rfid_code;
tsite = rfid_site;
tval = rfid_val;
rr1->credential = (tsite << 32) | tval;
return 0;
}
return -1;
}
static int copy_rider_one_cred( passdb_slim_context *ctx, int idx, rider_record_slim_one_cred *rr1 )
{
void *p;
p = passdb_slim_get_record_address( ctx, idx );
if (!p)
{
fprintf(stderr, "ERROR: copy_rider_one_cred, got NULL address, bad index? (idx %i)\n", idx);
return -1;
}
//*((seq_t *)p) = rr1->seq;
_ulliw(p, rr1->seq);
p += sizeof(seq_t);
//*((logical_card_id_t *)p) = rr1->id;
_ulliw(p, rr1->id);
p += sizeof(logical_card_id_t);
*((unsigned char *)p) = rr1->code;
p += sizeof(unsigned char);
//*((unsigned long long *)p) = rr1->credential;
_ulliw(p, rr1->credential);
p += sizeof(unsigned long long);
//*((unsigned short int *)p) = rr1->rule_param_bucket_id;;
_usiw(p, rr1->rule_param_bucket_id);
//p += sizeof(unsigned short int );
return 0;
}
static int copy_rider_two_cred( passdb_slim_context *ctx, int idx, rider_record_slim_two_cred *rr2 )
{
void *p;
p = passdb_slim_get_record_address( ctx, idx );
if (!p)
{
fprintf(stderr, "ERROR: copy_rider_two_cred, got NULL address, bad index? (idx %i)\n", idx);
return -1;
}
//*((seq_t *)p) = rr2->seq;
_ulliw(p, rr2->seq);
p += sizeof(seq_t);
//*((logical_card_id_t *)p) = rr2->id;
_ulliw(p, rr2->id);
p += sizeof(logical_card_id_t);
*((unsigned char *)p) = rr2->magstripe_code;
p += sizeof(unsigned char);
//*((unsigned long long *)p) = rr2->magstripe;
_ulliw(p, rr2->magstripe);
p += sizeof(unsigned long long);
*((unsigned char *)p) = rr2->rfid_code;
p += sizeof(unsigned char );
//*((unsigned long *)p) = rr2->rfid_site;
_uliw(p, rr2->rfid_site);
p += sizeof(unsigned long );
//*((unsigned long *)p) = rr2->rfid_val;
_uliw(p, rr2->rfid_val);
p += sizeof(unsigned long );
//*((unsigned short int *)p) = rr2->rule_param_bucket_id;;
_usiw(p, rr2->rule_param_bucket_id);
//p += sizeof(unsigned short int );
return 0;
}
static int copy_rider_spillover( passdb_slim_context *ctx, int idx, rider_record *rec )
{
void *p;
p = passdb_slim_get_record_address( ctx, idx );
if (!p)
{
fprintf(stderr, "ERROR: copy_rider_spillover, got NULL address, bad index? (idx %i)\n", idx);
return -1;
}
memcpy(p, rec, sizeof(rider_record) );
return 0;
}
// Take rider as stored in rider record, store in either one_cred or two_cred
// DB, depending on content.
//
static int copy_rider( passdb_slim_context *ctx, int idx, rider_record *src)
{
int rule_param_bucket_id;
rider_record_slim_one_cred rr1 = {0};
rider_record_slim_two_cred rr2 = {0};
if(! (src) )
return FAIL_PARAM;
// find rule param bucket
//
rule_param_bucket_id =
ruleparam_db_find( ctx->ruleparam_db, src->rule_name, src->rule_param );
if ( rule_param_bucket_id == RULEPARAM_DB_NOT_FOUND )
{
fprintf(stderr, "ERROR: copy_rider: rule_param_bucket_id for %s %s not found\n", src->rule_name, src->rule_param);
return rule_param_bucket_id;
}
else if (rule_param_bucket_id == RULEPARAM_DB_FULL )
{
fprintf(stderr, "ERROR: copy_rider: RULEPARAM DB FULL!\n");
return rule_param_bucket_id;
}
if (idx < INDEX_MIDPOINT)
{
rr1.rule_param_bucket_id = (unsigned short int)rule_param_bucket_id;
rr1.seq = src->seq;
rr1.id = src->id;
convert_single_credential( &rr1, src );
copy_rider_one_cred( ctx, idx, &rr1 );
}
else if (idx < (2*INDEX_MIDPOINT))
{
rr2.rule_param_bucket_id = (unsigned short int)rule_param_bucket_id;
rr2.seq = src->seq;
rr2.id = src->id;
convert_magstripe( &(rr2.magstripe_code), &(rr2.magstripe), src->magstripe_value );
convert_rfid( &(rr2.rfid_code), &(rr2.rfid_site), &(rr2.rfid_val), src->rfid_value );
copy_rider_two_cred( ctx, idx, &rr2 );
}
else
{
copy_rider_spillover( ctx, idx, src );
}
return 0;
}
static int alloc_rider_one_cred(passdb_slim_context *ctx)
{
rider_node *p;
p = ctx->freelist_one_cred;
if(p)
{
ctx->freelist_one_cred = ctx->freelist_one_cred->next;
p->next = ctx->activelist;
ctx->activelist = p;
ctx->n_one_cred++;
return p->idx;
}
else
{
return FAIL_FULL;
}
}
static int alloc_rider_two_cred(passdb_slim_context *ctx)
{
rider_node *p;
p = ctx->freelist_two_cred;
if(p)
{
ctx->freelist_two_cred = ctx->freelist_two_cred->next;
p->next = ctx->activelist;
ctx->activelist = p;
ctx->n_two_cred++;
return p->idx;
}
else
{
return FAIL_FULL;
}
}
static int alloc_rider_spillover(passdb_slim_context *ctx)
{
rider_node *p;
p = ctx->freelist_spillover;
if(p)
{
ctx->freelist_spillover = ctx->freelist_spillover->next;
p->next = ctx->activelist;
ctx->activelist = p;
ctx->n_spillover++;
return p->idx;
}
else
{
return FAIL_FULL;
}
}
static int free_rider(passdb_slim_context *ctx, int idx)
{
rider_node *p, *q;
void *record_p;
q = NULL;
p = ctx->activelist;
while(p)
{
if( p->idx == idx )
break;
q = p;
p = p->next;
}
if(p)
{
if(q)
{
q->next = p->next;
}
else
{
ctx->activelist = p->next;
}
if ( idx < INDEX_MIDPOINT )
{
p->next = ctx->freelist_one_cred;
ctx->freelist_one_cred = p;
}
else if (idx < (2*INDEX_MIDPOINT))
{
p->next = ctx->freelist_two_cred;
ctx->freelist_two_cred = p;
}
else
{
p->next = ctx->freelist_spillover;
ctx->freelist_spillover = p;
}
// ID_INVALID _should_ be 0
//
record_p = passdb_slim_get_record_address( ctx, idx );
if (!record_p)
return -1;
if ( idx < INDEX_MIDPOINT )
{
memset( record_p, 0, sizeof(RIDER_ONE_CRED_SIZE) );
}
else if (idx < (2*INDEX_MIDPOINT))
{
memset( record_p, 0, sizeof(RIDER_ONE_CRED_SIZE) );
}
else
{
memset( record_p, 0, sizeof(RIDER_SPILLOVER_SIZE) );
}
return 0;
}
else
{
return WARN_NOTFOUND;
}
}
static void sync_rider_change(passdb_slim_context *ctx, int idx)
{
int bank=-1, pos=-1;
int offset;
int offset_next;
int retval;
int n_pages = 1;
int tmpa, tmpb;
retval=-1;
if(idx < 0)
return;
if(!ctx)
return;
passdb_slim_get_cred_bank_and_pos( ctx, &bank, &pos, idx );
if ( idx < INDEX_MIDPOINT )
{
offset = (pos * RIDER_ONE_CRED_SIZE) / MEMORY_PAGE_SIZE; // calculate the beginning page number
offset *= MEMORY_PAGE_SIZE; // multiply by page size
retval = lseek(ctx->passes_one_cred_bank_fd[bank], offset, SEEK_SET);
offset_next = (((pos+1)*RIDER_ONE_CRED_SIZE) - 1) / MEMORY_PAGE_SIZE;
offset_next *= MEMORY_PAGE_SIZE;
if (offset_next != offset )
{
n_pages = 2;
}
if(retval != offset)
{
fprintf(stderr, "lseek() failed in sync_rider_change(). errno = %d\n", errno);
perror("one");
return;
}
retval = write(ctx->passes_one_cred_bank_fd[bank], ctx->rider_one_cred_bank[bank] + offset, n_pages * MEMORY_PAGE_SIZE);
tmpa = offset;
tmpb = offset + RIDER_ONE_CRED_SIZE;
if( retval != (n_pages*MEMORY_PAGE_SIZE) )
{
fprintf(stderr, "write() failed (one_cred) in sync_rider_change(). errno = %d\n", errno);
return;
}
fsync(ctx->passes_one_cred_bank_fd[bank]);
}
else if ( idx < (2*INDEX_MIDPOINT) )
{
offset = (pos * RIDER_TWO_CRED_SIZE) / MEMORY_PAGE_SIZE; // calculate the beginning page number
offset *= MEMORY_PAGE_SIZE; // multiply by page size
retval = lseek(ctx->passes_two_cred_bank_fd[bank], offset, SEEK_SET);
offset_next = (((pos+1)*RIDER_TWO_CRED_SIZE) - 1) / MEMORY_PAGE_SIZE;
offset_next *= MEMORY_PAGE_SIZE;
if (offset_next != offset )
{
n_pages = 2;
}
if(retval != offset)
{
fprintf(stderr, "lseek() failed in sync_rider_change(). errno = %d\n", errno);
perror("two");
return;
}
retval = write(ctx->passes_two_cred_bank_fd[bank], ctx->rider_two_cred_bank[bank] + offset, n_pages * MEMORY_PAGE_SIZE);
if ( retval != (n_pages*MEMORY_PAGE_SIZE) )
{
fprintf(stderr, "write() failed (two_cred) in sync_rider_change(). errno = %d\n", errno);
return;
}
fsync(ctx->passes_two_cred_bank_fd[bank]);
}
else
{
// SPILLOVER should be page aligned, but in case it's not, just do the same calculation as we do above
//
offset = (pos * RIDER_SPILLOVER_SIZE) / MEMORY_PAGE_SIZE; // calculate the beginning page number
offset *= MEMORY_PAGE_SIZE; // multiply by page size
retval = lseek(ctx->passes_spillover_bank_fd[bank], offset, SEEK_SET);
offset_next = (((pos+1)*RIDER_SPILLOVER_SIZE) - 1) / MEMORY_PAGE_SIZE;
offset_next *= MEMORY_PAGE_SIZE;
if (offset_next != offset )
{
n_pages = 2;
}
if(retval != offset)
{
fprintf(stderr, "lseek() failed in sync_rider_change(). errno = %d (\n", errno);
perror("three");
return;
}
retval = write(ctx->passes_spillover_bank_fd[bank], ctx->rider_spillover_bank[bank] + offset, n_pages * MEMORY_PAGE_SIZE);
if ( retval != (n_pages*MEMORY_PAGE_SIZE) )
{
fprintf(stderr, "write() failed (spillover) in sync_rider_change(). errno = %d\n", errno);
return;
}
fsync(ctx->passes_spillover_bank_fd[bank]);
}
}
void sync_all_riders(passdb_slim_context *ctx)
{
int retval;
int bank;
if(!ctx)
return;
// flush one cred first
//
for (bank=0; bankn_one_cred_bank; bank++)
{
retval = lseek(ctx->passes_one_cred_bank_fd[bank], 0, SEEK_SET);
if(retval != 0)
{
fprintf(stderr, "lseek() failed in sync_all_riders() for one credential DB (bank %i). errno = %d\n", bank, errno);
return;
}
retval = write(ctx->passes_one_cred_bank_fd[bank],
ctx->rider_one_cred_bank[bank],
ctx->n_one_cred_bank_size * RIDER_ONE_CRED_SIZE );
if(retval != (ctx->n_one_cred_bank_size * RIDER_ONE_CRED_SIZE) )
{
fprintf(stderr, "write() failed in sync_all_riders() for one credential DB (bank %i). errno = %d\n", bank, errno);
return;
}
}
// flush two cred next
//
for (bank=0; bankn_two_cred_bank; bank++)
{
retval = lseek(ctx->passes_two_cred_bank_fd[bank], 0, SEEK_SET);
if(retval != 0)
{
fprintf(stderr, "lseek() failed in sync_all_riders() for two credential DB (bank %i). errno = %d\n", bank, errno);
return;
}
retval = write(ctx->passes_two_cred_bank_fd[bank],
ctx->rider_two_cred_bank[bank],
ctx->n_two_cred_bank_size * RIDER_TWO_CRED_SIZE );
if(retval != (ctx->n_two_cred_bank_size * RIDER_TWO_CRED_SIZE) )
{
fprintf(stderr, "write() failed in sync_all_riders() for two credential DB (bank %i). errno = %d\n", bank, errno);
return;
}
}
// finall flush our spillover banks
//
for (bank=0; bankn_spillover_bank; bank++)
{
retval = lseek(ctx->passes_spillover_bank_fd[bank], 0, SEEK_SET);
if(retval != 0)
{
fprintf(stderr, "lseek() failed in sync_all_riders() for two credential DB (bank %i). errno = %d\n", bank, errno);
return;
}
retval = write(ctx->passes_spillover_bank_fd[bank],
ctx->rider_spillover_bank[bank],
ctx->n_spillover_bank_size * RIDER_SPILLOVER_SIZE );
if(retval != (ctx->n_spillover_bank_size * RIDER_SPILLOVER_SIZE) )
{
fprintf(stderr, "write() failed in sync_all_riders() for two credential DB (bank %i). errno = %d\n", bank, errno);
return;
}
}
}
int delete_rider(passdb_slim_context *ctx, rider_record *rec, int sync)
{
int bank;
int id_idx;
void *rider_p;
rider_record rr;
if(!ctx)
{
return FAIL_PARAM;
}
if (!ctx->rider_one_cred_bank) return FAIL_PARAM;
if (!ctx->rider_two_cred_bank) return FAIL_PARAM;
for (bank=0; bankn_one_cred_bank; bank++)
if (!ctx->rider_one_cred_bank[bank]) return FAIL_PARAM;
for (bank=0; bankn_two_cred_bank; bank++)
if (!ctx->rider_two_cred_bank[bank]) return FAIL_PARAM;
for (bank=0; bankn_spillover_bank; bank++)
if (!ctx->rider_spillover_bank[bank]) return FAIL_PARAM;
//If this record is older than out current database, ignore it as a duplicate.
//
if( ctx->seq >= rec->seq )
{
return 0;
}
//find the record to be deleted in our ID hash
//
id_idx = find_id_in_hash(ctx, rec->id);
//If we didn't find it, it must have already been deleted...
//
if(id_idx < 0)
{
return 0;
}
make_rider_record( ctx, &rr, id_idx );
//delete it from all hashes
//
delete_from_id_hash(ctx, id_idx);
delete_from_mag_hash(ctx, id_idx);
delete_from_rf_hash(ctx, id_idx);
//free the record (this zeros out the entire block)
//
free_rider(ctx, id_idx);
passdb_slim_manage_rider_banks(ctx);
rider_p = passdb_slim_get_record_address( ctx, id_idx );
if (!rider_p)
return -1;
if (id_idx < INDEX_MIDPOINT )
{
ctx->n_one_cred--;
memset( rider_p, 0, RIDER_ONE_CRED_SIZE );
}
else if (id_idx < (2*INDEX_MIDPOINT) )
{
ctx->n_two_cred--;
memset( rider_p, 0, RIDER_TWO_CRED_SIZE );
}
else
{
ctx->n_spillover--;
memset( rider_p, 0, RIDER_SPILLOVER_SIZE );
}
ctx->num_active--;
ctx->num_free++;
ctx->ruleparam_db->seq = ctx->seq;
ruleparam_db_update( ctx->ruleparam_db, rr.rule_name, rr.rule_param, -1 );
ruleparam_db_rate_limited_sync( ctx->ruleparam_db );
//and sync our SHM
//
if(sync)
{
sync_rider_change(ctx, id_idx);
}
return 1;
}
// Return the number of non blank credentials
//
int rider_count_credential( rider_record *rec )
{
int mag_flag = 0;
int rfid_flag = 0;
int count = 0;
int s, e;
int r;
unsigned char dummy_code;
unsigned long dummy_site, dummy_rfid;
unsigned long long dummy_mag;
for (s=0; (smagstripe_value[s] == ' ') ; s++);
for (e=s+1; (emagstripe_value[e]) ; e++ );
if ((e - s) > 1) mag_flag = 1;
for (s=0; (srfid_value[s] == ' ') ; s++);
for (e=s+1; (erfid_value[e]) ; e++ );
if ((e - s) > 1) rfid_flag = 1;
if (mag_flag)
{
r = convert_magstripe( &dummy_code, &dummy_mag, rec->magstripe_value );
if (r < 0)
return r;
count++;
}
if (rfid_flag)
{
r = convert_rfid( &dummy_code, &dummy_site, &dummy_rfid, rec->rfid_value );
if (r < 0)
return r;
count++;
}
return count;
}
// Called from pass_communication on an UPDATE message
//
int update_rider(passdb_slim_context *ctx, rider_record *rec, int sync)
{
int id_idx;
int mag_idx;
int rf_idx;
int update_credentials = 0;
int update_id_hash = 0;
int retval;
int credential_count = 0;
int old_credential_count = 0;
int is_new_record = 0;
int bank;
int decrement_old_rule_ref = 0;
rider_record rr = {0};
if(!ctx)
{
return FAIL_MEM;
}
if (!ctx->rider_one_cred_bank) return FAIL_PARAM;
if (!ctx->rider_two_cred_bank) return FAIL_PARAM;
for (bank=0; bankn_one_cred_bank; bank++)
if (!ctx->rider_one_cred_bank[bank]) return FAIL_PARAM;
for (bank=0; bankn_two_cred_bank; bank++)
if (!ctx->rider_two_cred_bank[bank]) return FAIL_PARAM;
if( ctx->seq >= rec->seq )
{
return 0;
}
credential_count = rider_count_credential( rec );
old_credential_count = credential_count;
if (credential_count == 0)
{
return 0;
}
id_idx = find_id_in_hash(ctx, rec->id);
mag_idx = find_mag_in_hash(ctx, rec->magstripe_value);
rf_idx = find_rf_in_hash(ctx, rec->rfid_value);
// We want to allow a short period of magstrip or RFID collision as the lesser of two evils vs. **STUPID**
// possibly losing a record due to a degenerately stupid administrator doing the following:
//
// 1) Create user 1 with magstripe '1:foo'
// 2) Delete user 1
// 3) Create user 2 with magstripe '1:foo'
// 4) Create user 1 with magstripe '1:bar' <---- THIS IS NOT ALLOWED (Creating user 1 is dissalowed after a delete of user 1. This card should be created as user 3).
//
// The issue here is that if the bus asks what's happensed since sequence number 1, it will get rows
// 3 and 4.
//
// In reality, we'd hope that each bus would complete a sync at least once on a shorter interval
// than the frequency at which credentials are recycled, but you never know... And if somebody manually
// fucks things up such that a user id (card id) is deleted, and then created again (this is a big no-no), we
// can recover by allowing a hash collision to exist in the meantime.
#ifndef ALLOW_CREDENTIAL_COLLISIONS
if( (mag_idx >= 0) && (mag_idx != id_idx) )
{
//fprintf(stderr, "Refusing to accept change that would introduce duplicate magstripe \"%s\" for records %llu and %llu.\n", rec->magstripe_value, ctx->riders[mag_idx].id, rec->id);
make_rider_record( ctx, &rr, id_idx );
fprintf(stderr, "Refusing to accept change that would introduce duplicate magstripe \"%s\" for records %llu and %llu.\n", rec->magstripe_value, rr.id, rec->id);
return FAIL_DUPKEY;
}
if( (rf_idx >= 0) && (rf_idx != id_idx) )
{
make_rider_record( ctx, &rr, id_idx );
fprintf(stderr, "Refusing to accept change that would introduce duplicate RFID \"%s\" for records %llu and %llu.\n", rec->rfid_value, rr.id, rec->id);
return FAIL_DUPKEY;
}
#endif
if(id_idx >= 0) //if rec->id already exists, we're updating an existing record...
{
// If EITHER the RFID or MAGSTRIPE values have changed...
//
make_rider_record( ctx, &rr, id_idx );
if( strncmp(rr.magstripe_value, rec->magstripe_value, CREDENTIAL_LEN) ||
strncmp(rr.rfid_value, rec->rfid_value, CREDENTIAL_LEN) )
{
update_credentials = 1;
}
// EXPERIMENTAL
//
if ( id_idx < INDEX_MIDPOINT ) { old_credential_count = 1; }
else if ( id_idx < 2*INDEX_MIDPOINT ) { old_credential_count = 2; }
else { old_credential_count = -1; }
/*
if ( strncmp( rr.magstripe_value , rec->magstripe_value, CREDENTIAL_LEN ) != 0 )
update_magstrip_credential = 1;
if ( strncmp( rr.rfid_value, rec->rfid_value, CREDENTIAL_LEN ) != 0 )
update_rfid_credential = 1;
*/
// For simplicity, decrement the reference count of the rule for the pre-existing
// rule record. The 'new' rule record will be incremented below. If the 'new'
// rule record is the same, this has the effect of not changing the reference count.
// If there is only one reference, this will delete and re-create it, but we
// expect rule records to most often have more than just a single reference, so
// this should be a case that is not hit very often.
// If the rule record has changed, this will correctly decrement the reference
// to the old rule record here, and increment the reference count of the new
// rule record below.
//
decrement_old_rule_ref = 1;
}
// EXPERIMENTAL
//
// If we've changed credential count (most likely 1 to 2 or 2 to 1),
// then we need to switch the database type (e.g. from the one credential
// database banks to the two credential database banks).
// Delete the old entry of the rider, both on disk and in our local
// hashes in memory, allocate a new index, then proceed as normal.
//
// If this is a new record, old_credential_count is default
// equal to credential count, so this block gets ignored.
//
if ( credential_count != old_credential_count )
{
// Delete old entry completely. delete_rider will
// update counts and delete the index from the hashes.
//
// We need to 'trick' delete_rider into accepting the change
// by setting the sequence number to be the current one from
// the fetched record. delete_rider also updates (decmeents etc.)
// the rule, so we don't need to do it later.
//
rr.seq = rec->seq;
delete_rider( ctx, &rr, 1 );
decrement_old_rule_ref = 0;
// Create new index and update counts
//
if ( credential_count == 1 ) { id_idx = alloc_rider_one_cred(ctx); }
else if ( credential_count == 2 ) { id_idx = alloc_rider_two_cred(ctx); }
else { id_idx = alloc_rider_spillover(ctx); }
update_credentials = update_id_hash = 1;
}
if (id_idx < 0) // we've got a new record
//else // otherwise, we're creating a new record...
{
if ( credential_count == 1 ) { id_idx = alloc_rider_one_cred(ctx); }
else if ( credential_count == 2 ) { id_idx = alloc_rider_two_cred(ctx); }
else { id_idx = alloc_rider_spillover(ctx); }
if(DB_FAIL(id_idx))
{
fprintf(stderr, "Error (%d) trying to allocate rider\n", id_idx);
return id_idx;
}
ctx->num_active++;
ctx->num_free--;
update_credentials = update_id_hash = 1;
is_new_record = 1;
}
if(update_credentials)
{
delete_from_mag_hash(ctx, id_idx);
delete_from_rf_hash(ctx, id_idx);
}
if(update_id_hash)
{
delete_from_id_hash(ctx, id_idx);
}
// Update our reference count to the rule/rule param for this
// rider record. ruleparam_db_update will automatically create
// a new rule if necessary and, if a new rule is created, will
// synchronize to it's database.
//
// We need to update ruleparam_db so that the record will be found
// by copy_record. After it's been updated, we can decrement
// the reference count if need be.
//
// At the very end, update ruleparam_db context with most recent
// sequence number and save the sequence to disk.
//
// - If we die after update but before copy_record completes,
// we have old sequence number, at worst a new rule/param will be inserted
// - If we die after copy_record, the record will have newer sequence number
// and get deleted, waiting for a replay
// - If we die after rule/param decrement, and the rule gets removed from ruleparam.db,
// credential will also get removed as it has newer sequnce number.
// - once we write sequence number, we're in a consistent state
//
ruleparam_db_update( ctx->ruleparam_db, rec->rule_name, rec->rule_param, 1 );
#ifdef PASSDB_CONSISTENCY_CHECK
retval = ruleparam_db_consistency_check( ctx->ruleparam_db );
if (retval < 0)
{
fprintf(stderr, "CONSISTENCY CHECK failed in update_rider: got %i\n", retval );
}
#endif
copy_rider( ctx, id_idx, rec );
if (decrement_old_rule_ref)
{
ruleparam_db_update( ctx->ruleparam_db, rr.rule_name, rr.rule_param, -1 );
#ifdef PASSDB_CONSISTENCY_CHECK
retval = ruleparam_db_consistency_check( ctx->ruleparam_db );
if (retval < 0)
{
fprintf(stderr, "CONSISTENCY CHECK failed in update_rider: got %i\n", retval );
}
#endif
}
passdb_slim_manage_rider_banks(ctx);
if(sync)
{
sync_rider_change(ctx, id_idx);
}
if(update_id_hash)
{
retval = add_to_id_hash(ctx, id_idx);
if( !DB_OKAY(retval) )
{
fprintf(stderr, "Error (%d) updating id hash for record seq = %llu\n", retval, (unsigned long long)rec->seq);
}
}
if(update_credentials)
{
retval = add_to_mag_hash(ctx, id_idx);
if( !DB_OKAY(retval) )
{
fprintf(stderr, "Error (%d) updating magstripe hash for record seq = %llu\n", retval, (unsigned long long)rec->seq);
}
retval = add_to_rf_hash(ctx, id_idx);
if( !DB_OKAY(retval) )
{
fprintf(stderr, "Error (%d) updating rf hash for record seq = %llu\n", retval, (unsigned long long)rec->seq);
}
}
ctx->seq = rec->seq;
// Rate limited sync always updates sequence number file,
// occassionally updates ruleparam.db. ruleparam.db updated
// above with relevant information if we added or removed
// rules.
//
ctx->ruleparam_db->seq = ctx->seq;
ruleparam_db_rate_limited_sync( ctx->ruleparam_db );
return 1;
}
void dump_hashes(passdb_slim_context *ctx)
{
int i;
rider_node *p;
rider_record rr = {0};
if(!ctx)
{
printf("NULL Context!\n");
return;
}
printf("ID HASH[%i]:\n", ctx->hash_modulus);
for(i=0; i < ctx->hash_modulus; i++)
{
if(!ctx->logical_card_id_hash[i]) continue;
printf("\t%d:", i);
p = ctx->logical_card_id_hash[i];
while(p)
{
make_rider_record(ctx, &rr, p->idx);
printf(" [%d] %llu", p->idx, rr.id );
p = p -> next;
}
printf("\n");
}
printf("RFID HASH[%i]:\n", ctx->hash_modulus);
for(i=0; i < ctx->hash_modulus; i++)
{
if(!ctx->rider_rf_hash[i]) continue;
printf("\t%d:", i);
p = ctx->rider_rf_hash[i];
while(p)
{
make_rider_record(ctx, &rr, p->idx);
printf(" [%d] %llu", p->idx, rr.id );
p = p -> next;
}
printf("\n");
}
printf("MAGSTRIPE HASH[%i]:\n", ctx->hash_modulus);
for(i=0; i < ctx->hash_modulus; i++)
{
if(!ctx->rider_mag_hash[i]) continue;
printf("\t%d:", i);
p = ctx->rider_mag_hash[i];
while(p)
{
make_rider_record(ctx, &rr, p->idx);
printf(" [%d] %llu", p->idx, rr.id );
p = p -> next;
}
printf("\n");
}
};
//-- database expansion functions
int expand_one_cred(passdb_slim_context *ctx)
{
int i;
char fn[FN_SZ];
int cur_bank;
int fd;
void *p;
size_t file_size;
int r;
long pagesize;
rider_node *q = NULL;
pagesize = sysconf(_SC_PAGE_SIZE);
cur_bank = ctx->n_one_cred_bank;
sprintf(fn, "%s%i%s", ctx->one_cred_db_base_fn, cur_bank, ctx->db_fn_suffix );
r = format_new_passdb( fn, ctx->n_one_cred_bank_size * RIDER_ONE_CRED_SIZE, pagesize );
if (r<0)
return r;
r = init_mmap_passfile( &fd, &p, &file_size, RIDER_ONE_CRED_SIZE, fn );
if (r<0)
return r;
for (i=0; i < ctx->n_one_cred_bank_size; i++)
{
q = (rider_node *) malloc( sizeof(rider_node) );
if (!q)
{
fprintf(stderr, "FAIL_MEM: expand_one_cred\n");
return FAIL_MEM;
}
q->next = ctx->freelist_one_cred;
q->idx = i + (cur_bank * ctx->n_one_cred_bank_size);
ctx->freelist_one_cred = q;
ctx->num_free++;
}
ctx->passes_one_cred_bank_fd[cur_bank] = fd;
ctx->rider_one_cred_bank[cur_bank] = p;
snprintf( ctx->one_cred_db_bank_fn[cur_bank] , FN_SZ, "%s", fn );
ctx->n_one_cred_bank++;
return 0;
}
// unused and untested
// EXPERIMENTAL:
// We only expand. In the future we might want to contract, so this
// function is left here for a starting point. Contraction still
// needs work, so only use this as a starting point.
//
/*
int contract_one_cred(passdb_slim_context *ctx)
{
int cur_bank;
int min_idx, max_idx;
rider_node *freenod = NULL, *prevnod = NULL, *nexnod = NULL;
void *p;
rider_record rr = {0};
int old_idx, new_idx;
cur_bank = ctx->n_one_cred_bank-1;
if (cur_bank < 0) return -1;
min_idx = cur_bank * ctx->n_one_cred_bank_size ;
max_idx = (cur_bank + 1) * ctx->n_one_cred_bank_size ;
// delete all nodes in the free list that fall into the
// bank we're about to delete.
// We'll be moving current riders into lower banks
// and choosing free indexes based on this list,
// so it needs to happen first.
//
freenod = ctx->freelist_one_cred;
while (freenod)
{
nexnod = freenod->next;
if ( (freenod->idx >= min_idx) &&
(freenod->idx < max_idx) )
{
if (prevnod)
prevnod->next = nexnod;
free(freenod);
}
else
{
prevnod = freenod;
}
freenod = nexnod;
}
// Now find all records that fall within the index
// range, delete all references to it in the
// id, mag and rf hash. To be "safe", 0
// out record in the old bank.
// Allocate a new index (from the lower banks)
// and put it back in.
//
freenod = ctx->activelist;
while (freenod)
{
nexnod = freenod->next;
if ( (freenod->idx < min_idx) &&
(freenod->idx >= max_idx) )
{
prevnod = freenod;
freenod = nexnod;
continue;
}
old_idx = freenod->idx;
make_rider_record( ctx, &rr, old_idx );
delete_from_id_hash(ctx, old_idx);
delete_from_mag_hash(ctx, old_idx);
delete_from_rf_hash(ctx, old_idx);
//just in case?
p = passdb_slim_get_record_address( ctx, old_idx );
memset(p, 0, sizeof(char)*RIDER_ONE_CRED_SIZE );
new_idx = alloc_rider_one_cred( ctx );
copy_rider( ctx, new_idx, &rr );
free(freenod);
if (prevnod)
prevnod->next = nexnod;
freenod = nexnod;
}
// finally, munmap the file, free the relevant memory
// and decrement the number of one credential banks.
//
munmap( ctx->rider_one_cred_bank[cur_bank], ctx->n_one_cred_bank_size * RIDER_ONE_CRED_SIZE );
ctx->rider_one_cred_bank[cur_bank] = NULL;
close(ctx->passes_two_cred_bank_fd[cur_bank]);
ctx->passes_one_cred_bank_fd[cur_bank] = -1;
free( ctx->one_cred_db_bank_fn[cur_bank] );
ctx->one_cred_db_bank_fn[cur_bank] = NULL;
ctx->n_one_cred_bank--;
return 0;
}
*/
//-----
int expand_two_cred(passdb_slim_context *ctx)
{
int i;
char fn[FN_SZ];
int cur_bank;
int fd;
void *p;
size_t file_size;
int r;
long pagesize;
rider_node *q = NULL;
pagesize = sysconf(_SC_PAGE_SIZE);
cur_bank = ctx->n_two_cred_bank;
sprintf(fn, "%s%i%s", ctx->two_cred_db_base_fn, cur_bank, ctx->db_fn_suffix );
r = format_new_passdb( fn, ctx->n_two_cred_bank_size * RIDER_TWO_CRED_SIZE, pagesize );
if (r<0)
return r;
r = init_mmap_passfile( &fd, &p, &file_size, RIDER_TWO_CRED_SIZE, fn );
if (r<0)
return r;
for (i=0; i < ctx->n_two_cred_bank_size; i++)
{
q = (rider_node *) malloc( sizeof(rider_node) );
if (!q)
{
fprintf(stderr, "FAIL_MEM: expand_two_cred\n");
return FAIL_MEM;
}
q->next = ctx->freelist_two_cred;
q->idx = INDEX_MIDPOINT + i + (cur_bank * ctx->n_two_cred_bank_size);
ctx->freelist_two_cred = q;
ctx->num_free++;
}
ctx->passes_two_cred_bank_fd[cur_bank] = fd;
ctx->rider_two_cred_bank[cur_bank] = p;
snprintf( ctx->two_cred_db_bank_fn[cur_bank] , FN_SZ, "%s", fn );
ctx->n_two_cred_bank++;
return 0;
}
// unused and untested
//
// EXPERIMENTAL:
// We only expand. In the future we might want to contract, so this
// function is left here for a starting point. Contraction still
// needs work, so only use this as a starting point.
//
/*
int contract_two_cred(passdb_slim_context *ctx)
{
int cur_bank;
int min_idx, max_idx;
rider_node *freenod = NULL, *prevnod = NULL, *nexnod = NULL;
void *p;
rider_record rr = {0};
int old_idx, new_idx;
cur_bank = ctx->n_two_cred_bank-1;
if (cur_bank < 0) return -1;
min_idx = cur_bank * ctx->n_two_cred_bank_size ;
max_idx = (cur_bank + 1) * ctx->n_two_cred_bank_size ;
min_idx += INDEX_MIDPOINT;
max_idx += INDEX_MIDPOINT;
// delete all nodes in the free list that fall into the
// bank we're about to delete.
// We'll be moving current riders into lower banks
// and choosing free indexes based on this list,
// so it needs to happen first.
//
freenod = ctx->freelist_two_cred;
while (freenod)
{
nexnod = freenod->next;
if ( (freenod->idx >= min_idx) &&
(freenod->idx < max_idx) )
{
if (prevnod)
prevnod->next = nexnod;
free(freenod);
}
else
{
prevnod = freenod;
}
freenod = nexnod;
}
// Now find all records that fall within the index
// range, delete all references to it in the
// id, mag and rf hash. To be "safe", 0
// out record in the old bank.
// Allocate a new index (from the lower banks)
// and put it back in.
//
freenod = ctx->activelist;
while (freenod)
{
nexnod = freenod->next;
if ( (freenod->idx < min_idx) &&
(freenod->idx >= max_idx) )
{
prevnod = freenod;
freenod = nexnod;
continue;
}
old_idx = freenod->idx;
make_rider_record( ctx, &rr, old_idx );
delete_from_id_hash(ctx, old_idx);
delete_from_mag_hash(ctx, old_idx);
delete_from_rf_hash(ctx, old_idx);
//just in case?
p = passdb_slim_get_record_address( ctx, old_idx );
memset(p, 0, sizeof(char)*RIDER_ONE_CRED_SIZE );
new_idx = alloc_rider_two_cred( ctx );
copy_rider( ctx, new_idx, &rr );
free(freenod);
if (prevnod)
prevnod->next = nexnod;
freenod = nexnod;
}
// finally, munmap the file, free the relevant memory
// and decrement the number of two credential banks.
//
munmap( ctx->rider_two_cred_bank[cur_bank], ctx->n_two_cred_bank_size * RIDER_TWO_CRED_SIZE );
ctx->rider_two_cred_bank[cur_bank] = NULL;
close(ctx->passes_two_cred_bank_fd[cur_bank]);
ctx->passes_two_cred_bank_fd[cur_bank] = -1;
free( ctx->two_cred_db_bank_fn[cur_bank] );
ctx->two_cred_db_bank_fn[cur_bank] = NULL;
ctx->n_two_cred_bank--;
return 0;
}
*/
// ---
// SPILLOVER
//
int expand_spillover(passdb_slim_context *ctx)
{
int i;
char fn[FN_SZ];
int cur_bank;
int fd;
void *p;
size_t file_size;
int r;
long pagesize;
rider_node *q = NULL;
pagesize = sysconf(_SC_PAGE_SIZE);
cur_bank = ctx->n_spillover_bank;
sprintf(fn, "%s%i%s", ctx->spillover_db_base_fn, cur_bank, ctx->db_fn_suffix );
r = format_new_passdb( fn, ctx->n_spillover_bank_size * RIDER_SPILLOVER_SIZE, pagesize );
if (r<0)
return r;
r = init_mmap_passfile( &fd, &p, &file_size, RIDER_SPILLOVER_SIZE, fn );
if (r<0)
return r;
for (i=0; i < ctx->n_spillover_bank_size; i++)
{
q = (rider_node *) malloc( sizeof(rider_node) );
if (!q)
{
fprintf(stderr, "FAIL_MEM: expand_spillover\n");
return FAIL_MEM;
}
q->next = ctx->freelist_spillover;
q->idx = (2*INDEX_MIDPOINT) + i + (cur_bank * ctx->n_spillover_bank_size);
ctx->freelist_spillover = q;
ctx->num_free++;
}
ctx->passes_spillover_bank_fd[cur_bank] = fd;
ctx->rider_spillover_bank[cur_bank] = p;
snprintf( ctx->spillover_db_bank_fn[cur_bank] , FN_SZ, "%s", fn );
ctx->n_spillover_bank++;
return 0;
}
int passdb_slim_manage_rider_banks(passdb_slim_context *ctx)
{
int r;
int upper_threshold;
int lower_threshold;
float hi_water;
float lo_water;
int bank_sz;
int max_bank;
int n;
int n_bank;
int save_config_flag = 0;
passdb_slim_config cfg = {0};
hi_water = ctx->high_watermark_threshold;
lo_water = ctx->low_watermark_threshold;
n = ctx->n_one_cred;
n_bank = ctx->n_one_cred_bank;
bank_sz = ctx->n_one_cred_bank_size;
max_bank = ctx->n_one_cred_max_bank;
//check one credenteial watermarks
//
upper_threshold = (int)( hi_water * (float)(bank_sz * n_bank) );
lower_threshold = (int)( lo_water * (float)(bank_sz * n_bank) );
// make sure we haven't maxed out our number of banks
//
if ( n_bank < max_bank )
{
if ( ctx->n_one_cred > upper_threshold )
{
r = expand_one_cred(ctx);
if (r<0) return r;
save_config_flag = 1;
}
}
n = ctx->n_two_cred;
n_bank = ctx->n_two_cred_bank;
bank_sz = ctx->n_two_cred_bank_size;
max_bank = ctx->n_two_cred_max_bank;
//check two credenteial watermarks
//
upper_threshold = (int)( hi_water * (float)(bank_sz * n_bank) );
lower_threshold = (int)( lo_water * (float)(bank_sz * n_bank) );
// make sure we haven't maxed out our number of banks
//
if ( n_bank < max_bank )
{
if ( n > upper_threshold )
{
r = expand_two_cred(ctx);
if (r<0) return r;
save_config_flag = 1;
}
}
n = ctx->n_spillover;
n_bank = ctx->n_spillover_bank;
bank_sz = ctx->n_spillover_bank_size;
max_bank = ctx->n_spillover_max_bank;
//check two credenteial watermarks
//
upper_threshold = (int)( hi_water * (float)(bank_sz * n_bank) );
lower_threshold = (int)( lo_water * (float)(bank_sz * n_bank) );
// make sure we haven't maxed out our number of banks
//
if ( n_bank < max_bank )
{
if ( n > upper_threshold )
{
r = expand_spillover(ctx);
if (r<0) return r;
save_config_flag = 1;
}
}
// --
if (save_config_flag)
{
passdb_slim_copy_config( &cfg, ctx );
save_config( &cfg, PASSDB_SLIM_CONFIG_FILE );
}
return 0;
}
// ------------------------------------------------
// CONSISTENCY CHECK
int _consist_update_ruleparam_entry( passdb_slim_ruleparam **rule_head, rider_record *rr )
{
static int id=1;
passdb_slim_ruleparam *nod = NULL, *prv = NULL;
nod = *rule_head;
if ( !nod )
{
nod = _alloc_ruleparam_db_nod( id++, rr->rule_name, rr->rule_param );
nod->reference_count++;
*rule_head = nod;
return id;
}
while (nod)
{
if ( (strncmp( nod->name , rr->rule_name , RULENAME_LEN) == 0) &&
(strncmp( nod->param, rr->rule_param , PARAM_LEN ) == 0) )
{
nod->reference_count++;
return nod->id;
}
prv = nod;
nod = nod->next;
}
prv->next = _alloc_ruleparam_db_nod( id++, rr->rule_name, rr->rule_param );
nod = prv->next;
nod->reference_count++;
return id;
}
int passdb_slim_consistency_check( passdb_slim_context *ctx )
{
//int idx;
int acount1=0, acount2=0, acounts=0;
int fcount1=0, fcount2=0, fcounts=0;
int tot1=0, tot2=0, tots=0;
int bank_acount1=0, bank_acount2=0, bank_acounts=0;
int bank, pos;
void *rider_p;
int k;
int rule_id;
char name[1024], param[1024];
rider_node *nod;
rider_record_slim_one_cred rr_one_cred;
rider_record_slim_two_cred rr_two_cred;
rider_record rr_spillover;
rider_record rr;
passdb_slim_ruleparam *rule_head = NULL, *rule_nod = NULL;
nod = ctx->activelist;
while (nod)
{
if ( nod->idx < INDEX_MIDPOINT )
{
acount1++;
tot1++;
}
else if (nod->idx < (2*INDEX_MIDPOINT))
{
acount2++;
tot2++;
}
else
{
acounts++;
tots++;
}
nod = nod->next;
}
nod = ctx->freelist_one_cred;
while (nod)
{
tot1++;
fcount1++;
nod = nod->next;
}
nod = ctx->freelist_two_cred;
while (nod)
{
tot2++;
fcount2++;
nod = nod->next;
}
nod = ctx->freelist_spillover;
while (nod)
{
tots++;
fcounts++;
nod = nod->next;
}
// Check that the active record count for each
// type appears in the banks and the counts match up
if ( tot1 != (ctx->n_one_cred_bank * ctx->n_one_cred_bank_size) )
return -1;
if ( tot2 != (ctx->n_two_cred_bank * ctx->n_two_cred_bank_size) )
return -2;
if ( tots != (ctx->n_spillover_bank * ctx->n_spillover_bank_size) )
return -3;
if ( acount1 != ctx->n_one_cred )
return -4;
if ( acount2 != ctx->n_two_cred )
return -5;
if ( acounts != ctx->n_spillover )
return -6;
// go through each of the elements in the banks and
// make sure they appear properly in the hash
//
// OK, this is a bit hard as we don't keep the information
// about what index is associated with which entry in the actual
// bank. That is, from the active list we can infer the position
// in the bank, but from the bank we would need to search the whole
// active list.
// Instead, count all entries. If the counts match up and all
// active list entries map properly, we can then infer consistency
// between the active list and the banks.
//
for (bank=0; bank < ctx->n_one_cred_bank; bank++)
{
for (pos=0; pos < ctx->n_one_cred_bank_size; pos++)
{
rider_p = ctx->rider_one_cred_bank[bank] + (pos*RIDER_ONE_CRED_SIZE);
populate_one_cred_rider_record( &rr_one_cred, 0, rider_p );
if (rr_one_cred.id == ID_INVALID)
continue;
bank_acount1++;
}
}
for (bank=0; bank < ctx->n_two_cred_bank; bank++)
{
for (pos=0; pos < ctx->n_two_cred_bank_size; pos++)
{
rider_p = ctx->rider_two_cred_bank[bank] + (pos*RIDER_TWO_CRED_SIZE);
populate_two_cred_rider_record( &rr_two_cred, 0, rider_p );
if (rr_two_cred.id == ID_INVALID)
continue;
bank_acount2++;
}
}
for (bank=0; bank < ctx->n_spillover_bank; bank++)
{
for (pos=0; pos < ctx->n_spillover_bank_size; pos++)
{
rider_p = ctx->rider_spillover_bank[bank] + (pos*RIDER_SPILLOVER_SIZE);
populate_spillover_rider_record( &rr_spillover, 0, rider_p );
if (rr_spillover.id == ID_INVALID)
continue;
bank_acounts++;
}
}
if ( (bank_acount1 != acount1) ||
(bank_acount2 != acount2) ||
(bank_acounts != acounts) )
{
fprintf(stderr, "CONSISTENCY ERROR: bank_acount1 (%i) !=? acount1 (%i), bank_acount2 (%i) !=? acount2 (%i) bank_acounts (%i) !=? acounts (%i)\n",
bank_acount1, acount1,
bank_acount2, acount2,
bank_acounts, acounts );
return -8;
}
// Make sure all records in activelist have a valid
// entry in the appropriate bank.
//
nod = ctx->activelist;
while (nod)
{
make_rider_record( ctx, &rr, nod->idx );
if (rr.id == ID_INVALID)
return -9;
nod = nod->next;
}
// we get to this point, hash and banks match up:
// go through all active records, add to a temporary
// rule param list and count the number of references,
// make sure it matches up to the ruleparam stored
//
// construct our own version of the rule names and parameters
//
nod = ctx->activelist;
while (nod)
{
make_rider_record( ctx, &rr, nod->idx );
_consist_update_ruleparam_entry( &rule_head, &rr );
nod = nod->next;
}
// compare each reference count to what we have in memory
//
rule_nod = rule_head;
while (rule_nod)
{
if ( rule_nod->reference_count != (k=ruleparam_db_reference_count( ctx->ruleparam_db, rule_nod->name, rule_nod->param )) )
{
rule_id = ruleparam_db_find( ctx->ruleparam_db, rule_nod->name, rule_nod->param );
ruleparam_db_get( name, param, ctx->ruleparam_db, rule_id );
fprintf(stderr, "CONSISTENCY ERROR: ruleparam_db reference count error: (%s,%s %i) != (%s,%s %i)\n",
rule_nod->name, rule_nod->param, rule_nod->reference_count,
name, param, k );
return -10;
}
rule_nod = rule_nod->next;
}
// free our local copy of the rule param
//
rule_nod = rule_head ;
while (rule_nod)
{
rule_nod = rule_nod->next;
free(rule_head);
rule_head = rule_nod;
}
return 1;
}
/*
int main(int argc, char **argv)
{
passdb_slim_context ctx = {0};
int retval;
rider_record foo = {12362, 6, "SILLYRF", "FOOBAR", "STUPIDRULE", "STUPIDPARAM"};
rider_record bar = {12354, 7, "WAWAWA", "SILLYMAG", "STUPIDRULE", "STUPIDPARAM"};
rider_record baz = {12361, 8, "", "BARFOLA_MAG", "STUPIDRULE2", "STUPIDPARAM2"};
rider_record bat = {12363, 7, "", "", "", ""};
retval = attach_to_passdb(&ctx);
dump_hashes(&ctx);
retval = update_rider(&ctx, &foo);
retval = update_rider(&ctx, &bar);
retval = update_rider(&ctx, &baz);
retval = delete_rider(&ctx, &bat);
dump_hashes(&ctx);
return 0;
}
*/