text: /*
   * security/tomoyo/realpath.c
   *
   * Pathname calculation functions for TOMOYO.
   *
   * Copyright (C) 2005-2010  NTT DATA CORPORATION
   */
  
  #include <linux/types.h>
  #include <linux/mount.h>
  #include <linux/mnt_namespace.h>
  #include <linux/fs_struct.h>
  #include <linux/magic.h>
  #include <linux/slab.h>
  #include <net/sock.h>
  #include "common.h"
  #include "../../fs/internal.h"
  
  /**
   * tomoyo_encode: Convert binary string to ascii string.
   *
   * @str: String in binary format.
   *
   * 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.
   */
  char *tomoyo_encode(const char *str)
  {
  	int len = 0;
  	const char *p = str;
  	char *cp;
  	char *cp0;
  
  	if (!p)
  		return NULL;
  	while (*p) {
  		const unsigned char c = *p++;
  		if (c == '\\')
  			len += 2;
  		else if (c > ' ' && c < 127)
  			len++;
  		else
  			len += 4;
  	}
  	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++;
  
  		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';
  		}
  	}
  	return cp0;
  }
  
  /**
   * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
   *
   * @path: Pointer to "struct path".
   *
   * 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 = NULL;
  	char *name = NULL;
  	unsigned int buf_len = PAGE_SIZE / 2;
  	struct dentry *dentry = path->dentry;
  	bool is_dir;
  	if (!dentry)
  		return NULL;
  	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;
  		/* go to whatever namespace root we are under */
  		pos = __d_path(path, &ns_root, buf, buf_len);
  		/* 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);
  	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;
  }
  
  /**
   * tomoyo_realpath_nofollow - Get realpath of a pathname.
   *
   * @pathname: The pathname to solve.
   *
   * Returns the realpath of @pathname on success, NULL otherwise.
   */
  char *tomoyo_realpath_nofollow(const char *pathname)
  {
  	struct path path;
  
  	if (pathname && kern_path(pathname, 0, &path) == 0) {
  		char *buf = tomoyo_realpath_from_path(&path);
  		path_put(&path);
  		return buf;
  	}
  	return NULL;
  }
  