diff --git a/fs/vfs/main.c b/fs/vfs/main.c
index 43420b9b0e6e15abe2553fb1e5d958b0b563566a..4bc5a5155dad13f2d0b98e2267d52976eb45a0d3 100755
--- a/fs/vfs/main.c
+++ b/fs/vfs/main.c
@@ -646,6 +646,12 @@ int __lxstat(int ver, const char *pathname, struct stat *st)
 }
 LFS64(__lxstat);
 
+int lstat(const char *pathname, struct stat *st)
+{
+	return __lxstat(1, pathname, st);
+}
+LFS64(lstat);
+
 int __statfs(const char *pathname, struct statfs *buf)
 {
 	struct task *t = main_task;
diff --git a/include/osv/mutex.h b/include/osv/mutex.h
index 5e00a105da5cbf5922ac99f73506b3e1e62ff1b4..3913e8204472e0b2d3e00abd52e1e85a44610974 100644
--- a/include/osv/mutex.h
+++ b/include/osv/mutex.h
@@ -2,6 +2,7 @@
 #define MUTEX_H_
 
 #include <stdbool.h>
+#include <string.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -32,6 +33,7 @@ void mutex_unlock(mutex_t* m);
 
 static __always_inline void mutex_init(mutex_t* m)
 {
+    memset(m, 0, sizeof(*m));
 }
 
 static __always_inline void mutex_destroy(mutex_t* m)
diff --git a/libc/build.mak b/libc/build.mak
index 95d8dab907d755968aae3efc1bba197337d33766..c6a7c23adf8a45c8d2b91fa73e4bd18c748a76b8 100644
--- a/libc/build.mak
+++ b/libc/build.mak
@@ -6,7 +6,6 @@ libc += internal/libc.o
 libc += internal/__lock.o
 libc += internal/shgetc.o
 
-libc += ctype/__ctype_b_loc.o
 libc += ctype/__ctype_get_mb_cur_max.o
 libc += ctype/__ctype_tolower_loc.o
 libc += ctype/__ctype_toupper_loc.o
@@ -44,6 +43,15 @@ libc += ctype/wcswidth.o
 libc += ctype/wctrans.o
 libc += ctype/wcwidth.o
 
+libc += env/__environ.o
+libc += env/clearenv.o
+libc += env/getenv.o
+libc += env/putenv.o
+libc += env/setenv.o
+libc += env/unsetenv.o
+
+libc += ctype/__ctype_b_loc.o
+
 libc += errno/strerror.o
 
 libc += exit/assert.o
@@ -329,6 +337,7 @@ libc += misc/getopt_long.o
 libc += misc/getresuid.o
 libc += misc/getresgid.o
 libc += misc/getsubopt.o
+libc += misc/realpath.o
 libc += misc/setdomainname.o
 
 libc += multibyte/btowc.o
diff --git a/libc/env/__environ.c b/libc/env/__environ.c
new file mode 100644
index 0000000000000000000000000000000000000000..37d5160c04d4cbd3c001ac51685acd2364749dba
--- /dev/null
+++ b/libc/env/__environ.c
@@ -0,0 +1,9 @@
+#include "libc.h"
+
+static char *__initial_environ = NULL;
+
+#undef environ
+char **__environ = &__initial_environ;
+weak_alias(__environ, ___environ);
+weak_alias(__environ, _environ);
+weak_alias(__environ, environ);
diff --git a/libc/env/clearenv.c b/libc/env/clearenv.c
new file mode 100644
index 0000000000000000000000000000000000000000..a2475ce76cef3be53299676568fb6f414d4a1a03
--- /dev/null
+++ b/libc/env/clearenv.c
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+
+extern char **__environ;
+
+int clearenv()
+{
+	__environ[0] = 0;
+	return 0;
+}
diff --git a/libc/env/getenv.c b/libc/env/getenv.c
new file mode 100644
index 0000000000000000000000000000000000000000..5317efdbf258645764bbef32fbc7b70972a2db64
--- /dev/null
+++ b/libc/env/getenv.c
@@ -0,0 +1,16 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "libc.h"
+
+char *getenv(const char *name)
+{
+	int i;
+	size_t l = strlen(name);
+
+	if (!__environ || !*name || strchr(name, '=')) return NULL;
+	for (i=0; __environ[i] && (strncmp(name, __environ[i], l)
+		|| __environ[i][l] != '='); i++);
+	if (__environ[i]) return __environ[i] + l+1;
+	return NULL;
+}
diff --git a/libc/env/putenv.c b/libc/env/putenv.c
new file mode 100644
index 0000000000000000000000000000000000000000..d141db138893c55f3c13575f794817d75b001b7f
--- /dev/null
+++ b/libc/env/putenv.c
@@ -0,0 +1,59 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+extern char **__environ;
+char **__env_map;
+
+int __putenv(char *s, int a)
+{
+	int i=0, j=0;
+	char *z = strchr(s, '=');
+	char **newenv = 0;
+	char **newmap = 0;
+	static char **oldenv;
+
+	if (!z) return unsetenv(s);
+	if (z==s) return -1;
+	for (; __environ[i] && memcmp(s, __environ[i], z-s+1); i++);
+	if (a) {
+		if (!__env_map) {
+			__env_map = calloc(2, sizeof(char *));
+			if (__env_map) __env_map[0] = s;
+		} else {
+			for (; __env_map[j] && __env_map[j] != __environ[i]; j++);
+			if (!__env_map[j]) {
+				newmap = realloc(__env_map, sizeof(char *)*(j+2));
+				if (newmap) {
+					__env_map = newmap;
+					__env_map[j] = s;
+					__env_map[j+1] = NULL;
+				}
+			} else {
+				free(__env_map[j]);
+			}
+		}
+	}
+	if (!__environ[i]) {
+		newenv = malloc(sizeof(char *)*(i+2));
+		if (!newenv) {
+			if (a && __env_map) __env_map[j] = 0;
+			return -1;
+		}
+		memcpy(newenv, __environ, sizeof(char *)*i);
+		newenv[i] = s;
+		newenv[i+1] = 0;
+		__environ = newenv;
+		free(oldenv);
+		oldenv = __environ;
+	}
+
+	__environ[i] = s;
+	return 0;
+}
+
+int putenv(char *s)
+{
+	return __putenv(s, 0);
+}
diff --git a/libc/env/setenv.c b/libc/env/setenv.c
new file mode 100644
index 0000000000000000000000000000000000000000..c2c25444cbbd069c1eb99cd9f9ec8a57be0d18ce
--- /dev/null
+++ b/libc/env/setenv.c
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+int __putenv(char *s, int a);
+
+int setenv(const char *var, const char *value, int overwrite)
+{
+	char *s;
+	int l1, l2;
+
+	if (!var || !*var || strchr(var, '=')) {
+		errno = EINVAL;
+		return -1;
+	}
+	if (!overwrite && getenv(var)) return 0;
+
+	l1 = strlen(var);
+	l2 = strlen(value);
+	s = malloc(l1+l2+2);
+	memcpy(s, var, l1);
+	s[l1] = '=';
+	memcpy(s+l1+1, value, l2);
+	s[l1+l2+1] = 0;
+	if (__putenv(s, 1)) {
+		free(s);
+		errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
diff --git a/libc/env/unsetenv.c b/libc/env/unsetenv.c
new file mode 100644
index 0000000000000000000000000000000000000000..7493d97084dd4480b2589464d56791c6ab917926
--- /dev/null
+++ b/libc/env/unsetenv.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+extern char **__environ;
+extern char **__env_map;
+
+int unsetenv(const char *name)
+{
+	int i, j;
+	size_t l = strlen(name);
+
+	if (!*name || strchr(name, '=')) {
+		errno = EINVAL;
+		return -1;
+	}
+again:
+	for (i=0; __environ[i] && (memcmp(name, __environ[i], l) || __environ[i][l] != '='); i++);
+	if (__environ[i]) {
+		if (__env_map) {
+			for (j=0; __env_map[j] && __env_map[j] != __environ[i]; j++);
+			free (__env_map[j]);
+			for (; __env_map[j]; j++)
+				__env_map[j] = __env_map[j+1];
+		}
+		for (; __environ[i]; i++)
+			__environ[i] = __environ[i+1];
+		goto again;
+	}
+	return 0;
+}
diff --git a/libc/internal/libc.h b/libc/internal/libc.h
index 6d48bcb0963ab7e925eddc89cfd820a311bde390..3deff59ee9ad8922435990989c23969b7c1303dd 100644
--- a/libc/internal/libc.h
+++ b/libc/internal/libc.h
@@ -37,6 +37,9 @@ void __unlockfile(FILE *) ATTR_LIBC_VISIBILITY;
 #define LOCK(x) (libc.threads_minus_1 ? (__lock(x),1) : ((void)(x),1))
 #define UNLOCK(x) (libc.threads_minus_1 ? (__unlock(x),1) : ((void)(x),1))
 
+extern char **__environ;
+#define environ __environ
+
 #undef weak_alias
 #define weak_alias(old, new) \
 	extern __typeof(old) new __attribute__((weak, alias(#old)))
diff --git a/libc/libc.cc b/libc/libc.cc
index 9cce265e9f1c43187a4ee81d56057795f7fb3588..dce126c40057fa3a65e5d73e534489e8fde25d73 100644
--- a/libc/libc.cc
+++ b/libc/libc.cc
@@ -26,47 +26,6 @@ int* __errno_location()
     return &errno;
 }
 
-char* realpath(const char* path, char* resolved_path)
-{
-    // assumes cwd == /
-    std::vector<std::string> components;
-    std::string spath = path;
-    boost::split(components, spath, [] (char c) { return c == '/'; });
-    std::vector<std::string> tmp;
-    for (auto c : components) {
-        if (c == "" || c == ".") {
-            continue;
-        } else if (c == "..") {
-            if (!tmp.empty()) {
-                tmp.pop_back();
-            }
-        } else {
-            tmp.push_back(c);
-        }
-    }
-    std::string ret;
-    for (auto c : tmp) {
-        ret += "/" + c;
-    }
-    ret = ret.substr(0, PATH_MAX - 1);
-    if (!resolved_path) {
-        resolved_path = static_cast<char*>(malloc(ret.size() + 1));
-    }
-    strcpy(resolved_path, ret.c_str());
-    return resolved_path;
-}
-
-char* getenv(const char* name)
-{
-    // no environment
-    return NULL;
-}
-
-int putenv(char* string)
-{
-    return 0; // no environent
-}
-
 int getrlimit(int resource, struct rlimit *rlim)
 {
     auto set = [=] (rlim_t r) { rlim->rlim_cur = rlim->rlim_max = r; };
diff --git a/libc/manifest.txt b/libc/manifest.txt
index c205b49598df4a4d0311ef87943a92787762dca0..b91e89200c16fde5b7855f02268e330ef52df99a 100644
--- a/libc/manifest.txt
+++ b/libc/manifest.txt
@@ -5,6 +5,7 @@ All the files under the following directories are imported from musl:
   errno
   string
   ctype
+  env
   exit
   internal
   locale
@@ -18,12 +19,17 @@ All the files under the following directories are imported from musl:
   time
   unistd
 
-With the exceptions of:
+With the following exceptions:
+
+1) written locally largely based on musl code:
 
   stdio/__fprintf_chk.c
   string/rawmemchr.c
 
-which were written locally largely based on musl code.
+2) imported from FreeBSD:
+
+  misc/realpath.c
+
 
 All imports are from musl.git commit f05f59b804f83ff8443ba8c549d6be3ba79e71a6
 and have various local modification to deal with OSv specifics or our
diff --git a/libc/misc/realpath.c b/libc/misc/realpath.c
new file mode 100644
index 0000000000000000000000000000000000000000..8fe0e9ec14ee66dd2d3065bcdb3513d5b294b7aa
--- /dev/null
+++ b/libc/misc/realpath.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osv/prex.h> /* for strlcpy / strlcat */
+
+/*
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components.  Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ */
+char *
+realpath(const char * __restrict path, char * __restrict resolved)
+{
+	struct stat sb;
+	char *p, *q, *s;
+	size_t left_len, resolved_len;
+	unsigned symlinks;
+	int m, slen;
+	char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
+
+	if (path == NULL) {
+		errno = EINVAL;
+		return (NULL);
+	}
+	if (path[0] == '\0') {
+		errno = ENOENT;
+		return (NULL);
+	}
+	if (resolved == NULL) {
+		resolved = malloc(PATH_MAX);
+		if (resolved == NULL)
+			return (NULL);
+		m = 1;
+	} else
+		m = 0;
+	symlinks = 0;
+	if (path[0] == '/') {
+		resolved[0] = '/';
+		resolved[1] = '\0';
+		if (path[1] == '\0')
+			return (resolved);
+		resolved_len = 1;
+		left_len = strlcpy(left, path + 1, sizeof(left));
+	} else {
+		if (getcwd(resolved, PATH_MAX) == NULL) {
+			if (m)
+				free(resolved);
+			else {
+				resolved[0] = '.';
+				resolved[1] = '\0';
+			}
+			return (NULL);
+		}
+		resolved_len = strlen(resolved);
+		left_len = strlcpy(left, path, sizeof(left));
+	}
+	if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
+		if (m)
+			free(resolved);
+		errno = ENAMETOOLONG;
+		return (NULL);
+	}
+
+	/*
+	 * Iterate over path components in `left'.
+	 */
+	while (left_len != 0) {
+		/*
+		 * Extract the next path component and adjust `left'
+		 * and its length.
+		 */
+		p = strchr(left, '/');
+		s = p ? p : left + left_len;
+		if (s - left >= sizeof(next_token)) {
+			if (m)
+				free(resolved);
+			errno = ENAMETOOLONG;
+			return (NULL);
+		}
+		memcpy(next_token, left, s - left);
+		next_token[s - left] = '\0';
+		left_len -= s - left;
+		if (p != NULL)
+			memmove(left, s + 1, left_len + 1);
+		if (resolved[resolved_len - 1] != '/') {
+			if (resolved_len + 1 >= PATH_MAX) {
+				if (m)
+					free(resolved);
+				errno = ENAMETOOLONG;
+				return (NULL);
+			}
+			resolved[resolved_len++] = '/';
+			resolved[resolved_len] = '\0';
+		}
+		if (next_token[0] == '\0') {
+			/*
+			 * Handle consequential slashes.  The path
+			 * before slash shall point to a directory.
+			 *
+			 * Only the trailing slashes are not covered
+			 * by other checks in the loop, but we verify
+			 * the prefix for any (rare) "//" or "/\0"
+			 * occurence to not implement lookahead.
+			 */
+			if (lstat(resolved, &sb) != 0) {
+				if (m)
+					free(resolved);
+				return (NULL);
+			}
+			if (!S_ISDIR(sb.st_mode)) {
+				if (m)
+					free(resolved);
+				errno = ENOTDIR;
+				return (NULL);
+			}
+			continue;
+		}
+		else if (strcmp(next_token, ".") == 0)
+			continue;
+		else if (strcmp(next_token, "..") == 0) {
+			/*
+			 * Strip the last path component except when we have
+			 * single "/"
+			 */
+			if (resolved_len > 1) {
+				resolved[resolved_len - 1] = '\0';
+				q = strrchr(resolved, '/') + 1;
+				*q = '\0';
+				resolved_len = q - resolved;
+			}
+			continue;
+		}
+
+		/*
+		 * Append the next path component and lstat() it.
+		 */
+		resolved_len = strlcat(resolved, next_token, PATH_MAX);
+		if (resolved_len >= PATH_MAX) {
+			if (m)
+				free(resolved);
+			errno = ENAMETOOLONG;
+			return (NULL);
+		}
+		if (lstat(resolved, &sb) != 0) {
+			if (m)
+				free(resolved);
+			return (NULL);
+		}
+		if (S_ISLNK(sb.st_mode)) {
+			if (symlinks++ > MAXSYMLINKS) {
+				if (m)
+					free(resolved);
+				errno = ELOOP;
+				return (NULL);
+			}
+			slen = readlink(resolved, symlink, sizeof(symlink) - 1);
+			if (slen < 0) {
+				if (m)
+					free(resolved);
+				return (NULL);
+			}
+			symlink[slen] = '\0';
+			if (symlink[0] == '/') {
+				resolved[1] = 0;
+				resolved_len = 1;
+			} else if (resolved_len > 1) {
+				/* Strip the last path component. */
+				resolved[resolved_len - 1] = '\0';
+				q = strrchr(resolved, '/') + 1;
+				*q = '\0';
+				resolved_len = q - resolved;
+			}
+
+			/*
+			 * If there are any path components left, then
+			 * append them to symlink. The result is placed
+			 * in `left'.
+			 */
+			if (p != NULL) {
+				if (symlink[slen - 1] != '/') {
+					if (slen + 1 >= sizeof(symlink)) {
+						if (m)
+							free(resolved);
+						errno = ENAMETOOLONG;
+						return (NULL);
+					}
+					symlink[slen] = '/';
+					symlink[slen + 1] = 0;
+				}
+				left_len = strlcat(symlink, left,
+				    sizeof(symlink));
+				if (left_len >= sizeof(left)) {
+					if (m)
+						free(resolved);
+					errno = ENAMETOOLONG;
+					return (NULL);
+				}
+			}
+			left_len = strlcpy(left, symlink, sizeof(left));
+		}
+	}
+
+	/*
+	 * Remove trailing slash except when the resolved pathname
+	 * is a single "/".
+	 */
+	if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
+		resolved[resolved_len - 1] = '\0';
+	return (resolved);
+}