Index: stdlib/getenv.c =================================================================== RCS file: /cvsroot/src/lib/libc/stdlib/getenv.c,v retrieving revision 1.30 diff -u -r1.30 getenv.c --- stdlib/getenv.c 4 Nov 2010 21:49:45 -0000 1.30 +++ stdlib/getenv.c 5 Nov 2010 07:09:18 -0000 @@ -39,8 +39,10 @@ #endif /* LIBC_SCCS and not lint */ #include "namespace.h" +#include #include #include +#include #include #include #include "reentrant.h" @@ -49,12 +51,82 @@ #ifdef _REENTRANT rwlock_t __environ_lock = RWLOCK_INITIALIZER; #endif -char **__environ_malloced; +static rb_tree_t *__environ_kvtree; static char **saveenv; -static size_t environ_malloced_len; +static size_t environ_len; __weak_alias(getenv_r, _getenv_r) +typedef struct malloced_keyvalue { + rb_node_t mk_node; + ssize_t mk_size; + char mk_kv[1]; +} malloced_keyvalue_t; + +static signed int +__environ_compkey(void *ctx, const void *obj, const void *key) +{ + const malloced_keyvalue_t *mk = obj; + + (void)&ctx; + if ((const void *)mk->mk_kv < key) + return -1; + return (const void *)mk->mk_kv > key; +} + +static signed int +__environ_compnode(void *ctx, const void *oa, const void *ob) +{ + const malloced_keyvalue_t *mk = ob; + + return __environ_compkey(ctx, oa, mk->mk_kv); +} + +const rb_tree_ops_t __environ_rbops = { + .rbto_compare_nodes = __environ_compnode, + .rbto_compare_key = __environ_compkey, + .rbto_node_offset = offsetof(malloced_keyvalue_t, mk_node), + .rbto_context = NULL +}; + +char * +__environ_kvalloc(size_t size) +{ + malloced_keyvalue_t *mk = malloc(sizeof(*mk) + size - 1); + + if (mk == NULL) + return NULL; + mk->mk_size = size; + if (rb_tree_insert_node(__environ_kvtree, mk) != mk) { + free(mk); + return NULL; + } + return mk->mk_kv; +} + +void +__environ_kvfree(char *p) +{ + malloced_keyvalue_t *mk; + + if (__environ_kvtree == NULL) + return; + + mk = rb_tree_find_node(__environ_kvtree, p); + if (mk == NULL) + return; + rb_tree_remove_node(__environ_kvtree, mk); + free(mk); +} + +size_t +__environ_kvsize(char *p) +{ + malloced_keyvalue_t *mk = rb_tree_find_node(__environ_kvtree, p); + + return mk == NULL ? 0 : mk->mk_size; +} + /* * getenv -- * Returns ptr to value associated with name, if any, else NULL. @@ -107,7 +179,7 @@ char **p; size_t required_len, new_len; - if (offset == -1 || saveenv != environ) { + if (saveenv != environ) { char **ptr; for (ptr = environ, offset = 0; *ptr != NULL; ptr++) offset++; @@ -116,20 +188,28 @@ /* one for potentially new entry one for NULL */ required_len = offset + 2; - if (required_len <= environ_malloced_len && saveenv == environ) + if (required_len <= environ_len && saveenv == environ) return 0; + if (__environ_kvtree == NULL) { + __environ_kvtree = malloc(sizeof(*__environ_kvtree)); + if (__environ_kvtree == NULL) + return -1; + + rb_tree_init(__environ_kvtree, &__environ_rbops); + } + /* Double the size of the arrays until we meet the requirement. */ - new_len = environ_malloced_len ? environ_malloced_len : 16; + new_len = environ_len ? environ_len : 16; while (new_len < required_len) new_len <<= 1; if (saveenv == environ) { /* just increase size */ if ((p = realloc(saveenv, new_len * sizeof(*p))) == NULL) return -1; - (void)memset(&p[environ_malloced_len], 0, - (new_len - environ_malloced_len) * sizeof(*p)); - saveenv = p; + (void)memset(&p[environ_len], 0, + (new_len - environ_len) * sizeof(*p)); + saveenv = p; } else { /* get new space */ free(saveenv); if ((saveenv = malloc(new_len * sizeof(*saveenv))) == NULL) @@ -139,16 +219,9 @@ (void)memset(&saveenv[required_len - 2], 0, (new_len - (required_len - 2)) * sizeof(*saveenv)); } - environ = saveenv; - - p = realloc(__environ_malloced, new_len * sizeof(*p)); - if (p == NULL) - return -1; - (void)memset(&p[environ_malloced_len], 0, - (new_len - environ_malloced_len) * sizeof(*p)); - environ_malloced_len = new_len; - __environ_malloced = p; + environ = saveenv; + environ_len = new_len; return 0; } @@ -162,16 +235,32 @@ void __scrubenv(int offset) { + malloced_keyvalue_t *mk, *mk_next; + int off; + if (environ[++offset] == NULL) return; - for (; environ[offset]; offset++) - if (environ[offset] == __environ_malloced[offset]) { - free(__environ_malloced[offset]); - environ[offset] = __environ_malloced[offset] = NULL; - } + for (off = 0; off < offset; off++) { + mk = rb_tree_find_node(__environ_kvtree, environ[off]); + if (mk == NULL) + continue; + mk->mk_size = -mk->mk_size; /* mark */ + } + (void)memset(&environ[offset], 0, + (environ_len - offset) * sizeof(*environ)); - environ[offset] = __environ_malloced[offset] = NULL; + for (mk = rb_tree_iterate(__environ_kvtree, NULL, RB_DIR_LEFT); + mk != NULL; mk = mk_next) { + mk_next = + rb_tree_iterate(__environ_kvtree, mk, RB_DIR_RIGHT); + if (mk->mk_size < 0) { + mk->mk_size = -mk->mk_size; /* unmark */ + continue; + } + rb_tree_remove_node(__environ_kvtree, mk); + free(mk); + } } /* Index: stdlib/local.h =================================================================== RCS file: /cvsroot/src/lib/libc/stdlib/local.h,v retrieving revision 1.5 diff -u -r1.5 local.h --- stdlib/local.h 3 Nov 2010 15:01:07 -0000 1.5 +++ stdlib/local.h 5 Nov 2010 07:09:18 -0000 @@ -27,10 +27,12 @@ char *__findenv(const char *, int *); int __allocenv(int); void __scrubenv(int); +char *__environ_kvalloc(size_t); +void __environ_kvfree(char *); +size_t __environ_kvsize(char *); #ifdef _REENTRANT extern rwlock_t __environ_lock; #endif extern char **environ; -extern char **__environ_malloced; Index: stdlib/putenv.c =================================================================== RCS file: /cvsroot/src/lib/libc/stdlib/putenv.c,v retrieving revision 1.18 diff -u -r1.18 putenv.c --- stdlib/putenv.c 3 Nov 2010 15:01:07 -0000 1.18 +++ stdlib/putenv.c 5 Nov 2010 07:09:18 -0000 @@ -72,10 +72,8 @@ if (__allocenv(offset) == -1) goto bad; - if (p != NULL && environ[offset] == __environ_malloced[offset]) { - free(__environ_malloced[offset]); - __environ_malloced[offset] = NULL; - } + if (p != NULL) + __environ_kvfree(environ[offset]); environ[offset] = str; if (p == NULL) Index: stdlib/setenv.c =================================================================== RCS file: /cvsroot/src/lib/libc/stdlib/setenv.c,v retrieving revision 1.42 diff -u -r1.42 setenv.c --- stdlib/setenv.c 3 Nov 2010 15:01:07 -0000 1.42 +++ stdlib/setenv.c 5 Nov 2010 07:09:18 -0000 @@ -91,26 +91,23 @@ if (f != NULL) { if (!rewrite) goto good; + /* * Check whether the buffer was allocated via setenv(3) and * whether there is enough space. If so simply overwrite the * existing value. */ - if (environ[offset] == __environ_malloced[offset] && - strlen(f) >= l_value) { + if (__environ_kvsize(environ[offset]) >= size + l_value + 2) { c = f; goto copy; } } /* name + `=' + value */ - if ((c = malloc(size + l_value + 2)) == NULL) + if ((c = __environ_kvalloc(size + l_value + 2)) == NULL) goto bad; - if (environ[offset] == __environ_malloced[offset]) - free(__environ_malloced[offset]); - + __environ_kvfree(environ[offset]); environ[offset] = c; - __environ_malloced[offset] = c; if (f == NULL) __scrubenv(offset); Index: stdlib/unsetenv.c =================================================================== RCS file: /cvsroot/src/lib/libc/stdlib/unsetenv.c,v retrieving revision 1.9 diff -u -r1.9 unsetenv.c --- stdlib/unsetenv.c 30 Sep 2010 12:41:33 -0000 1.9 +++ stdlib/unsetenv.c 5 Nov 2010 07:09:18 -0000 @@ -67,22 +67,11 @@ if (rwlock_wrlock(&__environ_lock) != 0) return -1; - if (__allocenv(-1) == -1) { - rwlock_unlock(&__environ_lock); - return -1; - } - while (__findenv(name, &offset) != NULL) { /* if set multiple times */ - if (environ[offset] == __environ_malloced[offset]) - free(__environ_malloced[offset]); + __environ_kvfree(environ[offset]); - while (environ[offset] != NULL) { + for (; environ[offset] != NULL; offset++) environ[offset] = environ[offset + 1]; - __environ_malloced[offset] = - __environ_malloced[offset + 1]; - offset++; - } - __environ_malloced[offset] = NULL; } rwlock_unlock(&__environ_lock);