Cache EF-PNN, EF-OPL sim files on disk.

This uses plain files in /var/lib/ofono for storing contents of the
operator lists to avoid possibly numerous queries to the SIM on every
startup.  Files are indexed with IMSI.  I'm not 100% sure about the
autoconf magic.

Users need to rerun bootstrap-configure after applying this.

Depends on [PATCH] Return SIM file access conditions from read_file_info.
This commit is contained in:
Andrzej Zaborowski 2009-07-20 18:12:02 +02:00 committed by Denis Kenzior
parent 5c981d421e
commit aa71d09516
3 changed files with 204 additions and 1 deletions

View File

@ -12,4 +12,5 @@ fi
--prefix=/usr \
--mandir=/usr/share/man \
--sysconfdir=/etc \
--disable-datafiles
--disable-datafiles \
--localstatedir=/var

View File

@ -96,6 +96,11 @@ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
eval "eval LOCALSTATE_DIR=$localstatedir"
AC_SUBST(LOCALSTATE_DIR)
AC_DEFINE_UNQUOTED(CONFIG_LOCALSTATEDIR, "$LOCALSTATE_DIR",
[Define to the location where state is stored.])
COMPILER_FLAGS
AC_OUTPUT(Makefile gdbus/Makefile gatchat/Makefile gisi/Makefile

197
src/sim.c
View File

@ -29,6 +29,10 @@
#include <dbus/dbus.h>
#include <glib.h>
#include <gdbus.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include "ofono.h"
@ -48,6 +52,7 @@ static gboolean sim_op_retrieve_next(gpointer user);
struct sim_file_op {
int id;
int cache;
enum ofono_sim_file_structure structure;
int length;
int record_length;
@ -267,6 +272,34 @@ static gboolean sim_retrieve_imsi(void *user_data)
return FALSE;
}
static int create_dirs(const char *filename, const mode_t mode)
{
struct stat st;
char *dir;
const char *prev, *next;
int err;
err = stat(filename, &st);
if (!err && S_ISREG(st.st_mode))
return 0;
dir = g_malloc(strlen(filename) + 1);
strcpy(dir, "/");
for (prev = filename; next = strchr(prev + 1, '/'); prev = next)
if (next > prev + 1) {
strncat(dir, prev + 1, next - prev);
if (mkdir(dir, mode) && errno != EEXIST) {
g_free(dir);
return -1;
}
}
g_free(dir);
return 0;
}
static void sim_op_error(struct ofono_modem *modem)
{
struct sim_manager_data *sim = modem->sim_manager;
@ -280,6 +313,11 @@ static void sim_op_error(struct ofono_modem *modem)
g_timeout_add(0, sim_op_next, modem);
}
#define SIM_CACHE_MODE 0600
#define SIM_CACHE_PATH CONFIG_LOCALSTATEDIR "/lib/ofono/%s/%04x"
#define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 2 + imsilen)
#define SIM_CACHE_HEADER_SIZE 6
static void sim_op_retrieve_cb(const struct ofono_error *error,
const unsigned char *data, int len, void *user)
{
@ -288,6 +326,10 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
int total = op->length / op->record_length;
char *imsi = sim->imsi;
char *path;
int fd, ret;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
if (op->current == 1)
sim_op_error(modem);
@ -298,6 +340,23 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
op->cb(modem, 1, op->structure, op->length, op->current,
data, op->record_length, op->userdata);
if (op->cache && imsi) {
/* Cache the record */
path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
fd = open(path, O_WRONLY);
g_free(path);
if (fd == -1)
goto next;
if (lseek(fd, (op->current - 1) * op->record_length +
SIM_CACHE_HEADER_SIZE, SEEK_SET) !=
(off_t) -1)
write(fd, data, op->record_length);
close(fd);
}
next:
if (op->current == total) {
op = g_queue_pop_head(sim->simop_q);
@ -363,6 +422,11 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
struct sim_manager_data *sim = modem->sim_manager;
struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
char *imsi = sim->imsi;
char *path;
unsigned char fileinfo[6];
int fd = -1;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
sim_op_error(modem);
return;
@ -370,6 +434,12 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
op->structure = structure;
op->length = length;
/* Never cache card holder writable files */
op->cache = (
access[OFONO_SIM_FILE_CONDITION_UPDATE] ==
OFONO_SIM_FILE_ACCESS_ADM ||
access[OFONO_SIM_FILE_CONDITION_UPDATE] ==
OFONO_SIM_FILE_ACCESS_NEVER);
if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
op->record_length = length;
@ -379,6 +449,30 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
op->current = 1;
g_timeout_add(0, sim_op_retrieve_next, modem);
if (op->cache && imsi) {
path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
if (create_dirs(path, SIM_CACHE_MODE | S_IXUSR) == 0)
fd = open(path, O_WRONLY | O_CREAT, SIM_CACHE_MODE);
g_free(path);
if (fd == -1) {
ofono_debug("Error %i creating cache file for "
"fileid %04x, IMSI %s",
errno, op->id, imsi);
return;
}
fileinfo[0] = error->type;
fileinfo[1] = length >> 8;
fileinfo[2] = length & 0xff;
fileinfo[3] = structure;
fileinfo[4] = record_length >> 8;
fileinfo[5] = record_length & 0xff;
write(fd, fileinfo, 6);
close(fd);
}
}
static gboolean sim_op_next(gpointer user_data)
@ -397,6 +491,106 @@ static gboolean sim_op_next(gpointer user_data)
return FALSE;
}
struct sim_cache_callback {
ofono_sim_file_read_cb_t cb;
void *userdata;
struct ofono_modem *modem;
int error;
int fd;
enum ofono_sim_file_structure structure;
unsigned int record_length;
unsigned int total;
};
static gboolean sim_op_cached_callback(gpointer user)
{
struct sim_cache_callback *cbs = user;
guint8 buffer[cbs->record_length];
unsigned int record;
if (cbs->error != OFONO_ERROR_TYPE_NO_ERROR) {
cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
goto cleanup;
}
for (record = 0; record < cbs->total; record++) {
if (read(cbs->fd, buffer, cbs->record_length) <
(int) cbs->record_length) {
cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
break;
}
cbs->cb(cbs->modem, 1, cbs->structure,
cbs->record_length * cbs->total, record + 1,
buffer, cbs->record_length, cbs->userdata);
}
cleanup:
close(cbs->fd);
g_free(cbs);
return FALSE;
}
static gboolean sim_op_check_cached(struct ofono_modem *modem, int fileid,
ofono_sim_file_read_cb_t cb, void *data)
{
struct sim_manager_data *sim = modem->sim_manager;
char *imsi = sim->imsi;
char *path;
int fd;
unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
ssize_t len;
struct ofono_error error;
unsigned int file_length;
enum ofono_sim_file_structure structure;
unsigned int record_length;
struct sim_cache_callback *cbs;
if (!imsi)
return FALSE;
path = g_strdup_printf(SIM_CACHE_PATH, imsi, fileid);
fd = open(path, O_RDONLY);
g_free(path);
if (fd == -1) {
if (errno != ENOENT)
ofono_debug("Error %i opening cache file for "
"fileid %04x, IMSI %s",
errno, fileid, imsi);
return FALSE;
}
len = read(fd, fileinfo, SIM_CACHE_HEADER_SIZE);
if (len != SIM_CACHE_HEADER_SIZE)
return FALSE;
error.type = fileinfo[0];
file_length = (fileinfo[1] << 8) | fileinfo[2];
structure = fileinfo[3];
record_length = (fileinfo[4] << 8) | fileinfo[5];
if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
record_length = file_length;
if (record_length == 0 || file_length < record_length)
return FALSE;
cbs = g_new(struct sim_cache_callback, 1);
cbs->cb = cb;
cbs->userdata = data;
cbs->modem = modem;
cbs->error = error.type;
cbs->fd = fd;
cbs->structure = structure;
cbs->record_length = record_length;
cbs->total = file_length / record_length;
g_timeout_add(0, sim_op_cached_callback, cbs);
return TRUE;
}
int ofono_sim_read(struct ofono_modem *modem, int id,
ofono_sim_file_read_cb_t cb, void *data)
{
@ -409,6 +603,9 @@ int ofono_sim_read(struct ofono_modem *modem, int id,
if (modem->sim_manager == NULL)
return -1;
if (sim_op_check_cached(modem, id, cb, data))
return 0;
if (!sim->ops)
return -1;