Cherry-pick UID/GID mapping support.
am: f240b14514

Change-Id: Ib1795ddb96a8f623c03d6f2bcb62e2a049e9ab10
diff --git a/squashfs-tools/mksquashfs.c b/squashfs-tools/mksquashfs.c
index 5ac11ad..6fe9555 100644
--- a/squashfs-tools/mksquashfs.c
+++ b/squashfs-tools/mksquashfs.c
@@ -274,6 +274,18 @@
 char *recovery_file = NULL;
 int recover = TRUE;
 
+/* uid/gid mapping tables */
+#define UGID_ENTRIES 340
+
+struct ugid_map_entry {
+	unsigned int	child_id;
+	unsigned int	parent_id;
+	unsigned int	length;
+};
+struct ugid_map_entry uid_mapping[UGID_ENTRIES], gid_mapping[UGID_ENTRIES];
+unsigned int uid_map_count = 0, gid_map_count = 0;
+
+
 struct id *id_hash_table[ID_ENTRIES];
 struct id *id_table[SQUASHFS_IDS], *sid_table[SQUASHFS_IDS];
 unsigned int uid_count = 0, guid_count = 0;
@@ -739,9 +751,33 @@
 }
 
 
-unsigned int get_uid(unsigned int uid)
+int resolve_child_ugid(unsigned int *ugid,
+	const struct ugid_map_entry *ugid_mapping,
+	unsigned int ugid_map_count)
 {
-	struct id *entry = get_id(uid);
+	unsigned int i;
+
+	for (i = 0; i < ugid_map_count; i++) {
+		if (ugid_mapping[i].parent_id <= *ugid &&
+			*ugid <
+			ugid_mapping[i].parent_id + ugid_mapping[i].length) {
+			*ugid = ugid_mapping[i].child_id + *ugid -
+				ugid_mapping[i].parent_id;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+unsigned int get_uid(unsigned int uid, int resolve)
+{
+	struct id *entry;
+
+	if (resolve && !resolve_child_ugid(&uid, uid_mapping, uid_map_count))
+		BAD_ERROR("uid not found in mapping: %d\n", uid);
+	entry = get_id(uid);
 
 	if(entry == NULL) {
 		if(id_count == SQUASHFS_IDS)
@@ -758,9 +794,13 @@
 }
 
 
-unsigned int get_guid(unsigned int guid)
+unsigned int get_guid(unsigned int guid, int resolve)
 {
-	struct id *entry = get_id(guid);
+	struct id *entry;
+
+	if (resolve && !resolve_child_ugid(&guid, gid_mapping, gid_map_count))
+		BAD_ERROR("gid not found in mapping: %d\n", guid);
+	entry = get_id(guid);
 
 	if(entry == NULL) {
 		if(id_count == SQUASHFS_IDS)
@@ -972,10 +1012,10 @@
 			
 	base->mode = SQUASHFS_MODE(buf->st_mode);
 	base->uid = get_uid((unsigned int) global_uid == -1 ?
-		buf->st_uid : global_uid);
+		buf->st_uid : global_uid, 1);
 	base->inode_type = type;
 	base->guid = get_guid((unsigned int) global_gid == -1 ?
-		buf->st_gid : global_gid);
+		buf->st_gid : global_gid, 1);
 	base->mtime = buf->st_mtime;
 	base->inode_number = get_inode_no(dir_ent->inode);
 
@@ -5300,6 +5340,63 @@
 }
 
 
+int parse_ugid_map(char *map_str,
+	struct ugid_map_entry ugid_mapping[UGID_ENTRIES],
+	unsigned int *ugid_map_count)
+{
+	char *line_state, *token_state;
+	char *line, *line_str, *token, *token_str;
+	long long numbers[3];
+	int i;
+
+	for (*ugid_map_count = 0, line_str = map_str;;
+		++*ugid_map_count, line_str = NULL) {
+		line = strtok_r(line_str, "\n", &line_state);
+		if (line == NULL)
+			break;
+                ERROR("line: %s\n", line);
+		if (*ugid_map_count >= UGID_ENTRIES) {
+			ERROR("Too many entries for u/gid mapping\n");
+			return -1;
+		}
+
+		for (i = 0, token_str = line; i < 3; i++, token_str = NULL) {
+			token = strtok_r(token_str, " ", &token_state);
+                        ERROR("token: %d, %s\n", i, token);
+			if (token == NULL ||
+				!parse_numberll(token, &numbers[i], 0) ||
+				numbers[i] < 0 || numbers[i] > ULONG_MAX) {
+				ERROR("Malformed u/gid mapping line1\n");
+				return -1;
+			}
+		}
+
+		if (numbers[0] + numbers[2] > ULONG_MAX) {
+			ERROR("u/gid mapping overflow\n");
+			return -1;
+		}
+
+		if (numbers[1] + numbers[2] > ULONG_MAX) {
+			ERROR("u/gid mapping overflow\n");
+			return -1;
+		}
+
+		if (strtok_r(NULL, " ", &token_state) != NULL) {
+			ERROR("Malformed u/gid mapping line2\n");
+			return -1;
+		}
+
+		ugid_mapping[*ugid_map_count].child_id =
+			(unsigned int)numbers[0];
+		ugid_mapping[*ugid_map_count].parent_id =
+			(unsigned int)numbers[1];
+		ugid_mapping[*ugid_map_count].length = (unsigned int)numbers[2];
+	}
+
+	return 0;
+}
+
+
 int get_physical_memory()
 {
 	int phys_mem;
@@ -5989,6 +6086,30 @@
 				exit(1);
 			}	
 			root_name = argv[i];
+		} else if (strcmp(argv[i], "-uid-map") == 0) {
+			if (++i == argc) {
+				ERROR("%s: -uid-map: missing mapping\n",
+					argv[0]);
+				exit(1);
+			}
+			if (parse_ugid_map(argv[i], uid_mapping,
+				&uid_map_count) != 0) {
+				ERROR("%s: -uid-map: invalid mapping\n",
+					argv[0]);
+				exit(1);
+			}
+		} else if (strcmp(argv[i], "-gid-map") == 0) {
+			if (++i == argc) {
+				ERROR("%s: -gid-map: missing mapping\n",
+					argv[0]);
+				exit(1);
+			}
+			if (parse_ugid_map(argv[i], gid_mapping,
+				&gid_map_count) != 0) {
+				ERROR("%s: -gid-map: invalid mapping\n",
+					argv[0]);
+				exit(1);
+			}
 		} else if(strcmp(argv[i], "-version") == 0) {
 			VERSION();
 		} else {
@@ -6077,6 +6198,12 @@
 				"dirs/files\n");
 			ERROR("-regex\t\t\tAllow POSIX regular expressions to "
 				"be used in exclude\n\t\t\tdirs/files\n");
+			ERROR("-uid-map <mapping>\tUser ID mapping.\n");
+			ERROR("\t\t\tFollows the format described in "
+				"user_namespaces(7).\n");
+			ERROR("-gid-map <mapping>\tGroup ID mapping.\n");
+			ERROR("\t\t\tFollows the format described in "
+				"user_namespaces(7).\n");
 			ERROR("\nFilesystem append options:\n");
 			ERROR("-noappend\t\tdo not append to existing "
 				"filesystem\n");
@@ -6129,6 +6256,19 @@
 		}
 	}
 
