/*- * Copyright (c) 2006 Fredrik Lindberg. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: filedb.c 57 2006-02-23 14:15:13Z fli $ */ /* * This implements a BIR database as a filebacked b-tree. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void * fdb_open(const char *, int, char *[]); static void fdb_close(void *); static struct birdb_rec ** fdb_get(void *, struct birdb_rec *); static void fdb_freegetres(void *, struct birdb_rec **); static int fdb_ins(void *, struct birdb_rec *); static int fdb_del(void *, struct birdb_rec *); static struct birdb_rec * fdb_seq_getfirst(void *); static struct birdb_rec * fdb_seq_getnext(void *, struct birdb_rec *); static void fdb_seq_free(void *handle, struct birdb_rec *); static BioAPI_BIR * buf2bir(char *, size_t); /* Backend module glue */ BIRDB_BACKEND_PARAMS = { "filedb", "Filebacked database (b-tree)", fdb_open, fdb_close, fdb_get, fdb_freegetres, fdb_ins, fdb_del, fdb_seq_getfirst, fdb_seq_getnext, fdb_seq_free, }; #define FDB_DEFPATH "/var/db/bioapi/bir" /* * filedb handle */ struct fdb_handle { DB *db; char *bspid; char path[FILENAME_MAX]; }; /* * Open and initialization routine * Arguments * bspid - BSP uuid * argc - Number of arguments * argv - Argument array * * Returns a filedb handle on success, otherwise NULL. */ static void * fdb_open(const char *bspid, int argc, char *argv[]) { BTREEINFO btreeinfo; struct fdb_handle *fdbh; struct stat sb; char *path; int fd; if (bspid == NULL) { warnx("Missing BSP ID"); return (NULL); } fdbh = malloc(sizeof(struct fdb_handle)); if (fdbh == NULL) return (NULL); fdbh->bspid = malloc(strlen(bspid) + 1); if (fdbh->bspid == NULL) goto error1; strncpy(fdbh->bspid, bspid, strlen(bspid) + 1); path = (argc < 1) ? FDB_DEFPATH : argv[0]; snprintf(fdbh->path, FILENAME_MAX - 1, "%s/%s.db", path, fdbh->bspid); if (stat(path, &sb) != 0) { if (errno == ENOENT) { if (mkdir(path, S_IRWXU) != 0) { warn("Unable to create %s", path); goto error2; } } else { warn("Unable to access %s", path); goto error2; } } /* * Test this first, dbopen seems to return a valid pointer * even on permission denied errors. */ fd = open(fdbh->path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { warn("Unable to access %s", fdbh->path); goto error2; } close(fd); memset(&btreeinfo, 0, sizeof(BTREEINFO)); btreeinfo.flags = R_DUP; fdbh->db = dbopen(fdbh->path, O_RDWR | O_CREAT | O_EXLOCK, S_IRUSR | S_IWUSR, DB_BTREE, &btreeinfo); return (fdbh); error2: free(fdbh->bspid); error1: free(fdbh); return (NULL); } /* * Close a filedb handle */ static void fdb_close(void *handle) { struct fdb_handle *fdbh = handle; DB *db = fdbh->db; db->close(db); free(fdbh->bspid); free(fdbh); } /* * Get a record from the database * Arguments * handle - filedb handle * br - BIRdb record (only br_key) * * Returns an array of birdb records that matches the given key. * This array must be freed with freegetres() */ static struct birdb_rec ** fdb_get(void *handle, struct birdb_rec *br) { struct fdb_handle *fdbh = handle; DB *db = fdbh->db; DBT dbk, dbd; struct birdb_rec **recarray = NULL; struct birdb_rec *rec; int sz, idx, i, keylen; keylen = strlen(br->br_key); dbk.data = br->br_key; dbk.size = keylen; sz = idx = 0; db->get(db, &dbk, &dbd, 0); for (i = db->seq(db, &dbk, &dbd, R_CURSOR); !i; i = db->seq(db, &dbk, &dbd, R_NEXT)) { if (memcmp(dbk.data, br->br_key, dbk.size) != 0) break; sz += sizeof(struct birdb_rec *); recarray = realloc(recarray, sz); if (recarray == NULL) goto fail; rec = recarray[idx] = malloc(sizeof(struct birdb_rec)); if (rec == NULL) goto fail; rec->br_key = strdup(br->br_key); if (rec->br_key == NULL) goto fail; memcpy(&rec->br_ctime, dbd.data, sizeof(time_t)); rec->br_bir = buf2bir(dbd.data + sizeof(time_t), dbd.size - sizeof(time_t)); idx++; } /* * Add a final NULL entry in the array to indicate the end */ sz += sizeof(struct birdb_rec *); recarray = realloc(recarray, sz); if (recarray == NULL) goto fail; recarray[idx] = NULL; return (recarray); fail: fdb_freegetres(handle, recarray); return (NULL); } /* * Free results returned by fdb_get() * Arguments * handle - filedb handle * recarray - result array returned by fdb_get */ static void fdb_freegetres(void *handle, struct birdb_rec **recarray) { int idx = 0; if (recarray == NULL) return; while (recarray[idx] != NULL) { free(recarray[idx++]); } free(recarray); } /* * Insert a record into the database * Arguments * handle - filedb handle * br - the birdb record to be inserted * * Returns 0 on success, non-zero on failure. */ static int fdb_ins(void *handle, struct birdb_rec *br) { struct fdb_handle *fdbh = handle; DB *db = fdbh->db; DBT dbk, dbd; BioAPI_BIR *bir = br->br_bir; int nbytes, offset, error; char *buf; nbytes = sizeof(time_t); nbytes += bir->Header.Length; nbytes += (bir->Signature != NULL) ? bir->Signature->Length + 4 : 0; buf = malloc(nbytes); if (buf == NULL) return (-1); DPRINTF(("Enrolled header\n")); HEXDUMP(&bir->Header, sizeof(BioAPI_BIR_HEADER)); DPRINTF(("Enrolled BiometricData\n")); HEXDUMP(bir->BiometricData, (bir->Header.Length) - sizeof(BioAPI_BIR_HEADER)); offset = 0; /* Store timestamp */ memcpy(buf, &br->br_ctime, sizeof(time_t)); offset += sizeof(time_t); /* Write header */ memcpy(buf + offset, &bir->Header, sizeof(BioAPI_BIR_HEADER)); offset += sizeof(BioAPI_BIR_HEADER); /* Write biometric data */ memcpy(buf + offset, bir->BiometricData, (bir->Header.Length) - sizeof(BioAPI_BIR_HEADER)); offset += (bir->Header.Length) - sizeof(BioAPI_BIR_HEADER); /* Store signature, if present */ if (bir->Signature) { memcpy(buf + offset, &bir->Signature->Length, 4); offset += 4; memcpy(buf + offset, bir->Signature->Data, bir->Signature->Length); } dbk.data = br->br_key; dbk.size = strlen(br->br_key); dbd.data = buf; dbd.size = nbytes; error = db->put(db, &dbk, &dbd, 0); if (error != 0) warn("Failed to write record to database"); db->sync(db, 0); free(buf); return (0); } /* * Remove a record from the database * Arguments * handle - filedb handle * br - the birdb record to be removed * * Returns 0 on success, non-zero on failure. */ static int fdb_del(void *handle, struct birdb_rec *br) { struct fdb_handle *fdbh = handle; DB *db = fdbh->db; DBT dbk, dbd; int i, keylen; time_t ctime; keylen = strlen(br->br_key); dbk.data = br->br_key; dbk.size = keylen; db->get(db, &dbk, &dbd, 0); for (i = db->seq(db, &dbk, &dbd, R_CURSOR); !i; i = db->seq(db, &dbk, &dbd, R_NEXT)) { if (memcmp(dbk.data, br->br_key, keylen) != 0) break; memcpy(&ctime, dbd.data, sizeof(time_t)); if (ctime == br->br_ctime) { return (db->del(db, &dbk, R_CURSOR)); } } return (-1); } /* * Get the first record of the database for sequential traversal * Arguments * handle - filedb handle * br - the birdb record to be removed * * Returns the first record if any, NULL otherwise */ static struct birdb_rec * fdb_seq_getfirst(void *handle) { struct fdb_handle *fdbh = handle; struct birdb_rec *rec; DB *db = fdbh->db; DBT dbk, dbd; int error; error = db->seq(db, &dbk, &dbd, R_FIRST); if (error != 0) return (NULL); rec = malloc(sizeof(struct birdb_rec)); if (rec == NULL) return (NULL); rec->br_key = malloc(dbk.size + 1); if (rec->br_key == NULL) return (NULL); strncpy(rec->br_key, dbk.data, dbk.size); rec->br_key[dbk.size] = '\0'; memcpy(&rec->br_ctime, dbd.data, sizeof(time_t)); rec->br_bir = buf2bir(dbd.data + sizeof(time_t), dbd.size - sizeof(time_t)); return (rec); } /* * Get the next logical record for use with sequential traversial * Arguments * handle - filedb handle * prev - previous record from getnext or getfirst * * Returns the next record if any, NULL otherwise * Resources allocated for the previous record will be freed. */ static struct birdb_rec * fdb_seq_getnext(void *handle, struct birdb_rec *prev) { struct fdb_handle *fdbh = handle; struct birdb_rec *rec; DB *db = fdbh->db; DBT dbk, dbd; int error, keylen; keylen = strlen(prev->br_key); dbk.data = prev->br_key; dbk.size = keylen; error = db->seq(db, &dbk, &dbd, R_NEXT); if (error != 0) return (NULL); rec = malloc(sizeof(struct birdb_rec)); if (rec == NULL) goto fail; rec->br_key = malloc(dbk.size + 1); if (rec->br_key == NULL) goto fail; strncpy(rec->br_key, dbk.data, dbk.size); rec->br_key[dbk.size] = '\0'; memcpy(&rec->br_ctime, dbd.data, sizeof(time_t)); rec->br_bir = buf2bir(dbd.data + sizeof(time_t), dbd.size - sizeof(time_t)); fdb_seq_free(handle, prev); return (rec); fail: if (rec != NULL); free(rec); fdb_seq_free(handle, prev); return (NULL); } /* * Free the remains of an sequential traversial * Arguments * handle - filedb handle * last - previous record from getnext or getfirst * * This is not needed if getnext returns NULL */ static void fdb_seq_free(void *handle, struct birdb_rec *last) { birdb_freerec(last); } /* * Creates a BioAPI bir from a byte buffer */ static BioAPI_BIR * buf2bir(char *buf, size_t len) { BioAPI_BIR *bir; int offset, nbytes; bir = malloc(sizeof(BioAPI_BIR)); if (bir == NULL) return (NULL); offset = 0; memcpy(&bir->Header, buf, sizeof(BioAPI_BIR_HEADER)); offset += sizeof(BioAPI_BIR_HEADER); DPRINTF(("Header\n")); HEXDUMP(&bir->Header, sizeof(BioAPI_BIR_HEADER)); nbytes = (bir->Header.Length) - sizeof(BioAPI_BIR_HEADER); bir->BiometricData = malloc(nbytes); if (bir->BiometricData == NULL) { free(bir); return (NULL); } memcpy(bir->BiometricData, buf + offset, nbytes); offset += nbytes; DPRINTF(("BiometricData\n")); HEXDUMP(bir->BiometricData, (bir->Header.Length) - sizeof(BioAPI_BIR_HEADER)); if (len > offset) { bir->Signature = malloc(sizeof (BioAPI_DATA)); if (bir->Signature == NULL) { free(bir->BiometricData); free(bir); return (NULL); } memcpy(&bir->Signature->Length, buf + offset, 4); offset += 4; bir->Signature->Data = malloc(bir->Signature->Length); if (bir->Signature->Data == NULL) { free(bir->Signature); free(bir->BiometricData); free(bir); return (NULL); } memcpy(bir->Signature->Data, buf + offset, bir->Signature->Length); } else { bir->Signature = NULL; } return (bir); }