Merge "test_atomics is an ARM specific test"
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index 97cd26f..64fcc84 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -97,7 +97,12 @@
 
 #ifndef USE_MINGW
 /* Read a local directory and create the same tree in the generated filesystem.
-   Calls itself recursively with each directory in the given directory */
+   Calls itself recursively with each directory in the given directory.
+   full_path is an absolute or relative path, with a trailing slash, to the
+   directory on disk that should be copied, or NULL if this is a directory
+   that does not exist on disk (e.g. lost+found).
+   dir_path is an absolute path, with trailing slash, to the same directory
+   if the image were mounted at the specified mount point */
 static u32 build_directory_structure(const char *full_path, const char *dir_path,
 		u32 dir_inode, fs_config_func_t fs_config_func,
 		struct selabel_handle *sehnd)
@@ -139,8 +144,8 @@
 		if (dentries[i].filename == NULL)
 			critical_error_errno("strdup");
 
-		asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name);
-		asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name);
+		asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
+		asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
 
 		free(namelist[i]);
 
@@ -171,17 +176,14 @@
 		}
 #ifndef USE_MINGW
 		if (sehnd) {
-			char *sepath = NULL;
-			asprintf(&sepath, "/%s", dentries[i].path);
-			if (selabel_lookup(sehnd, &dentries[i].secon, sepath, stat.st_mode) < 0) {
-				error("cannot lookup security context for %s", sepath);
+			if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
+				error("cannot lookup security context for %s", dentries[i].path);
 			}
 #if 0
 			// TODO make this a debug flag
 			if (dentries[i].secon)
-				printf("Labeling %s as %s\n", sepath, dentries[i].secon);
+				printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
 #endif
-			free(sepath);
 		}
 #endif
 
@@ -218,7 +220,7 @@
 		dentries = tmp;
 
 		dentries[0].filename = strdup("lost+found");
-		asprintf(&dentries[0].path, "%s/lost+found", dir_path);
+		asprintf(&dentries[0].path, "%slost+found", dir_path);
 		dentries[0].full_path = NULL;
 		dentries[0].size = 0;
 		dentries[0].mode = S_IRWXU;
@@ -226,11 +228,8 @@
 		dentries[0].uid = 0;
 		dentries[0].gid = 0;
 		if (sehnd) {
-			char *sepath = NULL;
-			asprintf(&sepath, "/%s", dentries[0].path);
-			if (selabel_lookup(sehnd, &dentries[0].secon, sepath, dentries[0].mode) < 0)
+			if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
 				error("cannot lookup security context for %s", dentries[0].path);
-			free(sepath);
 		}
 		entries++;
 		dirs++;
@@ -242,8 +241,20 @@
 		if (dentries[i].file_type == EXT4_FT_REG_FILE) {
 			entry_inode = make_file(dentries[i].full_path, dentries[i].size);
 		} else if (dentries[i].file_type == EXT4_FT_DIR) {
-			entry_inode = build_directory_structure(dentries[i].full_path,
-					dentries[i].path, inode, fs_config_func, sehnd);
+			char *subdir_full_path = NULL;
+			char *subdir_dir_path;
+			if (dentries[i].full_path) {
+				ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
+				if (ret < 0)
+					critical_error_errno("asprintf");
+			}
+			ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
+			if (ret < 0)
+				critical_error_errno("asprintf");
+			entry_inode = build_directory_structure(subdir_full_path,
+					subdir_dir_path, inode, fs_config_func, sehnd);
+			free(subdir_full_path);
+			free(subdir_dir_path);
 		} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
 			entry_inode = make_link(dentries[i].full_path, dentries[i].link);
 		} else {
@@ -363,17 +374,87 @@
 	return status;
 }
 
