From a84eaa56d0d2edd0872d1d881b82411dc06c6c92 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Bollo?= Date: Wed, 2 Mar 2016 17:45:05 +0100 Subject: [PATCH] afm-db: commenting, bug fixing, improving MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I208e8eb72c42231e077276fa61dd4ad5e952b2c4 Signed-off-by: José Bollo --- src/afm-db.c | 454 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 280 insertions(+), 174 deletions(-) diff --git a/src/afm-db.c b/src/afm-db.c index b9cf1e0..257e70f 100644 --- a/src/afm-db.c +++ b/src/afm-db.c @@ -31,159 +31,90 @@ #include "wgt-info.h" #include "afm-db.h" -struct afapps { - struct json_object *pubarr; - struct json_object *direct; - struct json_object *byapp; +/* + * The structure afm_apps records the data about applications + * for several accesses. + */ +struct afm_apps { + struct json_object *pubarr; /* array of the public data of applications */ + struct json_object *direct; /* hash of applications by their id */ + struct json_object *byapp; /* hash of versions of applications by their appid */ }; +/* + * Two types of directory are handled: + * - root directories: contains subdirectories appid/versio containing the applications + * - application directories: it contains an application + */ enum dir_type { - type_root, - type_app + type_root, /* type for root directory */ + type_app /* type for application directory */ }; +/* + * Structure for recording a path to application(s) + * in the list of directories. + */ struct afm_db_dir { - struct afm_db_dir *next; - char *path; - enum dir_type type; + struct afm_db_dir *next; /* link to the next item of the list */ + enum dir_type type; /* the type of the path */ + char path[1]; /* the path of the directory */ }; +/* + * The structure afm_db records the applications + * for a set of directories recorded as a linked list + */ struct afm_db { - int refcount; - struct afm_db_dir *dirhead; - struct afm_db_dir *dirtail; - struct afapps applications; + int refcount; /* count of references to the structure */ + struct afm_db_dir *dirhead; /* first directory of the set of directories */ + struct afm_db_dir *dirtail; /* last directory of the set of directories */ + struct afm_apps applications; /* the data about applications */ }; -struct afm_db *afm_db_create() -{ - struct afm_db *afdb = malloc(sizeof * afdb); - if (afdb == NULL) - errno = ENOMEM; - else { - afdb->refcount = 1; - afdb->dirhead = NULL; - afdb->dirtail = NULL; - afdb->applications.pubarr = NULL; - afdb->applications.direct = NULL; - afdb->applications.byapp = NULL; - } - return afdb; -} - -void afm_db_addref(struct afm_db *afdb) -{ - assert(afdb); - afdb->refcount++; -} - -void afm_db_unref(struct afm_db *afdb) -{ - struct afm_db_dir *dir; - assert(afdb); - if (!--afdb->refcount) { - json_object_put(afdb->applications.pubarr); - json_object_put(afdb->applications.direct); - json_object_put(afdb->applications.byapp); - while (afdb->dirhead != NULL) { - dir = afdb->dirhead; - afdb->dirhead = dir->next; - free(dir->path); - free(dir); - } - free(afdb); - } -} - -int add_dir(struct afm_db *afdb, const char *path, enum dir_type type) -{ - struct afm_db_dir *dir; - char *r; - - assert(afdb); - - /* don't depend on the cwd and unique name */ - r = realpath(path, NULL); - if (!r) - return -1; - - /* avoiding duplications */ - dir = afdb->dirhead; - while(dir != NULL && (strcmp(dir->path, path) || dir->type != type)) - dir = dir ->next; - if (dir != NULL) { - free(r); - return 0; - } - - /* allocates the structure */ - dir = malloc(sizeof * dir); - if (dir == NULL) { - free(r); - errno = ENOMEM; - return -1; - } - - /* add */ - dir->next = NULL; - dir->path = r; - dir->type = type; - if (afdb->dirtail == NULL) - afdb->dirhead = dir; - else - afdb->dirtail->next = dir; - afdb->dirtail = dir; - return 0; -} - -int afm_db_add_root(struct afm_db *afdb, const char *path) -{ - return add_dir(afdb, path, type_root); -} +/* + * The structure enumdata records data used when enumerating + * application directories of a root directory. + */ +struct enumdata { + char path[PATH_MAX]; /* "current" computed path */ + int length; /* length of path */ + struct afm_apps apps; /* */ +}; -int afm_db_add_application(struct afm_db *afdb, const char *path) +/* + * Release the data of the afm_apps object 'apps'. + */ +static void apps_put(struct afm_apps *apps) { - return add_dir(afdb, path, type_app); + json_object_put(apps->pubarr); + json_object_put(apps->direct); + json_object_put(apps->byapp); } -static int addapp(struct afapps *apps, const char *path) +/* + * Adds the application widget 'desc' of the directory 'path' to the + * afm_apps object 'apps'. + * Returns 0 in case of success. + * Returns -1 and set errno in case of error + */ +static int addwgt(struct afm_apps *apps, const char *path, const struct wgt_desc *desc) { - struct wgt_info *info; - const struct wgt_desc *desc; const struct wgt_desc_feature *feat; - struct json_object *priv = NULL, *pub, *bya, *plugs, *str; - - /* connect to the widget */ - info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0); - if (info == NULL) { - if (errno == ENOENT) - return 0; /* silently ignore bad directories */ - goto error; - } - desc = wgt_info_desc(info); + struct json_object *priv, *pub, *bya, *plugs, *str; /* create the application structure */ priv = json_object_new_object(); if (!priv) - goto error2; - - pub = json_object_new_object(); - if (!priv) - goto error2; - - if (!j_add(priv, "public", pub)) { - json_object_put(pub); - goto error2; - } + return -1; - plugs = json_object_new_array(); - if (!priv) - goto error2; + pub = j_add_new_object(priv, "public"); + if (!pub) + goto error; - if (!j_add(priv, "plugins", plugs)) { - json_object_put(plugs); - goto error2; - } + plugs = j_add_new_array(priv, "plugins"); + if (!plugs) + goto error; if(!j_add_string(priv, "id", desc->id) || !j_add_string(priv, "path", path) @@ -197,68 +128,83 @@ static int addapp(struct afapps *apps, const char *path) || !j_add_string(pub, "description", desc->description) || !j_add_string(pub, "shortname", desc->name_short) || !j_add_string(pub, "author", desc->author)) - goto error2; + goto error; + /* extract plugins from features */ feat = desc->features; while (feat) { static const char prefix[] = FWK_PREFIX_PLUGIN; if (!memcmp(feat->name, prefix, sizeof prefix - 1)) { str = json_object_new_string (feat->name + sizeof prefix - 1); if (str == NULL) - goto error2; + goto error; if (json_object_array_add(plugs, str)) { json_object_put(str); - goto error2; + goto error; } } feat = feat->next; } /* record the application structure */ + if (!j_add(apps->direct, desc->idaver, priv)) + goto error; + + if (json_object_array_add(apps->pubarr, pub)) + goto error; + json_object_get(pub); + if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) { - bya = json_object_new_object(); + bya = j_add_new_object(apps->byapp, desc->id); if (!bya) - goto error2; - if (!j_add(apps->byapp, desc->id, bya)) { - json_object_put(bya); - goto error2; - } + goto error; } - if (!j_add(apps->direct, desc->idaver, priv)) - goto error2; + if (!j_add(bya, desc->version, priv)) + goto error; json_object_get(priv); - - if (!j_add(bya, desc->version, priv)) { - json_object_put(priv); - goto error2; - } - - if (json_object_array_add(apps->pubarr, pub)) - goto error2; - - wgt_info_unref(info); return 0; -error2: - json_object_put(priv); - wgt_info_unref(info); error: + json_object_put(priv); return -1; } -struct enumdata { - char path[PATH_MAX]; - int length; - struct afapps apps; -}; +/* + * Adds the application widget in the directory 'path' to the + * afm_apps object 'apps'. + * Returns 0 in case of success. + * Returns -1 and set errno in case of error + */ +static int addapp(struct afm_apps *apps, const char *path) +{ + int rc; + struct wgt_info *info; + /* connect to the widget */ + info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0); + if (info == NULL) { + if (errno == ENOENT) + return 0; /* silently ignore bad directories */ + return -1; + } + /* adds the widget */ + rc = addwgt(apps, path, wgt_info_desc(info)); + wgt_info_unref(info); + return rc; +} + +/* + * Enumerate the directories designated by 'data' and call the + * function 'callto' for each of them. + */ static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *)) { DIR *dir; int rc; - char *beg, *end; + char *beg; struct dirent entry, *e; + size_t len; /* opens the directory */ dir = opendir(data->path); @@ -274,8 +220,12 @@ static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *)) while (!rc && e) { if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) { /* prepare callto */ - end = stpcpy(beg, entry.d_name); - data->length = end - data->path; + len = strlen(entry.d_name); + if (beg + len >= data->path + sizeof data->path) { + errno = ENAMETOOLONG; + return -1; + } + data->length = stpcpy(beg, entry.d_name) - data->path; /* call the function */ rc = callto(data); if (rc) @@ -287,24 +237,156 @@ static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *)) return rc; } +/* + * called for each version directory. + */ static int recordapp(struct enumdata *data) { return addapp(&data->apps, data->path); } -/* enumerate the versions */ +/* + * called for each application directory. + * enumerate directories of the existing versions. + */ static int enumvers(struct enumdata *data) { int rc = enumentries(data, recordapp); return !rc || errno != ENOTDIR ? 0 : rc; } -/* regenerate the list of applications */ +/* + * Adds the directory of 'path' and 'type' to the afm_db object 'afdb'. + * Returns 0 in case of success. + * Returns -1 and set errno in case of error + * Possible errno values: ENOMEM, ENAMETOOLONG + */ +static int add_dir(struct afm_db *afdb, const char *path, enum dir_type type) +{ + struct afm_db_dir *dir; + size_t len; + + assert(afdb); + + /* check size */ + len = strlen(path); + if (len >= PATH_MAX) { + errno = ENAMETOOLONG; + return -1; + } + + /* avoiding duplications */ + dir = afdb->dirhead; + while(dir != NULL && (strcmp(dir->path, path) || dir->type != type)) + dir = dir ->next; + if (dir != NULL) + return 0; + + /* allocates the structure */ + dir = malloc(strlen(path) + sizeof * dir); + if (dir == NULL) { + errno = ENOMEM; + return -1; + } + + /* add it at tail */ + dir->next = NULL; + dir->type = type; + strcpy(dir->path, path); + if (afdb->dirtail == NULL) + afdb->dirhead = dir; + else + afdb->dirtail->next = dir; + afdb->dirtail = dir; + return 0; +} + +/* + * Creates an afm_db object and returns it with one reference added. + * Return NULL with errno = ENOMEM if memory exhausted. + */ +struct afm_db *afm_db_create() +{ + struct afm_db *afdb = malloc(sizeof * afdb); + if (afdb == NULL) + errno = ENOMEM; + else { + afdb->refcount = 1; + afdb->dirhead = NULL; + afdb->dirtail = NULL; + afdb->applications.pubarr = NULL; + afdb->applications.direct = NULL; + afdb->applications.byapp = NULL; + } + return afdb; +} + +/* + * Adds a reference to an existing afm_db. + */ +void afm_db_addref(struct afm_db *afdb) +{ + assert(afdb); + afdb->refcount++; +} + +/* + * Removes a reference to an existing afm_db object. + * Removes the objet if there no more reference to it. + */ +void afm_db_unref(struct afm_db *afdb) +{ + struct afm_db_dir *dir; + + assert(afdb); + if (!--afdb->refcount) { + /* no more reference, clean the memory used by the object */ + apps_put(&afdb->applications); + while (afdb->dirhead != NULL) { + dir = afdb->dirhead; + afdb->dirhead = dir->next; + free(dir); + } + free(afdb); + } +} + +/* + * Adds the root directory of 'path' to the afm_db object 'afdb'. + * Be aware that no check is done on the directory of 'path' that will + * only be used within calls to the function 'afm_db_update_applications'. + * Returns 0 in case of success. + * Returns -1 and set errno in case of error + * Possible errno values: ENOMEM, ENAMETOOLONG + */ +int afm_db_add_root(struct afm_db *afdb, const char *path) +{ + return add_dir(afdb, path, type_root); +} + +/* + * Adds the application directory of 'path' to the afm_db object 'afdb'. + * Be aware that no check is done on the directory of 'path' that will + * only be used within calls to the function 'afm_db_update_applications'. + * Returns 0 in case of success. + * Returns -1 and set errno in case of error + * Possible errno values: ENOMEM, ENAMETOOLONG + */ +int afm_db_add_application(struct afm_db *afdb, const char *path) +{ + return add_dir(afdb, path, type_app); +} + +/* + * Regenerate the list of applications of the afm_bd object 'afdb'. + * Returns 0 in case of success. + * Returns -1 and set errno in case of error + */ int afm_db_update_applications(struct afm_db *afdb) { int rc; struct enumdata edata; - struct afapps oldapps; + struct afm_apps oldapps; struct afm_db_dir *dir; /* create the result */ @@ -315,7 +397,7 @@ int afm_db_update_applications(struct afm_db *afdb) errno = ENOMEM; goto error; } - /* for each root */ + /* for each directory of afdb */ for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) { if (dir->type == type_root) { edata.length = stpcpy(edata.path, dir->path) - edata.path; @@ -331,28 +413,39 @@ int afm_db_update_applications(struct afm_db *afdb) /* commit the result */ oldapps = afdb->applications; afdb->applications = edata.apps; - json_object_put(oldapps.pubarr); - json_object_put(oldapps.direct); - json_object_put(oldapps.byapp); + apps_put(&oldapps); return 0; error: - json_object_put(edata.apps.pubarr); - json_object_put(edata.apps.direct); - json_object_put(edata.apps.byapp); + apps_put(&edata.apps); return -1; } +/* + * Ensure that applications of the afm_bd object 'afdb' are listed. + * Returns 0 in case of success. + * Returns -1 and set errno in case of error + */ int afm_db_ensure_applications(struct afm_db *afdb) { return afdb->applications.pubarr ? 0 : afm_db_update_applications(afdb); } +/* + * Get the list of the applications public data of the afm_db object 'afdb'. + * The list is returned as a JSON-array that must be released using 'json_object_put'. + * Returns NULL in case of error. + */ struct json_object *afm_db_application_list(struct afm_db *afdb) { return afm_db_ensure_applications(afdb) ? NULL : json_object_get(afdb->applications.pubarr); } +/* + * Get the private data of the applications of 'id' in the afm_db object 'afdb'. + * It returns a JSON-object that must be released using 'json_object_put'. + * Returns NULL in case of error. + */ struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id) { struct json_object *result; @@ -361,10 +454,23 @@ struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id) return NULL; } +/* + * Get the public data of the applications of 'id' in the afm_db object 'afdb'. + * It returns a JSON-object that must be released using 'json_object_put'. + * Returns NULL in case of error. + */ struct json_object *afm_db_get_application_public(struct afm_db *afdb, const char *id) { - struct json_object *result = afm_db_get_application(afdb, id); - return result && json_object_object_get_ex(result, "public", &result) ? json_object_get(result) : NULL; + struct json_object *result; + struct json_object *priv = afm_db_get_application(afdb, id); + if (priv == NULL) + return NULL; + if (json_object_object_get_ex(priv, "public", &result)) + json_object_get(result); + else + result = NULL; + json_object_put(priv); + return result; } -- 2.16.6