diff options
Diffstat (limited to 'wgtpkg-files.c')
-rw-r--r-- | wgtpkg-files.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/wgtpkg-files.c b/wgtpkg-files.c new file mode 100644 index 0000000..17e909a --- /dev/null +++ b/wgtpkg-files.c @@ -0,0 +1,312 @@ +/* + Copyright 2015 IoT.bzh + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#define _GNU_SOURCE +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <syslog.h> +#include <dirent.h> +#include <stdio.h> + +#include "wgtpkg.h" + +struct fdb { + unsigned int count; + struct filedesc **files; +}; + +static struct fdb allfiles = { .count = 0, .files = NULL }; +static struct fdb allsignatures = { .count = 0, .files = NULL }; + +static const char author_file[] = "author-signature.xml"; +static const char distributor_file_prefix[] = "signature"; +static const char distributor_file_suffix[] = ".xml"; + +static unsigned int what_signature(const char *name) +{ + unsigned int len, id, nid; + + if (!strcmp(name, author_file)) + return UINT_MAX; + + len = sizeof(distributor_file_prefix)-1; + if (memcmp(name, distributor_file_prefix, len)) + return 0; + if (name[len] <= '0' || name[len] > '9') + return 0; + id = (unsigned int)(name[len++] - '0'); + while ('0' <= name[len] && name[len] <= '9') { + nid = 10 * id + (unsigned int)(name[len++] - '0'); + if (nid < id || nid == UINT_MAX) { + syslog(LOG_WARNING, "number too big for %s", name); + return 0; + } + id = nid; + } + if (strcmp(name+len, distributor_file_suffix)) + return 0; + + return id; +} + +static struct filedesc *get_filedesc(const char *name, int create) +{ + int cmp; + unsigned int low, up, mid, sig; + struct filedesc *result, **grow; + + /* search */ + low = 0; + up = allfiles.count; + while(low < up) { + mid = (low + up) >> 1; + result = allfiles.files[mid]; + cmp = strcmp(result->name, name); + if (!cmp) + return result; /* found */ + if (cmp > 0) + up = mid; + else + low = mid + 1; + } + + /* not found, can create ? */ + if (!create) + return NULL; + + sig = what_signature(name); + + /* allocations */ + grow = realloc(allfiles.files, (allfiles.count + 1) * sizeof(struct filedesc *)); + if (grow == NULL) { + syslog(LOG_ERR, "realloc failed in get_filedesc"); + return NULL; + } + allfiles.files = grow; + + if (sig) { + grow = realloc(allsignatures.files, (allsignatures.count + 1) * sizeof(struct filedesc *)); + if (grow == NULL) { + syslog(LOG_ERR, "second realloc failed in get_filedesc"); + return NULL; + } + allsignatures.files = grow; + } + + result = malloc(sizeof(struct filedesc) + strlen(name)); + if (!result) { + syslog(LOG_ERR, "calloc failed in get_filedesc"); + return NULL; + } + + /* initialisation */ + result->type = type_unset; + result->flags = sig == 0 ? 0 : sig == UINT_MAX ? flag_author_signature : flag_distributor_signature; + result->zindex = 0; + result->signum = sig; + strcpy(result->name, name); + + /* insertion */ + if (low < allfiles.count) + memmove(allfiles.files+low+1, allfiles.files+low, (allfiles.count - low) * sizeof(struct filedesc *)); + allfiles.files[low] = result; + allfiles.count++; + if (sig) { + for (low = 0 ; low < allsignatures.count && sig > allsignatures.files[low]->signum ; low++); + if (low < allsignatures.count) + memmove(allsignatures.files+low+1, allsignatures.files+low, (allsignatures.count - low) * sizeof(struct filedesc *)); + allsignatures.files[low] = result; + allsignatures.count++; + } + + return result; +} + + +static struct filedesc *file_add(const char *name, enum entrytype type) +{ + struct filedesc *desc; + + desc = get_filedesc(name, 1); + if (!desc) + errno = ENOMEM; + else if (desc->type == type_unset) + desc->type = type; + else { + syslog(LOG_ERR, "redeclaration of %s in file_add", name); + errno = EEXIST; + desc = NULL; + } + return desc; +} + +void file_reset() +{ + unsigned int i; + + allsignatures.count = 0; + for (i = 0 ; i < allfiles.count ; i++) + free(allfiles.files[i]); + allfiles.count = 0; +} + +unsigned int file_count() +{ + return allfiles.count; +} + +struct filedesc *file_of_index(unsigned int index) +{ + assert(index < allfiles.count); + return allfiles.files[index]; +} + +struct filedesc *file_of_name(const char *name) +{ + return get_filedesc(name, 0); +} + +struct filedesc *file_add_directory(const char *name) +{ + return file_add(name, type_directory); +} + +struct filedesc *file_add_file(const char *name) +{ + return file_add(name, type_file); +} + +unsigned int signature_count() +{ + return allsignatures.count; +} + +struct filedesc *signature_of_index(unsigned int index) +{ + assert(index < allsignatures.count); + return allsignatures.files[index]; +} + +struct filedesc *get_signature(unsigned int number) +{ + unsigned int idx; + + if (number == 0) + number = UINT_MAX; + for (idx = 0 ; idx < allsignatures.count ; idx++) + if (allsignatures.files[idx]->signum == number) + return allsignatures.files[idx]; + return NULL; +} + +struct filedesc *create_signature(unsigned int number) +{ + struct filedesc *result; + char *name; + int len; + + result = NULL; + if (number == 0 || number == UINT_MAX) + len = asprintf(&name, "%s", author_file); + else + len = asprintf(&name, "%s%u%s", distributor_file_prefix, number, distributor_file_suffix); + + if (len < 0) + syslog(LOG_ERR, "asprintf failed in create_signature"); + else { + assert(len > 0); + result = file_of_name(name); + if (result == NULL) + result = file_add_file(name); + free(name); + } + + return result; +} + +void file_clear_flags() +{ + unsigned int i; + for (i = 0 ; i < allfiles.count ; i++) + allfiles.files[i]->flags &= flag_signature; +} + +static int fill_files_rec(char name[PATH_MAX], int offset) +{ + int len, err; + DIR *dir; + struct dirent *ent; + + if (offset == 0) + dir = opendir("."); + else { + dir = opendir(name); + name[offset++] = '/'; + } + if (!dir) { + syslog(LOG_ERR, "opendir %.*s failed in zwr", offset, name); + return -1; + } + + ent = readdir(dir); + while (ent != NULL) { + len = strlen(ent->d_name); + if (ent->d_name[0] == '.' && (len == 1 || + (ent->d_name[1] == '.' && len == 2))) + ; + else if (offset + len >= PATH_MAX) { + closedir(dir); + syslog(LOG_ERR, "name too long in fill_files_rec"); + errno = ENAMETOOLONG; + return -1; + } else { + memcpy(name + offset, ent->d_name, 1+len); + switch (ent->d_type) { + case DT_DIR: + if (file_add_directory(name) == NULL) { + closedir(dir); + return -1; + } + err = fill_files_rec(name, offset + len); + if (err) { + closedir(dir); + return err; + } + break; + case DT_REG: + if (file_add_file(name) == NULL) { + closedir(dir); + return -1; + } + break; + default: + break; + } + } + ent = readdir(dir); + } + + closedir(dir); + return 0; +} + +int fill_files() +{ + char name[PATH_MAX]; + return fill_files_rec(name, 0); +} + |