TOMOYO: Support longer pathname.

Allow pathnames longer than 4000 bytes.

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/realpath.c b/security/tomoyo/realpath.c
index 1fd685a..153fa23 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -12,115 +12,60 @@
 #include <linux/fs_struct.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
+#include <net/sock.h>
 #include "common.h"
 
 /**
  * tomoyo_encode: Convert binary string to ascii string.
  *
- * @buffer:  Buffer for ASCII string.
- * @buflen:  Size of @buffer.
- * @str:     Binary string.
+ * @str: String in binary format.
  *
- * Returns 0 on success, -ENOMEM otherwise.
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
  */
-int tomoyo_encode(char *buffer, int buflen, const char *str)
+char *tomoyo_encode(const char *str)
 {
-	while (1) {
-		const unsigned char c = *(unsigned char *) str++;
+	int len = 0;
+	const char *p = str;
+	char *cp;
+	char *cp0;
 
-		if (tomoyo_is_valid(c)) {
-			if (--buflen <= 0)
-				break;
-			*buffer++ = (char) c;
-			if (c != '\\')
-				continue;
-			if (--buflen <= 0)
-				break;
-			*buffer++ = (char) c;
-			continue;
-		}
-		if (!c) {
-			if (--buflen <= 0)
-				break;
-			*buffer = '\0';
-			return 0;
-		}
-		buflen -= 4;
-		if (buflen <= 0)
-			break;
-		*buffer++ = '\\';
-		*buffer++ = (c >> 6) + '0';
-		*buffer++ = ((c >> 3) & 7) + '0';
-		*buffer++ = (c & 7) + '0';
+	if (!p)
+		return NULL;
+	while (*p) {
+		const unsigned char c = *p++;
+		if (c == '\\')
+			len += 2;
+		else if (c > ' ' && c < 127)
+			len++;
+		else
+			len += 4;
 	}
-	return -ENOMEM;
-}
+	len++;
+	/* Reserve space for appending "/". */
+	cp = kzalloc(len + 10, GFP_NOFS);
+	if (!cp)
+		return NULL;
+	cp0 = cp;
+	p = str;
+	while (*p) {
+		const unsigned char c = *p++;
 
-/**
- * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
- *
- * @path:        Pointer to "struct path".
- * @newname:     Pointer to buffer to return value in.
- * @newname_len: Size of @newname.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * If dentry is a directory, trailing '/' is appended.
- * Characters out of 0x20 < c < 0x7F range are converted to
- * \ooo style octal string.
- * Character \ is converted to \\ string.
- */
-int tomoyo_realpath_from_path2(struct path *path, char *newname,
-			       int newname_len)
-{
-	int error = -ENOMEM;
-	struct dentry *dentry = path->dentry;
-	char *sp;
-
-	if (!dentry || !path->mnt || !newname || newname_len <= 2048)
-		return -EINVAL;
-	if (dentry->d_op && dentry->d_op->d_dname) {
-		/* For "socket:[\$]" and "pipe:[\$]". */
-		static const int offset = 1536;
-		sp = dentry->d_op->d_dname(dentry, newname + offset,
-					   newname_len - offset);
-	} else {
-		struct path ns_root = {.mnt = NULL, .dentry = NULL};
-
-		spin_lock(&dcache_lock);
-		/* go to whatever namespace root we are under */
-		sp = __d_path(path, &ns_root, newname, newname_len);
-		spin_unlock(&dcache_lock);
-		/* Prepend "/proc" prefix if using internal proc vfs mount. */
-		if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
-		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
-			sp -= 5;
-			if (sp >= newname)
-				memcpy(sp, "/proc", 5);
-			else
-				sp = ERR_PTR(-ENOMEM);
+		if (c == '\\') {
+			*cp++ = '\\';
+			*cp++ = '\\';
+		} else if (c > ' ' && c < 127) {
+			*cp++ = c;
+		} else {
+			*cp++ = '\\';
+			*cp++ = (c >> 6) + '0';
+			*cp++ = ((c >> 3) & 7) + '0';
+			*cp++ = (c & 7) + '0';
 		}
 	}
-	if (IS_ERR(sp))
-		error = PTR_ERR(sp);
-	else
-		error = tomoyo_encode(newname, sp - newname, sp);
-	/* Append trailing '/' if dentry is a directory. */
-	if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)
-	    && *newname) {
-		sp = newname + strlen(newname);
-		if (*(sp - 1) != '/') {
-			if (sp < newname + newname_len - 4) {
-				*sp++ = '/';
-				*sp = '\0';
-			} else {
-				error = -ENOMEM;
-			}
-		}
-	}
-	if (error)
-		tomoyo_warn_oom(__func__);
-	return error;
+	return cp0;
 }
 
 /**
@@ -130,23 +75,90 @@
  *
  * Returns the realpath of the given @path on success, NULL otherwise.
  *
+ * If dentry is a directory, trailing '/' is appended.
+ * Characters out of 0x20 < c < 0x7F range are converted to
+ * \ooo style octal string.
+ * Character \ is converted to \\ string.
+ *
  * These functions use kzalloc(), so the caller must call kfree()
  * if these functions didn't return NULL.
  */
 char *tomoyo_realpath_from_path(struct path *path)
 {
-	char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS);
-
-	BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
-	BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
-		     <= TOMOYO_MAX_PATHNAME_LEN - 1);
-	if (!buf)
+	char *buf = NULL;
+	char *name = NULL;
+	unsigned int buf_len = PAGE_SIZE / 2;
+	struct dentry *dentry = path->dentry;
+	bool is_dir;
+	if (!dentry)
 		return NULL;
