TOMOYO: Add pathname grouping support.

This patch adds pathname grouping support, which is useful for grouping
pathnames that cannot be represented using /\{dir\}/ pattern.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 62e089c..b5dbdc9 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -76,6 +76,49 @@
 				const int buffer_len);
 
 /**
+ * tomoyo_parse_name_union - Parse a tomoyo_name_union.
+ *
+ * @filename: Name or name group.
+ * @ptr:      Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_name_union(const char *filename,
+			     struct tomoyo_name_union *ptr)
+{
+	if (!tomoyo_is_correct_path(filename, 0, 0, 0))
+		return false;
+	if (filename[0] == '@') {
+		ptr->group = tomoyo_get_path_group(filename + 1);
+		ptr->is_group = true;
+		return ptr->group != NULL;
+	}
+	ptr->filename = tomoyo_get_name(filename);
+	ptr->is_group = false;
+	return ptr->filename != NULL;
+}
+
+/**
+ * tomoyo_print_name_union - Print a tomoyo_name_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head,
+				 const struct tomoyo_name_union *ptr)
+{
+	int pos = head->read_avail;
+	if (pos && head->read_buf[pos - 1] == ' ')
+		head->read_avail--;
+	if (ptr->is_group)
+		return tomoyo_io_printf(head, " @%s",
+					ptr->group->group_name->name);
+	return tomoyo_io_printf(head, " %s", ptr->filename->name);
+}
+
+/**
  * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
  *
  * @str: Pointer to the string.
@@ -172,6 +215,33 @@
 }
 
 /**
+ * tomoyo_tokenize - Tokenize string.
+ *
+ * @buffer: The line to tokenize.
+ * @w:      Pointer to "char *".
+ * @size:   Sizeof @w .
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_tokenize(char *buffer, char *w[], size_t size)
+{
+	int count = size / sizeof(char *);
+	int i;
+	for (i = 0; i < count; i++)
+		w[i] = "";
+	for (i = 0; i < count; i++) {
+		char *cp = strchr(buffer, ' ');
+		if (cp)
+			*cp = '\0';
+		w[i] = buffer;
+		if (!cp)
+			break;
+		buffer = cp + 1;
+	}
+	return i < count || !*buffer;
+}
+
+/**
  * tomoyo_is_correct_path - Validate a pathname.
  * @filename:     The pathname to check.
  * @start_type:   Should the pathname start with '/'?
@@ -1368,21 +1438,20 @@
 {
 	int pos;
 	u8 bit;
-	const char *filename;
 	const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16);
 
-	filename = ptr->filename->name;
 	for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
-		const char *msg;
 		if (!(perm & (1 << bit)))
 			continue;
 		/* Print "read/write" instead of "read" and "write". */
 		if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE)
 		    && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
 			continue;
-		msg = tomoyo_path2keyword(bit);
 		pos = head->read_avail;
-		if (!tomoyo_io_printf(head, "allow_%s %s\n", msg, filename))
+		if (!tomoyo_io_printf(head, "allow_%s ",
+				      tomoyo_path2keyword(bit)) ||
+		    !tomoyo_print_name_union(head, &ptr->name) ||
+		    !tomoyo_io_printf(head, "\n"))
 			goto out;
 	}
 	head->read_bit = 0;
@@ -1405,21 +1474,18 @@
 				   struct tomoyo_path2_acl *ptr)
 {
 	int pos;
-	const char *filename1;
-	const char *filename2;
 	const u8 perm = ptr->perm;
 	u8 bit;
 
-	filename1 = ptr->filename1->name;
-	filename2 = ptr->filename2->name;
 	for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
-		const char *msg;
 		if (!(perm & (1 << bit)))
 			continue;
-		msg = tomoyo_path22keyword(bit);
 		pos = head->read_avail;
-		if (!tomoyo_io_printf(head, "allow_%s %s %s\n", msg,
-				      filename1, filename2))
+		if (!tomoyo_io_printf(head, "allow_%s ",
+				      tomoyo_path22keyword(bit)) ||
+		    !tomoyo_print_name_union(head, &ptr->name1) ||
+		    !tomoyo_print_name_union(head, &ptr->name2) ||
+		    !tomoyo_io_printf(head, "\n"))
 			goto out;
 	}
 	head->read_bit = 0;
@@ -1682,6 +1748,8 @@
 		return tomoyo_write_pattern_policy(data, is_delete);
 	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
 		return tomoyo_write_no_rewrite_policy(data, is_delete);
+	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP))
+		return tomoyo_write_path_group_policy(data, is_delete);
 	return -EINVAL;
 }
 
@@ -1738,6 +1806,12 @@
 			head->read_var2 = NULL;
 			head->read_step = 9;
 		case 9:
+			if (!tomoyo_read_path_group_policy(head))
+				break;
+			head->read_var1 = NULL;
+			head->read_var2 = NULL;
+			head->read_step = 10;
+		case 10:
 			head->read_eof = true;
 			break;
 		default: