Merge "Reland "Support UID/GID mapping.""
diff --git a/contrib/android/e2fsdroid.c b/contrib/android/e2fsdroid.c
index 2fe922d..237df51 100644
--- a/contrib/android/e2fsdroid.c
+++ b/contrib/android/e2fsdroid.c
@@ -13,6 +13,15 @@
 #include "basefs_allocator.h"
 #include "create_inode.h"
 
+#ifndef UID_GID_MAP_MAX_EXTENTS
+/*
+ * The value is defined in linux/user_namspace.h.
+ * The value is (arbitrarily) 5 in 4.14 and earlier, or 340 in 4.15 and later.
+ * Here, the bigger value is taken. See also man user_namespace(7).
+ */
+#define UID_GID_MAP_MAX_EXTENTS 340
+#endif
+
 static char *prog_name = "e2fsdroid";
 static char *in_file;
 static char *block_list;
@@ -32,7 +41,8 @@
 {
 	fprintf(stderr, "%s [-B block_list] [-D basefs_out] [-T timestamp]\n"
 			"\t[-C fs_config] [-S file_contexts] [-p product_out]\n"
-			"\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] [-s] image\n",
+			"\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] [-s]\n"
+			"\t[-u uid-mapping] [-g gid-mapping] image\n",
                 prog_name);
 	exit(ret);
 }
@@ -55,6 +65,135 @@
 	return ret;
 }
 
