Cache EF-PNN, EF-OPL sim files on disk.
authorAndrzej Zaborowski <andrew.zaborowski@intel.com>
Mon, 20 Jul 2009 16:12:02 +0000 (18:12 +0200)
committerDenis Kenzior <denkenz@gmail.com>
Mon, 27 Jul 2009 22:09:48 +0000 (17:09 -0500)
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.

bootstrap-configure
configure.ac
src/sim.c

index cc82e1a..cea17bf 100755 (executable)
@@ -12,4 +12,5 @@ fi
                --prefix=/usr \
                --mandir=/usr/share/man \
                --sysconfdir=/etc \
-               --disable-datafiles
+               --disable-datafiles \
+               --localstatedir=/var
index bb52d9d..f829b47 100644 (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
index 787ebc7..7f905b1 100644 (file)
--- a/src/sim.c
+++ b/src/sim.c
 #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;