+	if (!uid_map_count) {
+		uid_mapping[0].child_id = 0;
+		uid_mapping[0].parent_id = 0;
+		uid_mapping[0].length = 4294967295u;
+		uid_map_count = 1;
+	}
+	if (!gid_map_count) {
+		gid_mapping[0].child_id = 0;
+		gid_mapping[0].parent_id = 0;
+		gid_mapping[0].length = 4294967295u;
+		gid_map_count = 1;
+	}
+
 /* ANDROID CHANGES START*/
 #ifdef ANDROID
 	if (fs_config_file) {
diff --git a/squashfs-tools/mksquashfs.h b/squashfs-tools/mksquashfs.h
index bfbf0bf..d5cc205 100644
--- a/squashfs-tools/mksquashfs.h
+++ b/squashfs-tools/mksquashfs.h
@@ -158,8 +158,8 @@
 extern void add_file(long long, long long, long long, unsigned int *, int,
 	unsigned int, int, int);
 extern struct id *create_id(unsigned int);
-extern unsigned int get_uid(unsigned int);
-extern unsigned int get_guid(unsigned int);
+extern unsigned int get_uid(unsigned int, int);
+extern unsigned int get_guid(unsigned int, int);
 extern int read_bytes(int, void *, int);
 extern unsigned short get_checksum_mem(char *, int);
 #endif
diff --git a/squashfs-tools/read_fs.c b/squashfs-tools/read_fs.c
index ca84460..3ea9262 100644
--- a/squashfs-tools/read_fs.c
+++ b/squashfs-tools/read_fs.c
@@ -214,8 +214,8 @@
 		/* bad type, corrupted filesystem */
 		goto corrupted;
 
-	get_uid(id_table[dir_inode->base.uid]);
-	get_guid(id_table[dir_inode->base.guid]);
+	get_uid(id_table[dir_inode->base.uid], 0);
+	get_guid(id_table[dir_inode->base.guid], 0);
 
 	/* allocate fragment to file mapping table */
 	file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *));
@@ -234,8 +234,8 @@
 			(unsigned int) (cur_ptr - *inode_table),
 			base.inode_type);
 
-		get_uid(id_table[base.uid]);
-		get_guid(id_table[base.guid]);
+		get_uid(id_table[base.uid], 0);
+		get_guid(id_table[base.guid], 0);
 
 		switch(base.inode_type) {
 		case SQUASHFS_FILE_TYPE: {