libfprint/libfprint/data.c
Daniel Drake 294f9ad447 Improve print_data structure and on-disk format
Drivers now have an ID number. These will be assigned by me and documented
on the wiki. 0 cannot be used.

Drivers now define a devtype for each device they initialise. This is to
cope with the situation where a driver can support varying devices where
their print data is incompatible (i.e. image scaling is totally changed).
This doesn't apply to any existing supported devices.

Print data no longer includes driver name, and includes driver ID instead.

Paths to saved print data now include driver ID and devtype, and no longer
include driver name.

APIs exposed for converting a print_data into a blob which can then
be loaded back again later. Useful for systems who don't want to use
my simple storage system (which is only aimed at a single user).

File format is now defined and will be documented on the wiki. The header
is larger as we can no longer rely on directory paths in all scenarios.

Print data compat check now checks devtype and driver ID.
2007-10-28 22:13:51 +00:00

309 lines
7.3 KiB
C

/*
* Fingerprint data handling and storage
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <config.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>
#include "fp_internal.h"
#define DIR_PERMS 0700
/* FIXME: should free this during library shutdown */
static char *base_store = NULL;
static void storage_setup(void)
{
const char *homedir;
homedir = g_getenv("HOME");
if (!homedir)
homedir = g_get_home_dir();
if (!homedir)
return;
base_store = g_build_filename(homedir, ".fprint/prints", NULL);
g_mkdir_with_parents(base_store, DIR_PERMS);
/* FIXME handle failure */
}
static const char *finger_code_to_str(enum fp_finger finger)
{
const char *names[] = {
[LEFT_THUMB] = "lthu",
[LEFT_INDEX] = "lind",
[LEFT_MIDDLE] = "lmid",
[LEFT_RING] = "lrin",
[LEFT_LITTLE] = "llit",
[RIGHT_THUMB] = "rthu",
[RIGHT_INDEX] = "rind",
[RIGHT_MIDDLE] = "rmid",
[RIGHT_RING] = "rrin",
[RIGHT_LITTLE] = "rlit",
};
if (finger < LEFT_THUMB || finger > RIGHT_LITTLE)
return NULL;
return names[finger];
}
static enum fp_print_data_type get_data_type_for_dev(struct fp_dev *dev)
{
switch (dev->drv->type) {
case DRIVER_PRIMITIVE:
return PRINT_DATA_RAW;
case DRIVER_IMAGING:
return PRINT_DATA_NBIS_MINUTIAE;
default:
fp_err("unrecognised drv type %d", dev->drv->type);
return PRINT_DATA_RAW;
}
}
static struct fp_print_data *print_data_new(uint16_t driver_id,
uint32_t devtype, enum fp_print_data_type type, size_t length)
{
struct fp_print_data *data = g_malloc(sizeof(*data) + length);
fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype);
memset(data, 0, sizeof(*data));
data->driver_id = driver_id;
data->devtype = devtype;
data->type = type;
data->length = length;
return data;
}
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
{
struct fp_print_data *data = g_malloc(sizeof(*data) + length);
memset(data, 0, sizeof(*data));
return print_data_new(dev->drv->id, dev->devtype,
get_data_type_for_dev(dev), length);
}
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
unsigned char **ret)
{
struct fpi_print_data_fp1 *buf;
size_t buflen;
fp_dbg("");
buflen = sizeof(*buf) + data->length;
buf = malloc(buflen);
if (!buf)
return 0;
*ret = (unsigned char *) buf;
buf->prefix[0] = 'F';
buf->prefix[1] = 'P';
buf->prefix[2] = '1';
buf->driver_id = GUINT16_TO_LE(data->driver_id);
buf->devtype = GUINT32_TO_LE(data->devtype);
buf->data_type = data->type;
memcpy(buf->data, data->data, data->length);
return buflen;
}
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
size_t buflen)
{
struct fpi_print_data_fp1 *raw = (struct fpi_print_data_fp1 *) buf;
size_t print_data_len;
struct fp_print_data *data;
fp_dbg("buffer size %zd", buflen);
if (buflen < sizeof(*raw))
return NULL;
if (strncmp(raw->prefix, "FP1", 3) != 0) {
fp_dbg("bad header prefix");
return NULL;
}
print_data_len = buflen - sizeof(*raw);
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
GUINT32_FROM_LE(raw->devtype), raw->data_type, print_data_len);
memcpy(data->data, raw->data, print_data_len);
return data;
}
static char *__get_path_to_storedir(uint16_t driver_id, uint32_t devtype)
{
char idstr[5];
char devtypestr[9];
g_snprintf(idstr, sizeof(idstr), "%04x", driver_id);
g_snprintf(devtypestr, sizeof(devtypestr), "%08x", devtype);
return g_build_filename(base_store, idstr, devtypestr, NULL);
}
static char *get_path_to_storedir(struct fp_dev *dev)
{
return __get_path_to_storedir(dev->drv->id, dev->devtype);
}
static char *get_path_to_print(struct fp_dev *dev, const char *fingerstr)
{
char *dirpath;
char *path;
dirpath = get_path_to_storedir(dev);
path = g_build_filename(dirpath, fingerstr, NULL);
g_free(dirpath);
return path;
}
API_EXPORTED int fp_print_data_save(struct fp_print_data *data,
enum fp_finger finger)
{
GError *err = NULL;
char *path;
char *dirpath;
const char *fingerstr = finger_code_to_str(finger);
unsigned char *buf;
size_t len;
int r;
if (!fingerstr)
return -EINVAL;
if (!base_store)
storage_setup();
fp_dbg("save %s print from driver %04x", fingerstr, data->driver_id);
len = fp_print_data_get_data(data, &buf);
if (!len)
return -ENOMEM;
dirpath = __get_path_to_storedir(data->driver_id, data->devtype);
r = g_mkdir_with_parents(dirpath, DIR_PERMS);
if (r < 0) {
fp_err("couldn't create storage directory");
g_free(dirpath);
return r;
}
path = g_build_filename(dirpath, fingerstr, NULL);
fp_dbg("saving to %s", path);
g_file_set_contents(path, buf, len, &err);
free(buf);
g_free(dirpath);
g_free(path);
if (err) {
r = err->code;
fp_err("%s save failed: %s", fingerstr, err->message);
g_error_free(err);
/* FIXME interpret error codes */
return r;
}
return 0;
}
gboolean fpi_print_data_compatible(struct fp_print_data *data,
struct fp_dev *dev)
{
struct fp_driver *drv = dev->drv;
if (drv->id != data->driver_id) {
fp_dbg("driver name mismatch: %02x vs %02x", drv->id, data->driver_id);
return FALSE;
}
if (dev->devtype != data->devtype) {
fp_dbg("devtype mismatch: %04x vs %04x", dev->devtype, data->devtype);
return FALSE;
}
switch (data->type) {
case PRINT_DATA_RAW:
if (drv->type != DRIVER_PRIMITIVE) {
fp_dbg("raw data vs primitive driver mismatch");
return FALSE;
}
break;
case PRINT_DATA_NBIS_MINUTIAE:
if (drv->type != DRIVER_IMAGING) {
fp_dbg("minutiae data vs imaging driver mismatch");
return FALSE;
}
break;
default:
fp_err("unrecognised data type %d", data->type);
return FALSE;
}
return TRUE;
}
API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
enum fp_finger finger, struct fp_print_data **data)
{
const char *fingerstr = finger_code_to_str(finger);
gchar *path;
gsize length;
gchar *contents;
GError *err = NULL;
struct fp_print_data *fdata;
if (!fingerstr)
return -EINVAL;
if (!base_store)
storage_setup();
path = get_path_to_print(dev, fingerstr);
fp_dbg("from %s", path);
g_file_get_contents(path, &contents, &length, &err);
g_free(path);
if (err) {
int r = err->code;
fp_err("%s load failed: %s", fingerstr, err->message);
g_error_free(err);
/* FIXME interpret more error codes */
if (r == G_FILE_ERROR_NOENT)
return -ENOENT;
else
return r;
}
fdata = fp_print_data_from_data(contents, length);
g_free(contents);
if (!fpi_print_data_compatible(fdata, dev)) {
fp_err("print data is not compatible!");
return -EINVAL;
}
*data = fdata;
return 0;
}
API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
{
g_free(data);
}