diff options
Diffstat (limited to 'web/src/pubcookie/id_table.c')
-rw-r--r-- | web/src/pubcookie/id_table.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/web/src/pubcookie/id_table.c b/web/src/pubcookie/id_table.c new file mode 100644 index 00000000..1076524e --- /dev/null +++ b/web/src/pubcookie/id_table.c @@ -0,0 +1,294 @@ +#include "id_table.h" +#include "wp_uidmapper_lib.h" + +#include <sys/types.h> /* opendir */ +#include <sys/stat.h> /* stat */ +#include <stdlib.h> /* mallloc,free,strtol */ +#include <string.h> /* memcpy */ +#include <errno.h> /* errno */ + +#include <dirent.h> /* opendir */ +#include <unistd.h> /* stat */ + +unsigned long hash_func(char *string,unsigned long num_buckets) { + unsigned long i; + char *p; + for(i = 0, p = string; *p; p++) i = (9 * i) + *p; + return i % num_buckets; +} + +struct id_table_entry { + struct id_table_entry **pself; + struct id_table_entry *next; + int id; + char tmp; + char name[1]; +}; + +struct key_hash_entry { + unsigned key[WP_KEY_LEN]; + id_table_entry *e; + struct key_hash_entry **pself; + struct key_hash_entry *next; +}; + +id_table_range *id_table_range_new(char *str) { + id_table_range *rhead,*rtail; + char *p,*pn; + long s,e; + + rhead = rtail = 0; + for(p = str; *p; p = *pn ? pn + 1 : pn) { + s = strtoul(p,&pn,0); + if(pn > p) { + if(*pn == '-') { + p = pn + 1; + e = strtoul(p,&pn,0); + if(pn == p) e = s; + } else { + e = s; + } + + if(rtail) { + rtail->next = (id_table_range*)malloc(sizeof(id_table_range)); + rtail = rtail->next; + } else { + rhead = rtail = (id_table_range*)malloc(sizeof(id_table_range)); + } + rtail->start = s; + rtail->end = e; + } + } + if(rtail) rtail->next = 0; + return rhead; +} + +void id_table_range_delete(id_table_range *range) { + id_table_range *r; + while(r = range) { + range = range->next; + free(r); + } +} + +int id_table_init(id_table *t,id_table_range *range) { + id_table_range *r; + unsigned long array_end,u; + + if(!range) return -1; + t->array_start = range->start; + array_end = range->end; + for(r = range->next; r; r = r->next) { + if(r->start < t->array_start) t->array_start = r->start; + if(r->end > array_end) array_end = r->end; + } + if(t->array_start >= array_end) return -1; + t->array_size = array_end + 1 - t->array_start; + t->hash_size = t->array_size * 2 + 1; + t->key_hash_size = t->array_size * 2 + 1; + + t->array = (id_table_entry**)malloc(sizeof(id_table_entry*) * t->array_size); + t->hash = (id_table_entry**)malloc(sizeof(id_table_entry*) * t->hash_size); + t->key_hash = (key_hash_entry**)malloc(sizeof(key_hash_entry *) * t->key_hash_size); + if(!t->hash || !t->array || !t->key_hash) { + if(t->hash) free(t->hash); + if(t->key_hash) free(t->key_hash); + if(t->array) free(t->array); + return -1; + } + for(u = 0; u < t->array_size; u++) t->array[u] = (id_table_entry*)-1; + t->max_fill = 0; + for(r = range; r; r = r->next) { + t->max_fill += r->end + 1 - r->start; + for(u = r->start; u <= r->end; u++) t->array[u - t->array_start] = 0; + } + bzero(t->hash,sizeof(id_table_entry*) * t->hash_size); + bzero(t->key_hash, sizeof(key_hash_entry*) * t->key_hash_size); + + t->array_ptr = 0; + t->fill = 0; + return 0; +} + +void id_table_destroy(id_table *t) { + unsigned long u; + for(u = 0; u < t->array_size; u++) + if(t->array[u] && (t->array[u] != (id_table_entry*)-1)) free(t->array[u]); + free(t->hash); + free(t->array); +} + +int id_table_create_id(id_table *t,char *name,unsigned *key) { + id_table_entry **pe,*e; + unsigned long size; + + if(name && strlen(name)){ + for(pe = t->hash + hash_func(name,t->hash_size); *pe; pe = &(*pe)->next) + if(!strcmp(name,(*pe)->name)){ + if(key[0]){ + if((e = key_hash_get_entry(t, key)) == NULL){ + if(key_hash_create_entry(t, *pe, key) == NULL){ + return -1; + } + } + else if(e->id != (*pe)->id){ + return -1; + } + } + + return (*pe)->id; + } + + /* no matching name found */ + } + else { + if(e = key_hash_get_entry(t,key)) + return e->id; + + /* MUST have seen name/key pair at least once */ + errno = EINVAL; + return -1; + } + + /* need to add new entry */ + if(t->fill == t->max_fill) { + id_table_remove_stale(t); + if(t->fill == t->max_fill) { + errno = ENOSPC; + return -1; + } + for(pe = t->hash + hash_func(name,t->hash_size); *pe; pe = &(*pe)->next); + } + size = strlen(name); + e = (id_table_entry*)malloc(sizeof(id_table_entry) + size); + if(!e) return -1; + + while(t->array[t->array_ptr]) + t->array_ptr = (t->array_ptr + 1) % t->array_size; + + e->pself = pe; + e->next = 0; + e->id = (int)(t->array_ptr + t->array_start); + memcpy(e->name,name,size + 1); + if(key[0] && key_hash_create_entry(t, e,key) == NULL) + return -1; + + *pe = e; + t->array[t->array_ptr] = e; + t->array_ptr = (t->array_ptr + 1) % t->array_size; + t->fill++; + return e->id; +} + +static id_table_entry *id_table_get_entry(id_table *t,unsigned long id) { + if(id >= t->array_start) { + id -= t->array_start; + if(id < t->array_size) + if(t->array[id] && (t->array[id] != (id_table_entry*)-1)) + return t->array[id]; + } + return 0; +} + +char *id_table_get_name(id_table *t,int id) { + id_table_entry *e = id_table_get_entry(t,(unsigned long)id); + return e ? e->name : 0; +} + +int id_table_remove_stale(id_table *t) { + id_table_entry *e; + unsigned long u; + DIR *d; + struct dirent *de; + struct stat st; + char path[NAME_MAX + 7]; + + /* + * flag all uids inactive + */ + + for(u = 0; u < t->array_size; u++) + if(t->array[u] && (t->array[u] != (id_table_entry*)-1)) + t->array[u]->tmp = 0; + + /* + * go through list of processes, finding active uids + */ + + memcpy(path,"/proc/",6); + if(d = opendir("/proc")) { + while(de = readdir(d)) { + strcpy(path + 6,de->d_name); + if(!stat(path,&st)) { + if(e = id_table_get_entry(t,(unsigned long)st.st_uid)) e->tmp = 1; + } + } + closedir(d); + } + + /* + * remove ones still marked inactive + */ + + for(u = 0; u < t->array_size; u++) { + e = t->array[u]; + if(e && (e != (id_table_entry*)-1)) { + if(!e->tmp) { + if(e->next) e->next->pself = e->pself; + *e->pself = e->next; + t->array[u] = 0; + t->array_ptr = u; + t->fill--; + key_hash_delete_keys(t, e); + free(e); + } + } + } + return 0; +} + +id_table_entry *key_hash_get_entry(id_table *t, unsigned *key) { + key_hash_entry **ke; + + for(ke = t->key_hash + (key[0] % t->key_hash_size); *ke; ke = &(*ke)->next) + if(key[0] && (*ke)->key[0] == key[0] && !memcmp(key,(*ke)->key,WP_KEY_LEN * sizeof(unsigned))) + return (*ke)->e; + + return NULL; +} + +key_hash_entry *key_hash_create_entry(id_table *t, id_table_entry *pe, unsigned *key) { + key_hash_entry **ke, *kep; + + for(ke = &t->key_hash[key[0] % t->key_hash_size]; *ke; ke = &(*ke)->next) + if(!memcmp(key,(*ke)->key,WP_KEY_LEN * sizeof(unsigned))) + return NULL; + + if(kep = malloc(sizeof(key_hash_entry))){ + memcpy(kep->key,key,(WP_KEY_LEN * sizeof(unsigned int))); + kep->e = pe; + kep->pself = ke; + kep->next = NULL; + *ke = kep; + } + + return kep; +} + +void key_hash_delete_keys(id_table *t, id_table_entry *e) { + key_hash_entry *kp, *kd; + int u; + + for(u = 0; u < t->key_hash_size; u++){ + for(kp = t->key_hash[u]; kp; ) + if (kp->e == e){ + kd = kp; + if(kp->next) kp->next->pself = kp->pself; + *kp->pself = kp->next; + kp = kp->next; + free(kd); + } + else + kp = kp->next; + } +} |