+static int parse_ugid_map_entry(char* line, struct ugid_map_entry* result)
+{
+	char *token, *token_saveptr;
+	size_t num_tokens;
+	unsigned int *parsed[] = {&result->child_id,
+				  &result->parent_id,
+				  &result->length};
+	for (token = strtok_r(line, " ", &token_saveptr), num_tokens = 0;
+	     token && num_tokens < 3;
+	     token = strtok_r(NULL, " ", &token_saveptr), ++num_tokens) {
+		char* endptr = NULL;
+		*parsed[num_tokens] = strtoul(token, &endptr, 10);
+		if ((*parsed[num_tokens] == ULONG_MAX && errno) || *endptr) {
+			fprintf(stderr, "Malformed u/gid mapping line\n");
+			return 0;
+		}
+	}
+	if (num_tokens < 3 || strtok_r(NULL, " ", &token_saveptr) != NULL) {
+		fprintf(stderr, "Malformed u/gid mapping line\n");
+		return 0;
+	}
+	if (result->child_id + result->length < result->child_id ||
+	    result->parent_id + result->length < result->parent_id) {
+		fprintf(stderr, "u/gid mapping overflow\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Returns 1 if [begin1, begin1+length1) and [begin2, begin2+length2) have
+ * overlapping range. Otherwise 0.
+ */
+static int is_overlapping(unsigned int begin1, unsigned int length1,
+			  unsigned int begin2, unsigned int length2)
+{
+	unsigned int end1 = begin1 + length1;
+	unsigned int end2 = begin2 + length2;
+	return !(end1 <= begin2 || end2 <= begin1);
+}
+
+/*
+ * Verifies if the given mapping works.
+ * - Checks if the number of entries is less than or equals to
+ *   UID_GID_MAP_MAX_EXTENTS.
+ * - Checks if there is no overlapped ranges.
+ * Returns 1 if valid, otherwise 0.
+ */
+static int is_valid_ugid_map(const struct ugid_map* mapping)
+{
+	size_t i, j;
+
+	if (mapping->size > UID_GID_MAP_MAX_EXTENTS) {
+		fprintf(stderr, "too many u/gid mapping entries\n");
+		return 0;
+	}
+
+	for (i = 0; i < mapping->size; ++i) {
+		const struct ugid_map_entry *entry1 = &mapping->entries[i];
+		for (j = i + 1; j < mapping->size; ++j) {
+			const struct ugid_map_entry *entry2 =
+				&mapping->entries[j];
+			if (is_overlapping(entry1->child_id, entry1->length,
+					   entry2->child_id, entry2->length)) {
+				fprintf(stderr,
+					"Overlapping child u/gid: [%d %d %d],"
+					" [%d %d %d]\n",
+					entry1->child_id, entry1->parent_id,
+					entry1->length, entry2->child_id,
+					entry2->parent_id, entry2->length);
+				return 0;
+			}
+			if (is_overlapping(entry1->parent_id, entry1->length,
+					   entry2->parent_id, entry2->length)) {
+				fprintf(stderr,
+					"Overlapping parent u/gid: [%d %d %d],"
+					" [%d %d %d]\n",
+					entry1->child_id, entry1->parent_id,
+					entry1->length, entry2->child_id,
+					entry2->parent_id, entry2->length);
+				return 0;
+			}
+		}
+	}
+	return 1;
+}
+
+/*
+ * Parses the UID/GID mapping argument. The argument could be a multi-line
+ * string (separated by '\n', no trailing '\n' is allowed). Each line must
+ * contain exact three integer tokens; the first token is |child_id|,
+ * the second is |parent_id|, and the last is |length| of the mapping range.
+ * See also user_namespace(7) man page.
+ * On success, the parsed entries are stored in |result|, and it returns 1.
+ * Otherwise, returns 0.
+ */
+static int parse_ugid_map(char* arg, struct ugid_map* result)
+{
+	int i;
+	char *line, *line_saveptr;
+	size_t current_index;
+
+	/* Count the number of lines. */
+	result->size = 1;
+	for (i = 0; arg[i]; ++i) {
+		if (arg[i] == '\n')
+			++result->size;
+	}
+
+	/* Allocate memory for entries. */
+	result->entries = malloc(sizeof(struct ugid_map_entry) * result->size);
+	if (!result->entries) {
+		result->size = 0;
+		return 0;
+	}
+
+	/* Parse each line */
+	for (line = strtok_r(arg, "\n", &line_saveptr), current_index = 0;
+	     line;
+	     line = strtok_r(NULL, "\n", &line_saveptr), ++current_index) {
+		if (!parse_ugid_map_entry(
+			line, &result->entries[current_index])) {
+			return 0;
+		}
+	}
+
+	return is_valid_ugid_map(result);
+}
+
 int main(int argc, char *argv[])
 {
 	int c;
@@ -70,10 +209,11 @@
 	ext2_ino_t free_inodes_count;
 	blk64_t blocks_count;
 	blk64_t free_blocks_count;
+	struct ugid_map uid_map = { 0, NULL }, gid_map = { 0, NULL };
 
 	add_error_table(&et_ext2_error_table);
 
-	while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:es")) != EOF) {
+	while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:esu:g:")) != EOF) {
 		switch (c) {
 		case 'T':
 			fixed_time = strtoul(optarg, &p, 0);
@@ -122,6 +262,14 @@
 		case 's':
 			flags |= EXT2_FLAG_SHARE_DUP;
 			break;
+		case 'u':
+			if (!parse_ugid_map(optarg, &uid_map))
+				exit(EXIT_FAILURE);
+			break;
+		case 'g':
+			if (!parse_ugid_map(optarg, &gid_map))
+				exit(EXIT_FAILURE);
+			break;
 		default:
 			usage(EXIT_FAILURE);
 		}
@@ -173,8 +321,9 @@
 	}
 
 	if (android_configure) {
-		retval = android_configure_fs(fs, src_dir, product_out, mountpoint,
-			seopt_file, nr_opt, fs_config_file, fixed_time);
+		retval = android_configure_fs(
+			fs, src_dir, product_out, mountpoint, seopt_file,
+			nr_opt, fs_config_file, fixed_time, &uid_map, &gid_map);
 		if (retval) {
 			com_err(prog_name, retval, "%s",
 				"while configuring the file system");
diff --git a/contrib/android/perms.c b/contrib/android/perms.c
index d83ad35..546c059 100644
--- a/contrib/android/perms.c
+++ b/contrib/android/perms.c
@@ -23,6 +23,8 @@
 	fs_config_f fs_config_func;
 	struct selabel_handle *sehnd;
 	time_t fixed_time;
+	const struct ugid_map* uid_map;
+	const struct ugid_map* gid_map;
 };
 
 static errcode_t ino_add_xattr(ext2_filsys fs, ext2_ino_t ino, const char *name,
@@ -96,6 +98,26 @@
 	return retval;
 }
 
+/*
+ * Returns mapped UID/GID if there is a corresponding entry in |mapping|.
+ * Otherwise |id| as is.
+ */
+static unsigned int resolve_ugid(const struct ugid_map* mapping,
+				 unsigned int id)
+{
+	size_t i;
+	for (i = 0; i < mapping->size; ++i) {
+		const struct ugid_map_entry* entry = &mapping->entries[i];
+		if (entry->parent_id <= id &&
+		    id < entry->parent_id + entry->length) {
+			return id + entry->child_id - entry->parent_id;
+		}
+	}
+
+	/* No entry is found. */
+	return id;
+}
+
 static errcode_t set_perms_and_caps(ext2_filsys fs, ext2_ino_t ino,
 				    struct inode_params *params)
 {
@@ -116,8 +138,12 @@
 		params->fs_config_func(params->filename, S_ISDIR(inode.i_mode),
 				       params->target_out, &uid, &gid, &imode,
 				       &capabilities);
-		inode.i_uid = uid & 0xffff;
-		inode.i_gid = gid & 0xffff;
+		uid = resolve_ugid(params->uid_map, uid);
+		gid = resolve_ugid(params->gid_map, gid);
+		inode.i_uid = (__u16) uid;
+		inode.i_gid = (__u16) gid;
+		ext2fs_set_i_uid_high(inode, (__u16) (uid >> 16));
+		ext2fs_set_i_gid_high(inode, (__u16) (gid >> 16));
 		inode.i_mode = (inode.i_mode & S_IFMT) | (imode & 0xffff);
 		retval = ext2fs_write_inode(fs, ino, &inode);
 		if (retval) {
@@ -256,7 +282,9 @@
 				 char *mountpoint,
 				 fs_config_f fs_config_func,
 				 struct selabel_handle *sehnd,
-				 time_t fixed_time)
+				 time_t fixed_time,
+				 const struct ugid_map* uid_map,
+				 const struct ugid_map* gid_map)
 {
 	errcode_t retval;
 	struct inode_params params = {
@@ -269,6 +297,8 @@
 		.path = mountpoint,
 		.filename = mountpoint,
 		.mountpoint = mountpoint,
+		.uid_map = uid_map,
+		.gid_map = gid_map,
 	};
 
 	/* walk_dir will add the "/". Don't add it twice. */
@@ -290,7 +320,9 @@
 			       char *mountpoint,
 			       struct selinux_opt *seopts EXT2FS_ATTR((unused)),
 			       unsigned int nopt EXT2FS_ATTR((unused)),
-			       char *fs_config_file, time_t fixed_time)
+			       char *fs_config_file, time_t fixed_time,
+			       const struct ugid_map* uid_map,
+			       const struct ugid_map* gid_map)
 {
 	errcode_t retval;
 	fs_config_f fs_config_func = NULL;
@@ -330,5 +362,6 @@
 		fs_config_func = fs_config;
 
 	return __android_configure_fs(fs, src_dir, target_out, mountpoint,
-				      fs_config_func, sehnd, fixed_time);
+				      fs_config_func, sehnd, fixed_time,
+				      uid_map, gid_map);
 }
diff --git a/contrib/android/perms.h b/contrib/android/perms.h
index c404cb9..6d6a212 100644
--- a/contrib/android/perms.h
+++ b/contrib/android/perms.h
@@ -9,6 +9,25 @@
 			    unsigned *uid, unsigned *gid,
 			    unsigned *mode, uint64_t *capabilities);
 
+/*
+ * Represents a range of UID/GID mapping.
+ * This maps the id in [|parent_id|, |parent_id| + |length|) into
+ * [|child_id|, |child_id| + |length|)
+ */
+struct ugid_map_entry {
+	unsigned int child_id;
+	unsigned int parent_id;
+	unsigned int length;
+};
+
+struct ugid_map {
+	/* The number of elements in |entries|. */
+	size_t size;
+
+	/* An array of entries. If |size| is 0, this is a null pointer. */
+	struct ugid_map_entry* entries;
+};
+
 # ifdef _WIN32
 struct selabel_handle;
 static inline errcode_t android_configure_fs(ext2_filsys fs,
@@ -18,7 +37,9 @@
 					     void *seopts,
 					     unsigned int nopt,
 					     char *fs_config_file,
-					     time_t fixed_time)
+					     time_t fixed_time,
+					     const struct ugid_map* uid_map,
+					     const struct ugdi_map* gid_map)
 {
 	return 0;
 }
@@ -36,7 +57,9 @@
 			       char *mountpoint,
 			       struct selinux_opt *seopts,
 			       unsigned int nopt,
-			       char *fs_config_file, time_t fixed_time);
+			       char *fs_config_file, time_t fixed_time,
+			       const struct ugid_map* uid_map,
+			       const struct ugid_map* gid_map);
 
 # endif
 #endif /* !ANDROID_PERMS_H */