-int make_ext4fs_internal(int fd, const char *directory,
-                         const char *mountpoint, fs_config_func_t fs_config_func, int gzip,
+/* return a newly-malloc'd string that is a copy of str.  The new string
+   is guaranteed to have a trailing slash.  If absolute is true, the new string
+   is also guaranteed to have a leading slash.
+*/
+static char *canonicalize_slashes(const char *str, bool absolute)
+{
+	char *ret;
+	int len = strlen(str);
+	int newlen = len;
+	char *ptr;
+
+	if (len == 0 && absolute) {
+		return strdup("/");
+	}
+
+	if (str[0] != '/' && absolute) {
+		newlen++;
+	}
+	if (str[len - 1] != '/') {
+		newlen++;
+	}
+	ret = malloc(newlen + 1);
+	if (!ret) {
+		critical_error("malloc");
+	}
+
+	ptr = ret;
+	if (str[0] != '/' && absolute) {
+		*ptr++ = '/';
+	}
+
+	strcpy(ptr, str);
+	ptr += len;
+
+	if (str[len - 1] != '/') {
+		*ptr++ = '/';
+	}
+
+	if (ptr != ret + newlen) {
+		critical_error("assertion failed\n");
+	}
+
+	*ptr = '\0';
+
+	return ret;
+}
+
+static char *canonicalize_abs_slashes(const char *str)
+{
+	return canonicalize_slashes(str, true);
+}
+
+static char *canonicalize_rel_slashes(const char *str)
+{
+	return canonicalize_slashes(str, false);
+}
+
+int make_ext4fs_internal(int fd, const char *_directory,
+                         const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
                          int sparse, int crc, int wipe, int init_itabs,
                          struct selabel_handle *sehnd)
 {
 	u32 root_inode_num;
 	u16 root_mode;
+	char *mountpoint;
+	char *directory = NULL;
+	int ret;
 
 	if (setjmp(setjmp_env))
 		return EXIT_FAILURE; /* Handle a call to longjmp() */
 
+	if (_mountpoint == NULL) {
+		mountpoint = strdup("");
+	} else {
+		mountpoint = canonicalize_abs_slashes(_mountpoint);
+	}
+
+	if (_directory) {
+		directory = canonicalize_rel_slashes(_directory);
+	}
+
 	if (info.len <= 0)
 		info.len = get_file_size(fd);
 
@@ -471,23 +552,15 @@
 
 #ifndef USE_MINGW
 	if (sehnd) {
-		char *sepath = NULL;
 		char *secontext = NULL;
 
-		if (mountpoint[0] == '/')
-			sepath = strdup(mountpoint);
-		else
-			asprintf(&sepath, "/%s", mountpoint);
-		if (!sepath)
-			critical_error_errno("malloc");
-		if (selabel_lookup(sehnd, &secontext, sepath, S_IFDIR) < 0) {
-			error("cannot lookup security context for %s", sepath);
+		if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
+			error("cannot lookup security context for %s", mountpoint);
 		}
 		if (secontext) {
-			printf("Labeling %s as %s\n", sepath, secontext);
+			printf("Labeling %s as %s\n", mountpoint, secontext);
 			inode_set_selinux(root_inode_num, secontext);
 		}
-		free(sepath);
 		freecon(secontext);
 	}
 #endif
@@ -513,5 +586,8 @@
 	sparse_file_destroy(info.sparse_file);
 	info.sparse_file = NULL;
 
+	free(mountpoint);
+	free(directory);
+
 	return 0;
 }
diff --git a/ext4_utils/make_ext4fs_main.c b/ext4_utils/make_ext4fs_main.c
index 000b857..3f6d35b 100644
--- a/ext4_utils/make_ext4fs_main.c
+++ b/ext4_utils/make_ext4fs_main.c
@@ -52,7 +52,7 @@
 	int opt;
 	const char *filename = NULL;
 	const char *directory = NULL;
-	char *mountpoint = "";
+	char *mountpoint = NULL;
 	fs_config_func_t fs_config_func = NULL;
 	int gzip = 0;
 	int sparse = 0;
@@ -138,7 +138,7 @@
 
 #if !defined(HOST)
 	// Use only if -S option not requested
-	if (!sehnd && mountpoint[0] != '\0') {
+	if (!sehnd && mountpoint) {
 		sehnd = selinux_android_file_context_handle();
 
 		if (!sehnd) {
@@ -180,7 +180,7 @@
 	if (strcmp(filename, "-")) {
 		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
 		if (fd < 0) {
-			error_errno("open");
+			perror("open");
 			return EXIT_FAILURE;
 		}
 	} else {