summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-12-08 14:54:09 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2014-12-08 14:54:09 +0100
commit3eade89c7573f8893fe045290e374d4502092e13 (patch)
treedefd4218ff82053d44f54595af4e882ea7eb988a /common
parentc2b5a7015b7323af4b7c8cf19a06b954f4f09949 (diff)
parent7962e7a0b423a5dfba251622f64d3891f69a55c0 (diff)
downloadbarebox-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/Kconfig1
-rw-r--r--common/environment.c246
-rw-r--r--common/globalvar.c178
-rw-r--r--common/startup.c13
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)) {