diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2014-12-08 14:54:09 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2014-12-08 14:54:09 +0100 |
commit | 3eade89c7573f8893fe045290e374d4502092e13 (patch) | |
tree | defd4218ff82053d44f54595af4e882ea7eb988a /common | |
parent | c2b5a7015b7323af4b7c8cf19a06b954f4f09949 (diff) | |
parent | 7962e7a0b423a5dfba251622f64d3891f69a55c0 (diff) | |
download | barebox-3eade89c7573f8893fe045290e374d4502092e13.tar.gz barebox-3eade89c7573f8893fe045290e374d4502092e13.tar.xz |
Merge branch 'for-next/persistent-vars'
Conflicts:
arch/arm/boards/efika-mx-smartbook/defaultenv-efikasb/config
Diffstat (limited to 'common')
-rw-r--r-- | common/Kconfig | 1 | ||||
-rw-r--r-- | common/environment.c | 246 | ||||
-rw-r--r-- | common/globalvar.c | 178 | ||||
-rw-r--r-- | common/startup.c | 13 |
4 files changed, 360 insertions, 78 deletions
diff --git a/common/Kconfig b/common/Kconfig index 562798063b..7f551948d4 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -632,6 +632,7 @@ config DEFAULT_ENVIRONMENT_GENERIC_NEW select CMD_BASENAME select CMD_READLINK select CMD_DIRNAME + select CMD_NV select FLEXIBLE_BOOTARGS select CMD_BOOT select NET_CMD_IFUP if NET diff --git a/common/environment.c b/common/environment.c index e55c7b7417..2639411e26 100644 --- a/common/environment.c +++ b/common/environment.c @@ -36,19 +36,32 @@ #include <libbb.h> #include <libgen.h> #include <environment.h> +#include <globalvar.h> +#include <libfile.h> #else # define errno_str(x) ("void") #define EXPORT_SYMBOL(x) #endif +struct envfs_entry { + char *name; + void *buf; + int size; + struct envfs_entry *next; + mode_t mode; +}; + struct action_data { - int fd; const char *base; void *writep; + struct envfs_entry *env; }; #define PAD4(x) ((x + 3) & ~3) #ifdef __BAREBOX__ + +#define TMPDIR "/.defaultenv" + static char *default_environment_path = "/dev/env0"; void default_environment_path_set(char *path) @@ -60,6 +73,21 @@ char *default_environment_path_get(void) { return default_environment_path; } + +static int do_compare_file(const char *filename, const char *base) +{ + int ret; + char *cmp; + const char *relname = filename + strlen(base) + 1; + + cmp = asprintf("%s/%s", TMPDIR, relname); + ret = compare_file(cmp, filename); + + free(cmp); + + return ret; +} + #else static inline int protect(int fd, size_t count, unsigned long offset, int prot) { @@ -70,96 +98,140 @@ static inline int erase(int fd, size_t count, unsigned long offset) { return 0; } + +static int do_compare_file(const char *filename, const char *base) +{ + return 1; +} #endif -static int file_size_action(const char *filename, struct stat *statbuf, +static int file_action(const char *filename, struct stat *statbuf, void *userdata, int depth) { struct action_data *data = userdata; + struct envfs_entry *env; + int fd, ret; - data->writep += sizeof(struct envfs_inode); - data->writep += PAD4(strlen(filename) + 1 - strlen(data->base)); - data->writep += sizeof(struct envfs_inode_end); - if (S_ISLNK(statbuf->st_mode)) { - char path[PATH_MAX]; + if (!do_compare_file(filename, data->base)) + return 1; + + env = xzalloc(sizeof(*env)); + env->name = strdup(filename + strlen(data->base)); + env->size = statbuf->st_size; + + env->buf = calloc(env->size + 1, 1); + if (!env->buf) + goto out; - memset(path, 0, PATH_MAX); + env->mode = S_IRWXU | S_IRWXG | S_IRWXO; - if (readlink(filename, path, PATH_MAX - 1) < 0) { + if (S_ISLNK(statbuf->st_mode)) { + env->size++; + + ret = readlink(filename, env->buf, env->size); + if (ret < 0) { perror("read"); - return 0; + goto out; } - data->writep += PAD4(strlen(path) + 1); + + env->mode |= S_IFLNK; } else { - data->writep += PAD4(statbuf->st_size); + fd = open(filename, O_RDONLY); + if (fd < 0) + return fd; + + ret = read(fd, env->buf, env->size); + + close(fd); + + if (ret < env->size) { + perror("read"); + goto out; + } } + env->next = data->env; + data->env = env; +out: return 1; } -static int file_save_action(const char *filename, struct stat *statbuf, - void *userdata, int depth) +static void envfs_save_inode(struct action_data *data, struct envfs_entry *env) { - struct action_data *data = userdata; struct envfs_inode *inode; struct envfs_inode_end *inode_end; - int fd; - int namelen = strlen(filename) + 1 - strlen(data->base); + int namelen = strlen(env->name) + 1; - inode = (struct envfs_inode*)data->writep; + inode = data->writep; inode->magic = ENVFS_32(ENVFS_INODE_MAGIC); - inode->headerlen = ENVFS_32(PAD4(namelen + sizeof(struct envfs_inode_end))); + inode->headerlen = ENVFS_32(PAD4(namelen) + sizeof(struct envfs_inode_end)); + inode->size = ENVFS_32(env->size); + data->writep += sizeof(struct envfs_inode); - strcpy(data->writep, filename + strlen(data->base)); + strcpy(data->writep, env->name); data->writep += PAD4(namelen); - inode_end = (struct envfs_inode_end*)data->writep; - data->writep += sizeof(struct envfs_inode_end); + + inode_end = data->writep; inode_end->magic = ENVFS_32(ENVFS_INODE_END_MAGIC); - inode_end->mode = ENVFS_32(S_IRWXU | S_IRWXG | S_IRWXO); + inode_end->mode = ENVFS_32(env->mode); + data->writep += sizeof(struct envfs_inode_end); - if (S_ISLNK(statbuf->st_mode)) { - char path[PATH_MAX]; - int len; + memcpy(data->writep, env->buf, env->size); + data->writep += PAD4(env->size); +} + +#ifdef __BAREBOX__ +static int file_remove_action(const char *filename, struct stat *statbuf, + void *userdata, int depth) +{ + struct action_data *data = userdata; + char *envname; + struct stat s; + int ret; + struct envfs_entry *env; - memset(path, 0, PATH_MAX); + filename += sizeof(TMPDIR) - 1; - if (readlink(filename, path, PATH_MAX - 1) < 0) { - perror("read"); - goto out; - } - len = strlen(path) + 1; + envname = asprintf("%s/%s", data->base, filename); - inode_end->mode |= ENVFS_32(S_IFLNK); + ret = stat(envname, &s); + if (ret) { + char *base; - memcpy(data->writep, path, len); - inode->size = ENVFS_32(len); - data->writep += PAD4(len); - debug("handling symlink %s size %d namelen %d headerlen %d\n", - filename + strlen(data->base), - len, namelen, ENVFS_32(inode->headerlen)); - } else { - debug("handling file %s size %lld namelen %d headerlen %d\n", - filename + strlen(data->base), - statbuf->st_size, namelen, ENVFS_32(inode->headerlen)); + base = basename(envname); - inode->size = ENVFS_32(statbuf->st_size); - fd = open(filename, O_RDONLY); - if (fd < 0) { - printf("Open %s %s\n", filename, errno_str()); - goto out; - } + env = xzalloc(sizeof(*env)); + env->name = strdup(filename); + env->size = strlen(base) + 1; - if (read(fd, data->writep, statbuf->st_size) < statbuf->st_size) { - perror("read"); + env->buf = strdup(base); + if (!env->buf) goto out; - } - close(fd); - data->writep += PAD4(statbuf->st_size); - } + env->mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFLNK; + env->next = data->env; + data->env = env; + } out: + free(envname); + + return 1; +} +#else +static int file_remove_action(const char *filename, struct stat *statbuf, + void *userdata, int depth) +{ + return 0; +} +#endif + +static int dir_remove_action(const char *filename, struct stat *statbuf, + void *userdata, int depth) +{ + rmdir(filename); + return 1; } @@ -177,26 +249,39 @@ int envfs_save(const char *filename, const char *dirname, unsigned flags) { struct envfs_super *super; int envfd, size, ret; - struct action_data data; + struct action_data data = {}; void *buf = NULL, *wbuf; + struct envfs_entry *env; data.writep = NULL; data.base = dirname; +#ifdef __BAREBOX__ + defaultenv_load(TMPDIR, 0); +#endif + if (flags & ENVFS_FLAGS_FORCE_BUILT_IN) { size = 0; /* force no content */ } else { /* first pass: calculate size */ - recursive_action(dirname, ACTION_RECURSE, file_size_action, + recursive_action(dirname, ACTION_RECURSE, file_action, NULL, &data, 0); - - size = (unsigned long)data.writep; + recursive_action("/.defaultenv", ACTION_RECURSE, + file_remove_action, NULL, &data, 0); + size = 0; + + for (env = data.env; env; env = env->next) { + size += PAD4(env->size); + size += sizeof(struct envfs_inode); + size += PAD4(strlen(env->name) + 1); + size += sizeof(struct envfs_inode_end); + } } buf = xzalloc(size + sizeof(struct envfs_super)); data.writep = buf + sizeof(struct envfs_super); - super = (struct envfs_super *)buf; + super = buf; super->magic = ENVFS_32(ENVFS_MAGIC); super->major = ENVFS_MAJOR; super->minor = ENVFS_MINOR; @@ -205,8 +290,17 @@ int envfs_save(const char *filename, const char *dirname, unsigned flags) if (!(flags & ENVFS_FLAGS_FORCE_BUILT_IN)) { /* second pass: copy files to buffer */ - recursive_action(dirname, ACTION_RECURSE, file_save_action, - NULL, &data, 0); + env = data.env; + while (env) { + struct envfs_entry *next = env->next; + + envfs_save_inode(&data, env); + + free(env->buf); + free(env->name); + free(env); + env = next; + } } super->crc = ENVFS_32(crc32(0, buf + sizeof(struct envfs_super), size)); @@ -264,6 +358,9 @@ out: close(envfd); out1: free(buf); +#ifdef __BAREBOX__ + unlink_recursive(TMPDIR, NULL); +#endif return ret; } EXPORT_SYMBOL(envfs_save); @@ -320,7 +417,7 @@ static int envfs_load_data(struct envfs_super *super, void *buf, size_t size, struct envfs_inode_end *inode_end; uint32_t inode_size, inode_headerlen, namelen; - inode = (struct envfs_inode *)buf; + inode = buf; buf += sizeof(struct envfs_inode); if (ENVFS_32(inode->magic) != ENVFS_INODE_MAGIC) { @@ -334,15 +431,12 @@ static int envfs_load_data(struct envfs_super *super, void *buf, size_t size, if (super->major < 1) inode_end = &inode_end_dummy; else - inode_end = (struct envfs_inode_end *)(buf + PAD4(namelen)); + inode_end = buf + PAD4(namelen); debug("loading %s size %d namelen %d headerlen %d\n", inode->data, inode_size, namelen, inode_headerlen); str = concat_path_file(dir, inode->data); - tmp = strdup(str); - make_directory(dirname(tmp)); - free(tmp); headerlen_full = PAD4(inode_headerlen); buf += headerlen_full; @@ -353,11 +447,19 @@ static int envfs_load_data(struct envfs_super *super, void *buf, size_t size, goto out; } + tmp = strdup(str); + make_directory(dirname(tmp)); + free(tmp); + if (S_ISLNK(ENVFS_32(inode_end->mode))) { debug("symlink: %s -> %s\n", str, (char*)buf); - if (symlink((char*)buf, str) < 0) { - printf("symlink: %s -> %s :", str, (char*)buf); - perror(""); + if (!strcmp(buf, basename(str))) { + unlink(str); + } else { + ret = symlink(buf, str); + if (ret < 0) + printf("symlink: %s -> %s : %s\n", + str, (char*)buf, strerror(-errno)); } free(str); } else { @@ -392,6 +494,9 @@ skip: sizeof(struct envfs_inode); } + recursive_action(dir, ACTION_RECURSE | ACTION_DEPTHFIRST, NULL, + dir_remove_action, NULL, 0); + ret = 0; out: return ret; @@ -498,6 +603,7 @@ int envfs_load(const char *filename, const char *dir, unsigned flags) goto out; ret = 0; + out: close(envfd); free(buf); diff --git a/common/globalvar.c b/common/globalvar.c index c72f147d5f..9a793ac4a9 100644 --- a/common/globalvar.c +++ b/common/globalvar.c @@ -5,6 +5,9 @@ #include <init.h> #include <environment.h> #include <magicvar.h> +#include <fs.h> +#include <fcntl.h> +#include <libfile.h> #include <generated/utsrelease.h> struct device_d global_device = { @@ -12,6 +15,11 @@ struct device_d global_device = { .id = DEVICE_ID_SINGLE, }; +struct device_d nv_device = { + .name = "nv", + .id = DEVICE_ID_SINGLE, +}; + int globalvar_add(const char *name, int (*set)(struct device_d *dev, struct param_d *p, const char *val), const char *(*get)(struct device_d *, struct param_d *p), @@ -25,6 +33,175 @@ int globalvar_add(const char *name, return 0; } +static int nv_save(const char *name, const char *val) +{ + int fd, ret; + char *fname; + + ret = make_directory("/env/nv"); + if (ret) + return ret; + + fname = asprintf("/env/nv/%s", name); + + fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC); + + free(fname); + + if (fd < 0) + return fd; + + fprintf(fd, "%s", val); + + close(fd); + + return 0; +} + +static int nv_set(struct device_d *dev, struct param_d *p, const char *val) +{ + struct param_d *gp; + int ret; + + if (!val) + val = ""; + + gp = get_param_by_name(&global_device, p->name); + if (!gp) + return -EINVAL; + + ret = gp->set(&global_device, gp, val); + if (ret) + return ret; + + free(p->value); + p->value = xstrdup(val); + + return nv_save(p->name, val); +} + +static const char *nv_get(struct device_d *dev, struct param_d *p) +{ + return p->value ? p->value : ""; +} + +int nvvar_add(const char *name, const char *value) +{ + struct param_d *p, *gp; + int ret; + + gp = get_param_by_name(&nv_device, name); + if (gp) { + ret = dev_set_param(&global_device, name, value); + if (ret) + return ret; + + ret = dev_set_param(&nv_device, name, value); + if (ret) + return ret; + + return 0; + } + + ret = globalvar_add_simple(name, value); + if (ret && ret != -EEXIST) + return ret; + + p = dev_add_param(&nv_device, name, nv_set, nv_get, 0); + if (IS_ERR(p)) + return PTR_ERR(p); + + if (value) { + ret = dev_set_param(&global_device, name, value); + if (ret) + return ret; + } else { + value = dev_get_param(&global_device, name); + if (!value) + value = ""; + } + + p->value = xstrdup(value); + + return nv_save(p->name, value); +} + +int nvvar_remove(const char *name) +{ + struct param_d *p; + char *fname; + + p = get_param_by_name(&nv_device, name); + if (!p) + return -ENOENT; + + fname = asprintf("/env/nv/%s", p->name); + unlink(fname); + free(fname); + + list_del(&p->list); + free(p->name); + free(p); + + return 0; +} + +int nvvar_load(void) +{ + char *val; + int ret; + DIR *dir; + struct dirent *d; + + dir = opendir("/env/nv"); + if (!dir) + return -ENOENT; + + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + val = read_file_line("/env/nv/%s", d->d_name); + + pr_debug("%s: Setting \"%s\" to \"%s\"\n", + __func__, d->d_name, val); + + ret = nvvar_add(d->d_name, val); + if (ret) + pr_err("failed to create nv variable %s: %s\n", + d->d_name, strerror(-ret)); + } + + closedir(dir); + + return 0; +} + +static void device_param_print(struct device_d *dev) +{ + struct param_d *param; + + list_for_each_entry(param, &dev->parameters, list) { + const char *p = dev_get_param(dev, param->name); + const char *nv = NULL; + + if (dev != &nv_device) + nv = dev_get_param(&nv_device, param->name); + + printf("%s%s: %s\n", nv ? "* " : " ", param->name, p); + } +} + +void nvvar_print(void) +{ + device_param_print(&nv_device); +} + +void globalvar_print(void) +{ + device_param_print(&global_device); +} + /* * globalvar_get_match * @@ -87,6 +264,7 @@ int globalvar_add_simple(const char *name, const char *value) static int globalvar_init(void) { register_device(&global_device); + register_device(&nv_device); globalvar_add_simple("version", UTS_RELEASE); diff --git a/common/startup.c b/common/startup.c index ceb597b0eb..2b92efcb95 100644 --- a/common/startup.c +++ b/common/startup.c @@ -40,6 +40,7 @@ #include <envfs.h> #include <asm/sections.h> #include <uncompress.h> +#include <globalvar.h> extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[], __barebox_initcalls_end[]; @@ -78,17 +79,13 @@ void __noreturn start_barebox(void) pr_debug("initcalls done\n"); if (IS_ENABLED(CONFIG_ENV_HANDLING)) { - int ret; char *default_environment_path = default_environment_path_get(); - ret = envfs_load(default_environment_path, "/env", 0); - - if (ret && IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT)) { - pr_err("no valid environment found on %s. " - "Using default environment\n", - default_environment_path); + if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT)) defaultenv_load("/env", 0); - } + + envfs_load(default_environment_path, "/env", 0); + nvvar_load(); } if (IS_ENABLED(CONFIG_COMMAND_SUPPORT)) { |