-	if (tomoyo_realpath_from_path2(path, buf,
-				       TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
-		return buf;
+	is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode);
+	while (1) {
+		struct path ns_root = { .mnt = NULL, .dentry = NULL };
+		char *pos;
+		buf_len <<= 1;
+		kfree(buf);
+		buf = kmalloc(buf_len, GFP_NOFS);
+		if (!buf)
+			break;
+		/* Get better name for socket. */
+		if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {
+			struct inode *inode = dentry->d_inode;
+			struct socket *sock = inode ? SOCKET_I(inode) : NULL;
+			struct sock *sk = sock ? sock->sk : NULL;
+			if (sk) {
+				snprintf(buf, buf_len - 1, "socket:[family=%u:"
+					 "type=%u:protocol=%u]", sk->sk_family,
+					 sk->sk_type, sk->sk_protocol);
+			} else {
+				snprintf(buf, buf_len - 1, "socket:[unknown]");
+			}
+			name = tomoyo_encode(buf);
+			break;
+		}
+		/* For "socket:[\$]" and "pipe:[\$]". */
+		if (dentry->d_op && dentry->d_op->d_dname) {
+			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
+			if (IS_ERR(pos))
+				continue;
+			name = tomoyo_encode(pos);
+			break;
+		}
+		/* If we don't have a vfsmount, we can't calculate. */
+		if (!path->mnt)
+			break;
+		spin_lock(&dcache_lock);
+		/* go to whatever namespace root we are under */
+		pos = __d_path(path, &ns_root, buf, buf_len);
+		spin_unlock(&dcache_lock);
+		/* Prepend "/proc" prefix if using internal proc vfs mount. */
+		if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
+		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
+			pos -= 5;
+			if (pos >= buf)
+				memcpy(pos, "/proc", 5);
+			else
+				pos = ERR_PTR(-ENOMEM);
+		}
+		if (IS_ERR(pos))
+			continue;
+		name = tomoyo_encode(pos);
+		break;
+	}
 	kfree(buf);
-	return NULL;
+	if (!name)
+		tomoyo_warn_oom(__func__);
+	else if (is_dir && *name) {
+		/* Append trailing '/' if dentry is a directory. */
+		char *pos = name + strlen(name) - 1;
+		if (*pos != '/')
+			/*
+			 * This is OK because tomoyo_encode() reserves space
+			 * for appending "/".
+			 */
+			*++pos = '/';
+	}
+	return name;
 }
 
 /**