linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 00/10] vfs: getname/putname overhaul
@ 2012-09-07 13:37 Jeff Layton
  2012-09-07 13:37 ` [PATCH v1 01/10] vfs: allocate page instead of names_cache buffer in mount_block_root Jeff Layton
                   ` (10 more replies)
  0 siblings, 11 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:37 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

This patchset is a first pass at overhauling the getname/putname
interface to use a struct. The idea here is to add a new getname_info
struct that allow us to pass around some auxillary info along with
the string that getname() returns.

This allows us to do some interesting things:

- no need to walk the list of audit_names in certain cases since we
  can store a pointer to the correct audit_name

- we can now call getname() more than once on a userland string. Since
  we track the original userland pointer, we can avoid doing a second
  allocation, and can instead fill out the getname_info from the
  audit_names struct. That makes the ESTALE patchset cleaner, and doesn't
  explode out the list of getname() callers like the last set.

- eventually we might be able to track the length of the parent portion
  of the string so the audit code doesn't need to walk it again. I
  haven't implemented that yet, but it doesn't look too hard to do.

This is based on top of Al's signal.git#execve2 branch, with my most
recent audit series on top of that. Al is working on unifying much of
the execve code, which will reduce the number of getname callers greatly.

This set is still preliminary since Al's set isn't complete yet and will
probably need to be respun again once he's completed that work. That
should shrink patch #4 since we'll have fewer getname callers to deal
with at that point.

This set is based on top of my audit overhaul patchset (posted earlier
today). I'll also be posting a respun version of my ESTALE retry
patchset soon that's based on top of this one.

While this all seems to work correctly, I have my doubts about patch #9
in this series. That was suggested by Al and should make it so that we
only need a single allocation per getname() call in most cases. OTOH, it
adds a rarely traveled codepath that could be a source of bugs in the
future.

Jeff Layton (10):
  vfs: allocate page instead of names_cache buffer in mount_block_root
  vfs: make dir_name arg to do_mount a const char *
  acct: constify the name arg to acct_on
  vfs: define getname_info struct and have getname() return it
  audit: allow audit code to satisfy getname requests from its
    names_list
  vfs: turn do_path_lookup into wrapper around getname_info variant
  vfs: make path_openat take a getname_info pointer
  audit: make audit_inode take getname_info
  vfs: embed getname_info inside of names_cache allocation if possible
  vfs: unexport getname and putname symbols

 arch/alpha/kernel/osf_sys.c             |  16 +--
 arch/avr32/kernel/process.c             |   4 +-
 arch/blackfin/kernel/process.c          |   4 +-
 arch/c6x/kernel/process.c               |   4 +-
 arch/cris/arch-v10/kernel/process.c     |   4 +-
 arch/cris/arch-v32/kernel/process.c     |   4 +-
 arch/frv/kernel/process.c               |   4 +-
 arch/h8300/kernel/process.c             |   4 +-
 arch/hexagon/kernel/syscall.c           |   4 +-
 arch/ia64/kernel/process.c              |   4 +-
 arch/m32r/kernel/process.c              |   4 +-
 arch/m68k/kernel/process.c              |   4 +-
 arch/microblaze/kernel/sys_microblaze.c |   4 +-
 arch/mips/kernel/linux32.c              |   4 +-
 arch/mips/kernel/syscall.c              |   4 +-
 arch/mn10300/kernel/process.c           |   4 +-
 arch/openrisc/kernel/process.c          |   4 +-
 arch/parisc/hpux/fs.c                   |   4 +-
 arch/parisc/kernel/process.c            |   4 +-
 arch/parisc/kernel/sys_parisc32.c       |   4 +-
 arch/score/kernel/sys_score.c           |   4 +-
 arch/sh/kernel/process_32.c             |   4 +-
 arch/sh/kernel/process_64.c             |   4 +-
 arch/sparc/kernel/process_32.c          |   4 +-
 arch/sparc/kernel/process_64.c          |   4 +-
 arch/sparc/kernel/sys_sparc32.c         |   4 +-
 arch/tile/kernel/process.c              |   8 +-
 arch/unicore32/kernel/sys.c             |   4 +-
 arch/xtensa/kernel/process.c            |   4 +-
 fs/compat.c                             |  12 +-
 fs/exec.c                               |  13 +-
 fs/filesystems.c                        |   4 +-
 fs/internal.h                           |   4 +-
 fs/namei.c                              | 214 +++++++++++++++++++++-----------
 fs/namespace.c                          |   6 +-
 fs/open.c                               |  33 ++++-
 fs/quota/quota.c                        |   4 +-
 include/linux/audit.h                   |  26 ++--
 include/linux/fs.h                      |  23 +++-
 init/do_mounts.c                        |   7 +-
 ipc/mqueue.c                            |   9 +-
 kernel/acct.c                           |   6 +-
 kernel/auditsc.c                        | 124 +++++++++++-------
 mm/swapfile.c                           |  11 +-
 44 files changed, 392 insertions(+), 236 deletions(-)

-- 
1.7.11.4


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v1 01/10] vfs: allocate page instead of names_cache buffer in mount_block_root
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
@ 2012-09-07 13:37 ` Jeff Layton
  2012-09-07 13:37 ` [PATCH v1 02/10] vfs: make dir_name arg to do_mount a const char * Jeff Layton
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:37 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

First, it's incorrect to call putname() after __getname_gfp() since the
bare __getname_gfp() call skips the auditing code, while putname()
doesn't.

mount_block_root allocates a PATH_MAX buffer via __getname_gfp, and then
calls get_fs_names to fill the buffer. That function can call
get_filesystem_list which assumes that that buffer is a full page in
size. On arches where PAGE_SIZE != 4k, then this could potentially
overrun.

In practice, it's hard to imagine the list of filesystem names even
approaching 4k, but it's best to be safe. Just allocate a page for this
purpose instead.

With this, we can also remove the __getname_gfp() definition since there
are no more callers.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/fs.h | 3 +--
 init/do_mounts.c   | 7 ++++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index aa11047..69513a7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2221,8 +2221,7 @@ extern void __init vfs_caches_init(unsigned long);
 
 extern struct kmem_cache *names_cachep;
 
-#define __getname_gfp(gfp)	kmem_cache_alloc(names_cachep, (gfp))
-#define __getname()		__getname_gfp(GFP_KERNEL)
+#define __getname()		kmem_cache_alloc(names_cachep, GFP_KERNEL)
 #define __putname(name)		kmem_cache_free(names_cachep, (void *)(name))
 #ifndef CONFIG_AUDITSYSCALL
 #define putname(name)   __putname(name)
diff --git a/init/do_mounts.c b/init/do_mounts.c
index d3f0aee..f8a6642 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -353,8 +353,9 @@ static int __init do_mount_root(char *name, char *fs, int flags, void *data)
 
 void __init mount_block_root(char *name, int flags)
 {
-	char *fs_names = __getname_gfp(GFP_KERNEL
-		| __GFP_NOTRACK_FALSE_POSITIVE);
+	struct page *page = alloc_page(GFP_KERNEL |
+					__GFP_NOTRACK_FALSE_POSITIVE);
+	char *fs_names = page_address(page);
 	char *p;
 #ifdef CONFIG_BLOCK
 	char b[BDEVNAME_SIZE];
@@ -406,7 +407,7 @@ retry:
 #endif
 	panic("VFS: Unable to mount root fs on %s", b);
 out:
-	putname(fs_names);
+	put_page(page);
 }
  
 #ifdef CONFIG_ROOT_NFS
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 02/10] vfs: make dir_name arg to do_mount a const char *
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
  2012-09-07 13:37 ` [PATCH v1 01/10] vfs: allocate page instead of names_cache buffer in mount_block_root Jeff Layton
@ 2012-09-07 13:37 ` Jeff Layton
  2012-09-07 13:37 ` [PATCH v1 03/10] acct: constify the name arg to acct_on Jeff Layton
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:37 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

Nothing changes it, so it should be safe to do.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/namespace.c     | 2 +-
 include/linux/fs.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 4d31f73..8e52b1c 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2185,7 +2185,7 @@ int copy_mount_string(const void __user *data, char **where)
  * Therefore, if this magic number is present, it carries no information
  * and must be discarded.
  */
-long do_mount(char *dev_name, char *dir_name, char *type_page,
+long do_mount(char *dev_name, const char *dir_name, char *type_page,
 		  unsigned long flags, void *data_page)
 {
 	struct path path;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 69513a7..b6ce6fc 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2072,7 +2072,7 @@ extern struct vfsmount *kern_mount_data(struct file_system_type *, void *data);
 extern void kern_unmount(struct vfsmount *mnt);
 extern int may_umount_tree(struct vfsmount *);
 extern int may_umount(struct vfsmount *);
-extern long do_mount(char *, char *, char *, unsigned long, void *);
+extern long do_mount(char *, const char *, char *, unsigned long, void *);
 extern struct vfsmount *collect_mounts(struct path *);
 extern void drop_collected_mounts(struct vfsmount *);
 extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 03/10] acct: constify the name arg to acct_on
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
  2012-09-07 13:37 ` [PATCH v1 01/10] vfs: allocate page instead of names_cache buffer in mount_block_root Jeff Layton
  2012-09-07 13:37 ` [PATCH v1 02/10] vfs: make dir_name arg to do_mount a const char * Jeff Layton
@ 2012-09-07 13:37 ` Jeff Layton
  2012-09-07 13:37 ` [PATCH v1 04/10] vfs: define getname_info struct and have getname() return it Jeff Layton
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:37 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 kernel/acct.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/acct.c b/kernel/acct.c
index 02e6167..e97d159 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -193,7 +193,7 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file,
 	}
 }
 
-static int acct_on(char *name)
+static int acct_on(const char *name)
 {
 	struct file *file;
 	struct vfsmount *mnt;
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 04/10] vfs: define getname_info struct and have getname() return it
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (2 preceding siblings ...)
  2012-09-07 13:37 ` [PATCH v1 03/10] acct: constify the name arg to acct_on Jeff Layton
@ 2012-09-07 13:37 ` Jeff Layton
  2012-09-07 13:38 ` [PATCH v1 05/10] audit: allow audit code to satisfy getname requests from its names_list Jeff Layton
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:37 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

---
 arch/alpha/kernel/osf_sys.c             |  16 ++---
 arch/avr32/kernel/process.c             |   4 +-
 arch/blackfin/kernel/process.c          |   4 +-
 arch/c6x/kernel/process.c               |   4 +-
 arch/cris/arch-v10/kernel/process.c     |   4 +-
 arch/cris/arch-v32/kernel/process.c     |   4 +-
 arch/frv/kernel/process.c               |   4 +-
 arch/h8300/kernel/process.c             |   4 +-
 arch/hexagon/kernel/syscall.c           |   4 +-
 arch/ia64/kernel/process.c              |   4 +-
 arch/m32r/kernel/process.c              |   4 +-
 arch/m68k/kernel/process.c              |   4 +-
 arch/microblaze/kernel/sys_microblaze.c |   4 +-
 arch/mips/kernel/linux32.c              |   4 +-
 arch/mips/kernel/syscall.c              |   4 +-
 arch/mn10300/kernel/process.c           |   4 +-
 arch/openrisc/kernel/process.c          |   4 +-
 arch/parisc/hpux/fs.c                   |   4 +-
 arch/parisc/kernel/process.c            |   4 +-
 arch/parisc/kernel/sys_parisc32.c       |   4 +-
 arch/score/kernel/sys_score.c           |   4 +-
 arch/sh/kernel/process_32.c             |   4 +-
 arch/sh/kernel/process_64.c             |   4 +-
 arch/sparc/kernel/process_32.c          |   4 +-
 arch/sparc/kernel/process_64.c          |   4 +-
 arch/sparc/kernel/sys_sparc32.c         |   4 +-
 arch/tile/kernel/process.c              |   8 +--
 arch/unicore32/kernel/sys.c             |   4 +-
 arch/xtensa/kernel/process.c            |   4 +-
 fs/compat.c                             |  12 ++--
 fs/exec.c                               |  12 ++--
 fs/filesystems.c                        |   4 +-
 fs/namei.c                              | 113 +++++++++++++++++++-------------
 fs/namespace.c                          |   4 +-
 fs/open.c                               |   4 +-
 fs/quota/quota.c                        |   4 +-
 include/linux/audit.h                   |   8 ++-
 include/linux/fs.h                      |  14 +++-
 ipc/mqueue.c                            |  13 ++--
 kernel/acct.c                           |   4 +-
 kernel/auditsc.c                        |  78 +++++++++++-----------
 mm/swapfile.c                           |  11 ++--
 42 files changed, 224 insertions(+), 189 deletions(-)

diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index bc1acdd..b3f41e8 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -452,7 +452,7 @@ osf_ufs_mount(char *dirname, struct ufs_args __user *args, int flags)
 {
 	int retval;
 	struct cdfs_args tmp;
-	char *devname;
+	struct getname_info *devname;
 
 	retval = -EFAULT;
 	if (copy_from_user(&tmp, args, sizeof(tmp)))
@@ -461,7 +461,7 @@ osf_ufs_mount(char *dirname, struct ufs_args __user *args, int flags)
 	retval = PTR_ERR(devname);
 	if (IS_ERR(devname))
 		goto out;
-	retval = do_mount(devname, dirname, "ext2", flags, NULL);
+	retval = do_mount(devname->name, dirname, "ext2", flags, NULL);
 	putname(devname);
  out:
 	return retval;
@@ -472,7 +472,7 @@ osf_cdfs_mount(char *dirname, struct cdfs_args __user *args, int flags)
 {
 	int retval;
 	struct cdfs_args tmp;
-	char *devname;
+	struct getname_info *devname;
 
 	retval = -EFAULT;
 	if (copy_from_user(&tmp, args, sizeof(tmp)))
@@ -481,7 +481,7 @@ osf_cdfs_mount(char *dirname, struct cdfs_args __user *args, int flags)
 	retval = PTR_ERR(devname);
 	if (IS_ERR(devname))
 		goto out;
-	retval = do_mount(devname, dirname, "iso9660", flags, NULL);
+	retval = do_mount(devname->name, dirname, "iso9660", flags, NULL);
 	putname(devname);
  out:
 	return retval;
@@ -502,7 +502,7 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path,
 		int, flag, void __user *, data)
 {
 	int retval;
-	char *name;
+	struct getname_info *name;
 
 	name = getname(path);
 	retval = PTR_ERR(name);
@@ -510,13 +510,13 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path,
 		goto out;
 	switch (typenr) {
 	case 1:
-		retval = osf_ufs_mount(name, data, flag);
+		retval = osf_ufs_mount(name->name, data, flag);
 		break;
 	case 6:
-		retval = osf_cdfs_mount(name, data, flag);
+		retval = osf_cdfs_mount(name->name, data, flag);
 		break;
 	case 9:
-		retval = osf_procfs_mount(name, data, flag);
+		retval = osf_procfs_mount(name->name, data, flag);
 		break;
 	default:
 		retval = -EINVAL;
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 92c5af9..c2034e8 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -388,14 +388,14 @@ asmlinkage int sys_execve(const char __user *ufilename,
 			  struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(ufilename);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
 
-	error = do_execve(filename, uargv, uenvp, regs);
+	error = do_execve(filename->name, uargv, uenvp, regs);
 	putname(filename);
 
 out:
diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c
index 62bcea7..bc5c162 100644
--- a/arch/blackfin/kernel/process.c
+++ b/arch/blackfin/kernel/process.c
@@ -213,14 +213,14 @@ asmlinkage int sys_execve(const char __user *name,
 			  const char __user *const __user *envp)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 	struct pt_regs *regs = (struct pt_regs *)((&name) + 6);
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		return error;
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 	return error;
 }
diff --git a/arch/c6x/kernel/process.c b/arch/c6x/kernel/process.c
index 45e924a..b2e8128 100644
--- a/arch/c6x/kernel/process.c
+++ b/arch/c6x/kernel/process.c
@@ -230,14 +230,14 @@ SYSCALL_DEFINE4(c6x_execve, const char __user *, name,
 		struct pt_regs *, regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
 
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 out:
 	return error;
diff --git a/arch/cris/arch-v10/kernel/process.c b/arch/cris/arch-v10/kernel/process.c
index bee8df4..92b57e7 100644
--- a/arch/cris/arch-v10/kernel/process.c
+++ b/arch/cris/arch-v10/kernel/process.c
@@ -212,14 +212,14 @@ asmlinkage int sys_execve(const char *fname,
 			  struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(fname);
 	error = PTR_ERR(filename);
 
 	if (IS_ERR(filename))
 	        goto out;
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
  out:
 	return error;
diff --git a/arch/cris/arch-v32/kernel/process.c b/arch/cris/arch-v32/kernel/process.c
index 0570e8c..a80be6f 100644
--- a/arch/cris/arch-v32/kernel/process.c
+++ b/arch/cris/arch-v32/kernel/process.c
@@ -224,7 +224,7 @@ sys_execve(const char *fname,
 	   struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(fname);
 	error = PTR_ERR(filename);
@@ -232,7 +232,7 @@ sys_execve(const char *fname,
 	if (IS_ERR(filename))
 	        goto out;
 
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
  out:
 	return error;
diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c
index ff95f50..a5ca216 100644
--- a/arch/frv/kernel/process.c
+++ b/arch/frv/kernel/process.c
@@ -223,13 +223,13 @@ asmlinkage int sys_execve(const char __user *name,
 			  const char __user *const __user *envp)
 {
 	int error;
-	char * filename;
+	struct getname_info *filename;
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		return error;
-	error = do_execve(filename, argv, envp, __frame);
+	error = do_execve(filename->name, argv, envp, __frame);
 	putname(filename);
 	return error;
 }
diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c
index 0e9c315..67d357d 100644
--- a/arch/h8300/kernel/process.c
+++ b/arch/h8300/kernel/process.c
@@ -214,14 +214,14 @@ asmlinkage int sys_execve(const char *name,
 			  int dummy, ...)
 {
 	int error;
-	char * filename;
+	struct getname_info *filename;
 	struct pt_regs *regs = (struct pt_regs *) ((unsigned char *)&dummy-4);
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		return error;
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 	return error;
 }
diff --git a/arch/hexagon/kernel/syscall.c b/arch/hexagon/kernel/syscall.c
index 553cd60..f67b4d5 100644
--- a/arch/hexagon/kernel/syscall.c
+++ b/arch/hexagon/kernel/syscall.c
@@ -40,7 +40,7 @@ asmlinkage int sys_execve(char __user *ufilename,
 			  const char __user *const __user *envp)
 {
 	struct pt_regs *pregs = current_thread_info()->regs;
-	char *filename;
+	struct getname_info *filename;
 	int retval;
 
 	filename = getname(ufilename);
@@ -48,7 +48,7 @@ asmlinkage int sys_execve(char __user *ufilename,
 	if (IS_ERR(filename))
 		return retval;
 
-	retval = do_execve(filename, argv, envp, pregs);
+	retval = do_execve(filename->name, argv, envp, pregs);
 	putname(filename);
 
 	return retval;
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index dd6fc14..d2cf668 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -611,14 +611,14 @@ sys_execve (const char __user *filename,
 	    const char __user *const __user *envp,
 	    struct pt_regs *regs)
 {
-	char *fname;
+	struct getname_info *fname;
 	int error;
 
 	fname = getname(filename);
 	error = PTR_ERR(fname);
 	if (IS_ERR(fname))
 		goto out;
-	error = do_execve(fname, argv, envp, regs);
+	error = do_execve(fname->name, argv, envp, regs);
 	putname(fname);
 out:
 	return error;
diff --git a/arch/m32r/kernel/process.c b/arch/m32r/kernel/process.c
index 3a4a32b..e1ea185 100644
--- a/arch/m32r/kernel/process.c
+++ b/arch/m32r/kernel/process.c
@@ -293,14 +293,14 @@ asmlinkage int sys_execve(const char __user *ufilename,
 			  unsigned long r6, struct pt_regs regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(ufilename);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
 
-	error = do_execve(filename, uargv, uenvp, &regs);
+	error = do_execve(filename->name, uargv, uenvp, &regs);
 	putname(filename);
 out:
 	return error;
diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
index c488e3c..29a486d 100644
--- a/arch/m68k/kernel/process.c
+++ b/arch/m68k/kernel/process.c
@@ -342,14 +342,14 @@ asmlinkage int sys_execve(const char __user *name,
 			  const char __user *const __user *envp)
 {
 	int error;
-	char * filename;
+	struct getname_info *filename;
 	struct pt_regs *regs = (struct pt_regs *) &name;
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		return error;
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 	return error;
 }
diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c
index e5b154f..f273d16 100644
--- a/arch/microblaze/kernel/sys_microblaze.c
+++ b/arch/microblaze/kernel/sys_microblaze.c
@@ -54,13 +54,13 @@ asmlinkage long microblaze_execve(const char __user *filenamei,
 				  struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(filenamei);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 out:
 	return error;
diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c
index 922a554..62ff5a9 100644
--- a/arch/mips/kernel/linux32.c
+++ b/arch/mips/kernel/linux32.c
@@ -83,13 +83,13 @@ out:
 asmlinkage int sys32_execve(nabi_no_regargs struct pt_regs regs)
 {
 	int error;
-	char * filename;
+	struct getname_info *filename;
 
 	filename = getname(compat_ptr(regs.regs[4]));
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = compat_do_execve(filename, compat_ptr(regs.regs[5]),
+	error = compat_do_execve(filename->name, compat_ptr(regs.regs[5]),
 				 compat_ptr(regs.regs[6]), &regs);
 	putname(filename);
 
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
index b08220c..679c417 100644
--- a/arch/mips/kernel/syscall.c
+++ b/arch/mips/kernel/syscall.c
@@ -133,13 +133,13 @@ _sys_clone(nabi_no_regargs struct pt_regs regs)
 asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs)
 {
 	int error;
-	char * filename;
+	struct getname_info *filename;
 
 	filename = getname((const char __user *) (long)regs.regs[4]);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = do_execve(filename,
+	error = do_execve(filename->name,
 			  (const char __user *const __user *) (long)regs.regs[5],
 	                  (const char __user *const __user *) (long)regs.regs[6],
 			  &regs);
diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c
index 7dab0cd..9375d34 100644
--- a/arch/mn10300/kernel/process.c
+++ b/arch/mn10300/kernel/process.c
@@ -303,14 +303,14 @@ asmlinkage long sys_execve(const char __user *name,
 			   const char __user *const __user *argv,
 			   const char __user *const __user *envp)
 {
-	char *filename;
+	struct getname_info *filename;
 	int error;
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		return error;
-	error = do_execve(filename, argv, envp, current_frame());
+	error = do_execve(filename->name, argv, envp, current_frame());
 	putname(filename);
 	return error;
 }
diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c
index 55210f3..9edec79 100644
--- a/arch/openrisc/kernel/process.c
+++ b/arch/openrisc/kernel/process.c
@@ -271,7 +271,7 @@ asmlinkage long _sys_execve(const char __user *name,
 			    struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
@@ -279,7 +279,7 @@ asmlinkage long _sys_execve(const char __user *name,
 	if (IS_ERR(filename))
 		goto out;
 
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 
 out:
diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c
index c71eb6c..a40d81f 100644
--- a/arch/parisc/hpux/fs.c
+++ b/arch/parisc/hpux/fs.c
@@ -34,14 +34,14 @@
 int hpux_execve(struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname((const char __user *) regs->gr[26]);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
 
-	error = do_execve(filename,
+	error = do_execve(filename->name,
 			  (const char __user *const __user *) regs->gr[25],
 			  (const char __user *const __user *) regs->gr[24],
 			  regs);
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index d4b94b3..c855774 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -339,13 +339,13 @@ unsigned long thread_saved_pc(struct task_struct *t)
 asmlinkage int sys_execve(struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname((const char __user *) regs->gr[26]);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = do_execve(filename,
+	error = do_execve(filename->name,
 			  (const char __user *const __user *) regs->gr[25],
 			  (const char __user *const __user *) regs->gr[24],
 			  regs);
diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c
index dc9a624..7e17607 100644
--- a/arch/parisc/kernel/sys_parisc32.c
+++ b/arch/parisc/kernel/sys_parisc32.c
@@ -60,14 +60,14 @@
 asmlinkage int sys32_execve(struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	DBG(("sys32_execve(%p) r26 = 0x%lx\n", regs, regs->gr[26]));
 	filename = getname((const char __user *) regs->gr[26]);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = compat_do_execve(filename, compat_ptr(regs->gr[25]),
+	error = compat_do_execve(filename->name, compat_ptr(regs->gr[25]),
 				 compat_ptr(regs->gr[24]), regs);
 	putname(filename);
 out:
diff --git a/arch/score/kernel/sys_score.c b/arch/score/kernel/sys_score.c
index e478bf9..9ee9ac9 100644
--- a/arch/score/kernel/sys_score.c
+++ b/arch/score/kernel/sys_score.c
@@ -92,14 +92,14 @@ asmlinkage long
 score_execve(struct pt_regs *regs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname((char __user*)regs->regs[4]);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		return error;
 
-	error = do_execve(filename,
+	error = do_execve(filename->name,
 			  (const char __user *const __user *)regs->regs[5],
 			  (const char __user *const __user *)regs->regs[6],
 			  regs);
diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c
index 59521e8..93d4879 100644
--- a/arch/sh/kernel/process_32.c
+++ b/arch/sh/kernel/process_32.c
@@ -298,14 +298,14 @@ asmlinkage int sys_execve(const char __user *ufilename,
 {
 	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(ufilename);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
 
-	error = do_execve(filename, uargv, uenvp, regs);
+	error = do_execve(filename->name, uargv, uenvp, regs);
 	putname(filename);
 out:
 	return error;
diff --git a/arch/sh/kernel/process_64.c b/arch/sh/kernel/process_64.c
index 602545b..bad7c30 100644
--- a/arch/sh/kernel/process_64.c
+++ b/arch/sh/kernel/process_64.c
@@ -491,14 +491,14 @@ asmlinkage int sys_execve(const char *ufilename, char **uargv,
 			  struct pt_regs *pregs)
 {
 	int error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname((char __user *)ufilename);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
 
-	error = do_execve(filename,
+	error = do_execve(filename->name,
 			  (const char __user *const __user *)uargv,
 			  (const char __user *const __user *)uenvp,
 			  pregs);
diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
index 14006d8..8de0cfc 100644
--- a/arch/sparc/kernel/process_32.c
+++ b/arch/sparc/kernel/process_32.c
@@ -482,7 +482,7 @@ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs)
 asmlinkage int sparc_execve(struct pt_regs *regs)
 {
 	int error, base = 0;
-	char *filename;
+	struct getname_info *filename;
 
 	/* Check for indirect call. */
 	if(regs->u_regs[UREG_G1] == 0)
@@ -492,7 +492,7 @@ asmlinkage int sparc_execve(struct pt_regs *regs)
 	error = PTR_ERR(filename);
 	if(IS_ERR(filename))
 		goto out;
-	error = do_execve(filename,
+	error = do_execve(filename->name,
 			  (const char __user *const  __user *)
 			  regs->u_regs[base + UREG_I1],
 			  (const char __user *const  __user *)
diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
index aff0c72..9769ec4 100644
--- a/arch/sparc/kernel/process_64.c
+++ b/arch/sparc/kernel/process_64.c
@@ -722,7 +722,7 @@ EXPORT_SYMBOL(dump_fpu);
 asmlinkage int sparc_execve(struct pt_regs *regs)
 {
 	int error, base = 0;
-	char *filename;
+	struct getname_info *filename;
 
 	/* User register window flush is done by entry.S */
 
@@ -734,7 +734,7 @@ asmlinkage int sparc_execve(struct pt_regs *regs)
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = do_execve(filename,
+	error = do_execve(filename->name,
 			  (const char __user *const __user *)
 			  regs->u_regs[base + UREG_I1],
 			  (const char __user *const __user *)
diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
index f739233..1213d7d 100644
--- a/arch/sparc/kernel/sys_sparc32.c
+++ b/arch/sparc/kernel/sys_sparc32.c
@@ -403,7 +403,7 @@ asmlinkage long compat_sys_rt_sigaction(int sig,
 asmlinkage long sparc32_execve(struct pt_regs *regs)
 {
 	int error, base = 0;
-	char *filename;
+	struct getname_info *filename;
 
 	/* User register window flush is done by entry.S */
 
@@ -416,7 +416,7 @@ asmlinkage long sparc32_execve(struct pt_regs *regs)
 	if (IS_ERR(filename))
 		goto out;
 
-	error = compat_do_execve(filename,
+	error = compat_do_execve(filename->name,
 				 compat_ptr(regs->u_regs[base + UREG_I1]),
 				 compat_ptr(regs->u_regs[base + UREG_I2]), regs);
 
diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c
index 6be7991..04b0e62 100644
--- a/arch/tile/kernel/process.c
+++ b/arch/tile/kernel/process.c
@@ -594,13 +594,13 @@ SYSCALL_DEFINE4(execve, const char __user *, path,
 		struct pt_regs *, regs)
 {
 	long error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(path);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 	if (error == 0)
 		single_step_execve();
@@ -615,13 +615,13 @@ long compat_sys_execve(const char __user *path,
 		       struct pt_regs *regs)
 {
 	long error;
-	char *filename;
+	struct getname_info *filename;
 
 	filename = getname(path);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = compat_do_execve(filename, argv, envp, regs);
+	error = compat_do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 	if (error == 0)
 		single_step_execve();
diff --git a/arch/unicore32/kernel/sys.c b/arch/unicore32/kernel/sys.c
index 5fd9af7..7a0c372 100644
--- a/arch/unicore32/kernel/sys.c
+++ b/arch/unicore32/kernel/sys.c
@@ -51,13 +51,13 @@ asmlinkage long __sys_execve(const char __user *filename,
 			  struct pt_regs *regs)
 {
 	int error;
-	char *fn;
+	struct getname_info *fn;
 
 	fn = getname(filename);
 	error = PTR_ERR(fn);
 	if (IS_ERR(fn))
 		goto out;
-	error = do_execve(fn, argv, envp, regs);
+	error = do_execve(fn->name, argv, envp, regs);
 	putname(fn);
 out:
 	return error;
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index 2c8d6a3..03d723d 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -325,13 +325,13 @@ long xtensa_execve(const char __user *name,
                    struct pt_regs *regs)
 {
 	long error;
-	char * filename;
+	struct getname_info *filename;
 
 	filename = getname(name);
 	error = PTR_ERR(filename);
 	if (IS_ERR(filename))
 		goto out;
-	error = do_execve(filename, argv, envp, regs);
+	error = do_execve(filename->name, argv, envp, regs);
 	putname(filename);
 out:
 	return error;
diff --git a/fs/compat.c b/fs/compat.c
index 1bdb350..224f726 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -776,16 +776,16 @@ asmlinkage long compat_sys_mount(const char __user * dev_name,
 	char *kernel_type;
 	unsigned long data_page;
 	char *kernel_dev;
-	char *dir_page;
+	struct getname_info *dir;
 	int retval;
 
 	retval = copy_mount_string(type, &kernel_type);
 	if (retval < 0)
 		goto out;
 
-	dir_page = getname(dir_name);
-	retval = PTR_ERR(dir_page);
-	if (IS_ERR(dir_page))
+	dir = getname(dir_name);
+	retval = PTR_ERR(dir);
+	if (IS_ERR(dir))
 		goto out1;
 
 	retval = copy_mount_string(dev_name, &kernel_dev);
@@ -807,7 +807,7 @@ asmlinkage long compat_sys_mount(const char __user * dev_name,
 		}
 	}
 
-	retval = do_mount(kernel_dev, dir_page, kernel_type,
+	retval = do_mount(kernel_dev, dir->name, kernel_type,
 			flags, (void*)data_page);
 
  out4:
@@ -815,7 +815,7 @@ asmlinkage long compat_sys_mount(const char __user * dev_name,
  out3:
 	kfree(kernel_dev);
  out2:
-	putname(dir_page);
+	putname(dir);
  out1:
 	kfree(kernel_type);
  out:
diff --git a/fs/exec.c b/fs/exec.c
index 5481276..35932ea 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -115,7 +115,7 @@ static inline void put_binfmt(struct linux_binfmt * fmt)
 SYSCALL_DEFINE1(uselib, const char __user *, library)
 {
 	struct file *file;
-	char *tmp = getname(library);
+	struct getname_info *tmp = getname(library);
 	int error = PTR_ERR(tmp);
 	static const struct open_flags uselib_flags = {
 		.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
@@ -126,7 +126,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
 	if (IS_ERR(tmp))
 		goto out;
 
-	file = do_filp_open(AT_FDCWD, tmp, &uselib_flags, LOOKUP_FOLLOW);
+	file = do_filp_open(AT_FDCWD, tmp->name, &uselib_flags, LOOKUP_FOLLOW);
 	putname(tmp);
 	error = PTR_ERR(file);
 	if (IS_ERR(file))
@@ -2325,10 +2325,10 @@ SYSCALL_DEFINE3(execve,
 		const char __user *const __user *, argv,
 		const char __user *const __user *, envp)
 {
-	const char *path = getname(filename);
+	struct getname_info *path = getname(filename);
 	int error = PTR_ERR(path);
 	if (!IS_ERR(path)) {
-		error = do_execve(path, argv, envp, current_pt_regs());
+		error = do_execve(path->name, argv, envp, current_pt_regs());
 		putname(path);
 	}
 	return error;
@@ -2361,10 +2361,10 @@ asmlinkage long compat_sys_execve(const char __user * filename,
 	const compat_uptr_t __user * argv,
 	const compat_uptr_t __user * envp)
 {
-	const char *path = getname(filename);
+	struct getname_info *path = getname(filename);
 	int error = PTR_ERR(path);
 	if (!IS_ERR(path)) {
-		error = compat_do_execve(path, argv, envp, current_pt_regs());
+		error = compat_do_execve(path->name, argv, envp, current_pt_regs());
 		putname(path);
 	}
 	return error;
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 96f2428..9f4d31a 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -124,7 +124,7 @@ EXPORT_SYMBOL(unregister_filesystem);
 static int fs_index(const char __user * __name)
 {
 	struct file_system_type * tmp;
-	char * name;
+	struct getname_info *name;
 	int err, index;
 
 	name = getname(__name);
@@ -135,7 +135,7 @@ static int fs_index(const char __user * __name)
 	err = -EINVAL;
 	read_lock(&file_systems_lock);
 	for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {
-		if (strcmp(tmp->name,name) == 0) {
+		if (strcmp(tmp->name, name->name) == 0) {
 			err = index;
 			break;
 		}
diff --git a/fs/namei.c b/fs/namei.c
index 64f7350..60db390 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -117,18 +117,37 @@
  * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
  * PATH_MAX includes the nul terminator --RR.
  */
-static char *getname_flags(const char __user *filename, int flags, int *empty)
+void final_putname(struct getname_info *ginfo)
 {
-	char *result = __getname(), *err;
+	__putname(ginfo->name);
+	kfree(ginfo);
+}
+
+static struct getname_info *
+getname_flags(const char __user *filename, int flags, int *empty)
+{
+	struct getname_info *result, *err;
+	char *kname;
 	int len;
 
+	/* FIXME: create dedicated slabcache? */
+	result = kzalloc(sizeof(*result), GFP_KERNEL);
 	if (unlikely(!result))
 		return ERR_PTR(-ENOMEM);
 
-	len = strncpy_from_user(result, filename, PATH_MAX);
-	err = ERR_PTR(len);
-	if (unlikely(len < 0))
+	kname = __getname();
+	if (unlikely(!kname)) {
+		err = ERR_PTR(-ENOMEM);
+		goto error_free_ginfo;
+	}
+
+	result->name = kname;
+	result->uptr = filename;
+	len = strncpy_from_user(kname, filename, PATH_MAX);
+	if (unlikely(len < 0)) {
+		err = ERR_PTR(len);
 		goto error;
+	}
 
 	/* The empty path is special. */
 	if (unlikely(!len)) {
@@ -146,25 +165,28 @@ static char *getname_flags(const char __user *filename, int flags, int *empty)
 	}
 
 error:
-	__putname(result);
+	__putname(kname);
+error_free_ginfo:
+	kfree(result);
 	return err;
 }
 
-char *getname(const char __user * filename)
+struct getname_info *
+getname(const char __user * filename)
 {
 	return getname_flags(filename, 0, NULL);
 }
+EXPORT_SYMBOL(getname);
 
-#ifdef CONFIG_AUDITSYSCALL
-void putname(const char *name)
+void putname(struct getname_info *ginfo)
 {
+#ifdef CONFIG_AUDITSYSCALL
 	if (unlikely(!audit_dummy_context()))
-		audit_putname(name);
-	else
-		__putname(name);
+		return audit_putname(ginfo);
+#endif
+	final_putname(ginfo);
 }
 EXPORT_SYMBOL(putname);
-#endif
 
 static int check_acl(struct inode *inode, int mask)
 {
@@ -2098,13 +2120,13 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
 		 struct path *path, int *empty)
 {
 	struct nameidata nd;
-	char *tmp = getname_flags(name, flags, empty);
+	struct getname_info *tmp = getname_flags(name, flags, empty);
 	int err = PTR_ERR(tmp);
 	if (!IS_ERR(tmp)) {
 
 		BUG_ON(flags & LOOKUP_PARENT);
 
-		err = do_path_lookup(dfd, tmp, flags, &nd);
+		err = do_path_lookup(dfd, tmp->name, flags, &nd);
 		putname(tmp);
 		if (!err)
 			*path = nd.path;
@@ -2118,22 +2140,22 @@ int user_path_at(int dfd, const char __user *name, unsigned flags,
 	return user_path_at_empty(dfd, name, flags, path, NULL);
 }
 
-static int user_path_parent(int dfd, const char __user *path,
-			struct nameidata *nd, char **name)
+static struct getname_info *
+user_path_parent(int dfd, const char __user *path, struct nameidata *nd)
 {
-	char *s = getname(path);
+	struct getname_info *s = getname(path);
 	int error;
 
 	if (IS_ERR(s))
-		return PTR_ERR(s);
+		return s;
 
-	error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd);
-	if (error)
+	error = do_path_lookup(dfd, s->name, LOOKUP_PARENT, nd);
+	if (error) {
 		putname(s);
-	else
-		*name = s;
+		return ERR_PTR(error);
+	}
 
-	return error;
+	return s;
 }
 
 /*
@@ -3044,11 +3066,11 @@ EXPORT_SYMBOL(done_path_create);
 
 struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, int is_dir)
 {
-	char *tmp = getname(pathname);
+	struct getname_info *tmp = getname(pathname);
 	struct dentry *res;
 	if (IS_ERR(tmp))
 		return ERR_CAST(tmp);
-	res = kern_path_create(dfd, tmp, path, is_dir);
+	res = kern_path_create(dfd, tmp->name, path, is_dir);
 	putname(tmp);
 	return res;
 }
@@ -3253,13 +3275,13 @@ out:
 static long do_rmdir(int dfd, const char __user *pathname)
 {
 	int error = 0;
-	char * name;
+	struct getname_info *name;
 	struct dentry *dentry;
 	struct nameidata nd;
 
-	error = user_path_parent(dfd, pathname, &nd, &name);
-	if (error)
-		return error;
+	name = user_path_parent(dfd, pathname, &nd);
+	if (IS_ERR(name))
+		return PTR_ERR(name);
 
 	switch(nd.last_type) {
 	case LAST_DOTDOT:
@@ -3348,14 +3370,14 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
 static long do_unlinkat(int dfd, const char __user *pathname)
 {
 	int error;
-	char *name;
+	struct getname_info *name;
 	struct dentry *dentry;
 	struct nameidata nd;
 	struct inode *inode = NULL;
 
-	error = user_path_parent(dfd, pathname, &nd, &name);
-	if (error)
-		return error;
+	name = user_path_parent(dfd, pathname, &nd);
+	if (IS_ERR(name))
+		return PTR_ERR(name);
 
 	error = -EISDIR;
 	if (nd.last_type != LAST_NORM)
@@ -3439,7 +3461,7 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
 		int, newdfd, const char __user *, newname)
 {
 	int error;
-	char *from;
+	struct getname_info *from;
 	struct dentry *dentry;
 	struct path path;
 
@@ -3452,9 +3474,9 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
 	if (IS_ERR(dentry))
 		goto out_putname;
 
-	error = security_path_symlink(&path, dentry, from);
+	error = security_path_symlink(&path, dentry, from->name);
 	if (!error)
-		error = vfs_symlink(path.dentry->d_inode, dentry, from);
+		error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
 	done_path_create(&path, dentry);
 out_putname:
 	putname(from);
@@ -3734,17 +3756,21 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 	struct dentry *old_dentry, *new_dentry;
 	struct dentry *trap;
 	struct nameidata oldnd, newnd;
-	char *from;
-	char *to;
+	struct getname_info *from;
+	struct getname_info *to;
 	int error;
 
-	error = user_path_parent(olddfd, oldname, &oldnd, &from);
-	if (error)
+	from = user_path_parent(olddfd, oldname, &oldnd);
+	if (IS_ERR(from)) {
+		error = PTR_ERR(from);
 		goto exit;
+	}
 
-	error = user_path_parent(newdfd, newname, &newnd, &to);
-	if (error)
+	to = user_path_parent(newdfd, newname, &newnd);
+	if (IS_ERR(to)) {
+		error = PTR_ERR(to);
 		goto exit1;
+	}
 
 	error = -EXDEV;
 	if (oldnd.path.mnt != newnd.path.mnt)
@@ -3968,7 +3994,6 @@ EXPORT_SYMBOL(follow_down_one);
 EXPORT_SYMBOL(follow_down);
 EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
-EXPORT_SYMBOL(getname);
 EXPORT_SYMBOL(lock_rename);
 EXPORT_SYMBOL(lookup_one_len);
 EXPORT_SYMBOL(page_follow_link_light);
diff --git a/fs/namespace.c b/fs/namespace.c
index 8e52b1c..7dfe6ac 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2402,7 +2402,7 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
 {
 	int ret;
 	char *kernel_type;
-	char *kernel_dir;
+	struct getname_info *kernel_dir;
 	char *kernel_dev;
 	unsigned long data_page;
 
@@ -2424,7 +2424,7 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
 	if (ret < 0)
 		goto out_data;
 
-	ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,
+	ret = do_mount(kernel_dev, kernel_dir->name, kernel_type, flags,
 		(void *) data_page);
 
 	free_page(data_page);
diff --git a/fs/open.c b/fs/open.c
index ec06921..3796d0c 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -945,13 +945,13 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 {
 	struct open_flags op;
 	int lookup = build_open_flags(flags, mode, &op);
-	char *tmp = getname(filename);
+	struct getname_info *tmp = getname(filename);
 	int fd = PTR_ERR(tmp);
 
 	if (!IS_ERR(tmp)) {
 		fd = get_unused_fd_flags(flags);
 		if (fd >= 0) {
-			struct file *f = do_filp_open(dfd, tmp, &op, lookup);
+			struct file *f = do_filp_open(dfd, tmp->name, &op, lookup);
 			if (IS_ERR(f)) {
 				put_unused_fd(fd);
 				fd = PTR_ERR(f);
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 6f15578..1cf3f2a 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -315,11 +315,11 @@ static struct super_block *quotactl_block(const char __user *special, int cmd)
 #ifdef CONFIG_BLOCK
 	struct block_device *bdev;
 	struct super_block *sb;
-	char *tmp = getname(special);
+	struct getname_info *tmp = getname(special);
 
 	if (IS_ERR(tmp))
 		return ERR_CAST(tmp);
-	bdev = lookup_bdev(tmp);
+	bdev = lookup_bdev(tmp->name);
 	putname(tmp);
 	if (IS_ERR(bdev))
 		return ERR_CAST(bdev);
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 0f7284a..d361921 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -459,6 +459,8 @@ extern int audit_classify_arch(int arch);
 #define AUDIT_TYPE_CHILD_CREATE 4	/* a child being created */
 
 #ifdef CONFIG_AUDITSYSCALL
+struct getname_info;
+
 /* These are defined in auditsc.c */
 				/* Public API */
 extern int  audit_alloc(struct task_struct *task);
@@ -467,8 +469,8 @@ extern void __audit_syscall_entry(int arch,
 				  int major, unsigned long a0, unsigned long a1,
 				  unsigned long a2, unsigned long a3);
 extern void __audit_syscall_exit(int ret_success, long ret_value);
-extern void __audit_getname(const char *name);
-extern void audit_putname(const char *name);
+extern void __audit_getname(struct getname_info *ginfo);
+extern void audit_putname(struct getname_info *ginfo);
 extern void __audit_inode(const char *name, const struct dentry *dentry,
 				unsigned int parent);
 extern void __audit_inode_child(const struct inode *parent,
@@ -503,7 +505,7 @@ static inline void audit_syscall_exit(void *pt_regs)
 		__audit_syscall_exit(success, return_code);
 	}
 }
-static inline void audit_getname(const char *name)
+static inline void audit_getname(struct getname_info *name)
 {
 	if (unlikely(!audit_dummy_context()))
 		__audit_getname(name);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b6ce6fc..1b0d0fb 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2189,6 +2189,10 @@ static inline int break_lease(struct inode *inode, unsigned int mode)
 #endif /* CONFIG_FILE_LOCKING */
 
 /* fs/open.c */
+struct getname_info {
+	const char *name;		/* pointer to actual string */
+	const __user char *uptr;	/* original userland pointer */
+};
 
 extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
 		       struct file *filp);
@@ -2201,7 +2205,9 @@ extern struct file *file_open_root(struct dentry *, struct vfsmount *,
 				   const char *, int);
 extern struct file * dentry_open(const struct path *, int, const struct cred *);
 extern int filp_close(struct file *, fl_owner_t id);
-extern char * getname(const char __user *);
+
+extern struct getname_info *getname(const char __user *);
+
 enum {
 	FILE_CREATED = 1,
 	FILE_OPENED = 2
@@ -2221,12 +2227,14 @@ extern void __init vfs_caches_init(unsigned long);
 
 extern struct kmem_cache *names_cachep;
 
+extern void final_putname(struct getname_info *ginfo);
+
 #define __getname()		kmem_cache_alloc(names_cachep, GFP_KERNEL)
 #define __putname(name)		kmem_cache_free(names_cachep, (void *)(name))
 #ifndef CONFIG_AUDITSYSCALL
-#define putname(name)   __putname(name)
+#define putname(name)		final_putname(name)
 #else
-extern void putname(const char *name);
+extern void putname(struct getname_info *ginfo);
 #endif
 
 #ifdef CONFIG_BLOCK
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 69cb5fe..2071a27 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -773,7 +773,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
 {
 	struct path path;
 	struct file *filp;
-	char *name;
+	struct getname_info *name;
 	struct mq_attr attr;
 	int fd, error;
 	struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
@@ -796,7 +796,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
 	ro = mnt_want_write(mnt);	/* we'll drop it in any case */
 	error = 0;
 	mutex_lock(&root->d_inode->i_mutex);
-	path.dentry = lookup_one_len(name, root, strlen(name));
+	path.dentry = lookup_one_len(name->name, root, strlen(name->name));
 	if (IS_ERR(path.dentry)) {
 		error = PTR_ERR(path.dentry);
 		goto out_putfd;
@@ -805,7 +805,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
 
 	if (oflag & O_CREAT) {
 		if (path.dentry->d_inode) {	/* entry already exists */
-			audit_inode(name, path.dentry, 0);
+			audit_inode(name->name, path.dentry, 0);
 			if (oflag & O_EXCL) {
 				error = -EEXIST;
 				goto out;
@@ -825,7 +825,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
 			error = -ENOENT;
 			goto out;
 		}
-		audit_inode(name, path.dentry, 0);
+		audit_inode(name->name, path.dentry, 0);
 		filp = do_open(&path, oflag);
 	}
 
@@ -850,7 +850,7 @@ out_putname:
 SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
 {
 	int err;
-	char *name;
+	struct getname_info *name;
 	struct dentry *dentry;
 	struct inode *inode = NULL;
 	struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
@@ -864,7 +864,8 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
 	if (err)
 		goto out_name;
 	mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT);
-	dentry = lookup_one_len(name, mnt->mnt_root, strlen(name));
+	dentry = lookup_one_len(name->name, mnt->mnt_root,
+				strlen(name->name));
 	if (IS_ERR(dentry)) {
 		err = PTR_ERR(dentry);
 		goto out_unlock;
diff --git a/kernel/acct.c b/kernel/acct.c
index e97d159..3943608 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -260,10 +260,10 @@ SYSCALL_DEFINE1(acct, const char __user *, name)
 		return -EPERM;
 
 	if (name) {
-		char *tmp = getname(name);
+		struct getname_info *tmp = getname(name);
 		if (IS_ERR(tmp))
 			return (PTR_ERR(tmp));
-		error = acct_on(tmp);
+		error = acct_on(tmp->name);
 		putname(tmp);
 	} else {
 		struct bsd_acct_struct *acct;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 2f20b97..3ffb71a 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -105,26 +105,26 @@ struct audit_cap_data {
  *
  * Further, in fs/namei.c:path_lookup() we store the inode and device. */
 struct audit_names {
-	struct list_head list;		/* audit_context->names_list */
-	const char	*name;
-	unsigned long	ino;
-	dev_t		dev;
-	umode_t		mode;
-	uid_t		uid;
-	gid_t		gid;
-	dev_t		rdev;
-	u32		osid;
-	struct audit_cap_data fcap;
-	unsigned int	fcap_ver;
-	int		name_len;	/* number of name's characters to log */
-	unsigned char	type;		/* record type */
-	bool		name_put;	/* call __putname() for this name */
+	struct list_head	list;		/* audit_context->names_list */
+	struct getname_info	*ginfo;
+	unsigned long		ino;
+	dev_t			dev;
+	umode_t			mode;
+	uid_t			uid;
+	gid_t			gid;
+	dev_t			rdev;
+	u32			osid;
+	struct audit_cap_data 	fcap;
+	unsigned int		fcap_ver;
+	int			name_len;	/* nr of characters to log */
+	unsigned char		type;		/* record type */
+	bool			name_put;	/* call final_putname() */
 	/*
 	 * This was an allocated audit_names and not from the array of
 	 * names allocated in the task audit context.  Thus this name
 	 * should be freed on syscall exit
 	 */
-	bool		should_free;
+	bool			should_free;
 };
 
 struct audit_aux_data {
@@ -1004,7 +1004,7 @@ static inline void audit_free_names(struct audit_context *context)
 		       context->ino_count);
 		list_for_each_entry(n, &context->names_list, list) {
 			printk(KERN_ERR "names[%d] = %p = %s\n", i,
-			       n->name, n->name ?: "(null)");
+			       n->ginfo, n->ginfo->name ?: "(null)");
 		}
 		dump_stack();
 		return;
@@ -1017,8 +1017,8 @@ static inline void audit_free_names(struct audit_context *context)
 
 	list_for_each_entry_safe(n, next, &context->names_list, list) {
 		list_del(&n->list);
-		if (n->name && n->name_put)
-			__putname(n->name);
+		if (n->ginfo && n->name_put)
+			__putname(n->ginfo);
 		if (n->should_free)
 			kfree(n);
 	}
@@ -1529,12 +1529,12 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
 
 	audit_log_format(ab, "item=%d", record_num);
 
-	if (n->name) {
+	if (n->ginfo) {
 		switch (n->name_len) {
 		case AUDIT_NAME_FULL:
 			/* log the full path */
 			audit_log_format(ab, " name=");
-			audit_log_untrustedstring(ab, n->name);
+			audit_log_untrustedstring(ab, n->ginfo->name);
 			break;
 		case 0:
 			/* name was specified as a relative path and the
@@ -1544,7 +1544,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
 		default:
 			/* log the name's directory component */
 			audit_log_format(ab, " name=");
-			audit_log_n_untrustedstring(ab, n->name,
+			audit_log_n_untrustedstring(ab, n->ginfo->name,
 						    n->name_len);
 		}
 	} else
@@ -2040,7 +2040,7 @@ static struct audit_names *audit_alloc_name(struct audit_context *context,
  * Add a name to the list of audit names for this context.
  * Called from fs/namei.c:getname().
  */
-void __audit_getname(const char *name)
+void __audit_getname(struct getname_info *ginfo)
 {
 	struct audit_context *context = current->audit_context;
 	struct audit_names *n;
@@ -2048,7 +2048,7 @@ void __audit_getname(const char *name)
 	if (!context->in_syscall) {
 #if AUDIT_DEBUG == 2
 		printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n",
-		       __FILE__, __LINE__, context->serial, name);
+		       __FILE__, __LINE__, context->serial, ginfo);
 		dump_stack();
 #endif
 		return;
@@ -2058,7 +2058,7 @@ void __audit_getname(const char *name)
 	if (!n)
 		return;
 
-	n->name = name;
+	n->ginfo = ginfo;
 	n->name_len = AUDIT_NAME_FULL;
 	n->name_put = true;
 
@@ -2073,7 +2073,7 @@ void __audit_getname(const char *name)
  * then we delay the putname until syscall exit.
  * Called from include/linux/fs.h:putname().
  */
-void audit_putname(const char *name)
+void audit_putname(struct getname_info *ginfo)
 {
 	struct audit_context *context = current->audit_context;
 
@@ -2081,17 +2081,17 @@ void audit_putname(const char *name)
 	if (!context->in_syscall) {
 #if AUDIT_DEBUG == 2
 		printk(KERN_ERR "%s:%d(:%d): __putname(%p)\n",
-		       __FILE__, __LINE__, context->serial, name);
+		       __FILE__, __LINE__, context->serial, ginfo);
 		if (context->name_count) {
 			struct audit_names *n;
 			int i;
 
 			list_for_each_entry(n, &context->names_list, list)
 				printk(KERN_ERR "name[%d] = %p = %s\n", i,
-				       n->name, n->name ?: "(null)");
+				       n->ginfo, n->ginfo->name ?: "(null)");
 			}
 #endif
-		__putname(name);
+		__putname(ginfo);
 	}
 #if AUDIT_DEBUG
 	else {
@@ -2102,8 +2102,8 @@ void audit_putname(const char *name)
 			       " put_count=%d\n",
 			       __FILE__, __LINE__,
 			       context->serial, context->major,
-			       context->in_syscall, name, context->name_count,
-			       context->put_count);
+			       context->in_syscall, ginfo->name,
+			       context->name_count, context->put_count);
 			dump_stack();
 		}
 	}
@@ -2166,7 +2166,7 @@ void __audit_inode(const char *name, const struct dentry *dentry,
 
 	list_for_each_entry_reverse(n, &context->names_list, list) {
 		/* does the name pointer match? */
-		if (n->name != name)
+		if (!n->ginfo || n->ginfo->name != name)
 			continue;
 
 		/* match the correct record type */
@@ -2189,7 +2189,7 @@ out_alloc:
 		return;
 out:
 	if (parent) {
-		n->name_len = n->name ? parent_len(n->name) : AUDIT_NAME_FULL;
+		n->name_len = n->ginfo ? parent_len(n->ginfo->name) : AUDIT_NAME_FULL;
 		n->type = AUDIT_TYPE_PARENT;
 	} else {
 		n->name_len = AUDIT_NAME_FULL;
@@ -2230,11 +2230,11 @@ void __audit_inode_child(const struct inode *parent,
 
 	/* look for a parent entry first */
 	list_for_each_entry(n, &context->names_list, list) {
-		if (!n->name || n->type != AUDIT_TYPE_PARENT)
+		if (!n->ginfo || n->type != AUDIT_TYPE_PARENT)
 			continue;
 
 		if (n->ino == parent->i_ino &&
-		    !audit_compare_dname_path(dname, n->name, n->name_len)) {
+		    !audit_compare_dname_path(dname, n->ginfo->name, n->name_len)) {
 			found_parent = n;
 			break;
 		}
@@ -2243,15 +2243,15 @@ void __audit_inode_child(const struct inode *parent,
 	/* is there a matching child entry? */
 	list_for_each_entry(n, &context->names_list, list) {
 		/* can only match entries that have a name */
-		if (!n->name || n->type != type)
+		if (!n->ginfo || n->type != type)
 			continue;
 
 		/* if we found a parent, make sure this one is a child of it */
-		if (found_parent && (n->name != found_parent->name))
+		if (found_parent && (n->ginfo != found_parent->ginfo))
 			continue;
 
-		if (!strcmp(dname, n->name) ||
-		    !audit_compare_dname_path(dname, n->name,
+		if (!strcmp(dname, n->ginfo->name) ||
+		    !audit_compare_dname_path(dname, n->ginfo->name,
 						found_parent ?
 						found_parent->name_len :
 						AUDIT_NAME_FULL)) {
@@ -2277,7 +2277,7 @@ void __audit_inode_child(const struct inode *parent,
 		 * directory. All names for this context are relinquished in
 		 * audit_free_names() */
 		if (found_parent) {
-			found_child->name = found_parent->name;
+			found_child->ginfo = found_parent->ginfo;
 			found_child->name_len = AUDIT_NAME_FULL;
 			/* don't call __putname() */
 			found_child->name_put = false;
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 14e254c..8dbf46d 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1483,7 +1483,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 	struct file *swap_file, *victim;
 	struct address_space *mapping;
 	struct inode *inode;
-	char *pathname;
+	struct getname_info *pathname;
 	int oom_score_adj;
 	int i, type, prev;
 	int err;
@@ -1498,8 +1498,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 	if (IS_ERR(pathname))
 		goto out;
 
-	victim = filp_open(pathname, O_RDWR|O_LARGEFILE, 0);
-	putname(pathname);
+	victim = filp_open(pathname->name, O_RDWR|O_LARGEFILE, 0);
 	err = PTR_ERR(victim);
 	if (IS_ERR(victim))
 		goto out;
@@ -1936,7 +1935,7 @@ static int setup_swap_map_and_extents(struct swap_info_struct *p,
 SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 {
 	struct swap_info_struct *p;
-	char *name;
+	struct getname_info *name;
 	struct file *swap_file = NULL;
 	struct address_space *mapping;
 	int i;
@@ -1967,7 +1966,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 		name = NULL;
 		goto bad_swap;
 	}
-	swap_file = filp_open(name, O_RDWR|O_LARGEFILE, 0);
+	swap_file = filp_open(name->name, O_RDWR|O_LARGEFILE, 0);
 	if (IS_ERR(swap_file)) {
 		error = PTR_ERR(swap_file);
 		swap_file = NULL;
@@ -2053,7 +2052,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 
 	printk(KERN_INFO "Adding %uk swap on %s.  "
 			"Priority:%d extents:%d across:%lluk %s%s%s\n",
-		p->pages<<(PAGE_SHIFT-10), name, p->prio,
+		p->pages<<(PAGE_SHIFT-10), name->name, p->prio,
 		nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10),
 		(p->flags & SWP_SOLIDSTATE) ? "SS" : "",
 		(p->flags & SWP_DISCARDABLE) ? "D" : "",
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 05/10] audit: allow audit code to satisfy getname requests from its names_list
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (3 preceding siblings ...)
  2012-09-07 13:37 ` [PATCH v1 04/10] vfs: define getname_info struct and have getname() return it Jeff Layton
@ 2012-09-07 13:38 ` Jeff Layton
  2012-09-07 13:38 ` [PATCH v1 06/10] vfs: turn do_path_lookup into wrapper around getname_info variant Jeff Layton
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:38 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

Currently, if we call getname() on a userland string more than once,
we'll get multiple copies of the string and multiple audit_names
records.

Add a function that will allow the audit_names code to satisfy getname
requests using info from the audit_names list, avoiding a new allocation
and audit_names records.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/audit.h |  8 ++++++++
 kernel/auditsc.c      | 23 +++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index d361921..ea58de3 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -469,6 +469,7 @@ extern void __audit_syscall_entry(int arch,
 				  int major, unsigned long a0, unsigned long a1,
 				  unsigned long a2, unsigned long a3);
 extern void __audit_syscall_exit(int ret_success, long ret_value);
+extern struct getname_info *__audit_reusename(const __user char *uptr);
 extern void __audit_getname(struct getname_info *ginfo);
 extern void audit_putname(struct getname_info *ginfo);
 extern void __audit_inode(const char *name, const struct dentry *dentry,
@@ -505,6 +506,12 @@ static inline void audit_syscall_exit(void *pt_regs)
 		__audit_syscall_exit(success, return_code);
 	}
 }
+static inline struct getname_info *audit_reusename(const __user char *name)
+{
+	if (unlikely(!audit_dummy_context()))
+		return __audit_reusename(name);
+	return NULL;
+}
 static inline void audit_getname(struct getname_info *name)
 {
 	if (unlikely(!audit_dummy_context()))
@@ -642,6 +649,7 @@ extern int audit_signals;
 #define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0)
 #define audit_syscall_exit(r) do { ; } while (0)
 #define audit_dummy_context() 1
+#define audit_reusename(g) ({ NULL; })
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
 #define __audit_inode(n,d,p) do { ; } while (0)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 3ffb71a..22121bb 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2034,6 +2034,29 @@ static struct audit_names *audit_alloc_name(struct audit_context *context,
 }
 
 /**
+ * audit_reusename - fill out getname_info with info from existing entry
+ * @uptr: userland ptr to pathname
+ *
+ * Search the audit_names list for the current audit context. If there is an
+ * existing entry with a matching "uptr" then return the getname_info
+ * associated with that audit_name. If not, return NULL.
+ */
+struct getname_info *
+__audit_reusename(const __user char *uptr)
+{
+	struct audit_context *context = current->audit_context;
+	struct audit_names *n;
+
+	list_for_each_entry(n, &context->names_list, list) {
+		if (!n->ginfo)
+			continue;
+		if (n->ginfo->uptr == uptr)
+			return n->ginfo;
+	}
+	return NULL;
+}
+
+/**
  * audit_getname - add a name to the list
  * @name: name to add
  *
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 06/10] vfs: turn do_path_lookup into wrapper around getname_info variant
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (4 preceding siblings ...)
  2012-09-07 13:38 ` [PATCH v1 05/10] audit: allow audit code to satisfy getname requests from its names_list Jeff Layton
@ 2012-09-07 13:38 ` Jeff Layton
  2012-09-07 13:38 ` [PATCH v1 07/10] vfs: make path_openat take a getname_info pointer Jeff Layton
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:38 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

...and make the user_path callers use that variant instead.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/namei.c | 30 +++++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 60db390..774f942 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1989,20 +1989,30 @@ static int path_lookupat(int dfd, const char *name,
 	return err;
 }
 
-static int do_path_lookup(int dfd, const char *name,
+static int ginfo_lookup(int dfd, struct getname_info *ginfo,
 				unsigned int flags, struct nameidata *nd)
 {
-	int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd);
+	int retval = path_lookupat(dfd, ginfo->name, flags | LOOKUP_RCU, nd);
 	if (unlikely(retval == -ECHILD))
-		retval = path_lookupat(dfd, name, flags, nd);
+		retval = path_lookupat(dfd, ginfo->name, flags, nd);
 	if (unlikely(retval == -ESTALE))
-		retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd);
+		retval = path_lookupat(dfd, ginfo->name,
+						flags | LOOKUP_REVAL, nd);
 
 	if (likely(!retval))
-		audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT);
+		audit_inode(ginfo->name, nd->path.dentry,
+						flags & LOOKUP_PARENT);
 	return retval;
 }
 
+static int do_path_lookup(int dfd, const char *name,
+				unsigned int flags, struct nameidata *nd)
+{
+	struct getname_info ginfo = { .name = name };
+
+	return ginfo_lookup(dfd, &ginfo, flags, nd);
+}
+
 /* does lookup, returns the object with parent locked */
 struct dentry *kern_path_locked(const char *name, struct path *path)
 {
@@ -2126,7 +2136,7 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
 
 		BUG_ON(flags & LOOKUP_PARENT);
 
-		err = do_path_lookup(dfd, tmp->name, flags, &nd);
+		err = ginfo_lookup(dfd, tmp, flags, &nd);
 		putname(tmp);
 		if (!err)
 			*path = nd.path;
@@ -2140,6 +2150,12 @@ int user_path_at(int dfd, const char __user *name, unsigned flags,
 	return user_path_at_empty(dfd, name, flags, path, NULL);
 }
 
+/*
+ * NB: most callers don't do anything directly with the reference to the
+ *     to ginfo, but the nd->last pointer points into the name string
+ *     allocated by getinfo. So we must hold the reference to it until all
+ *     path-walking is complete.
+ */
 static struct getname_info *
 user_path_parent(int dfd, const char __user *path, struct nameidata *nd)
 {
@@ -2149,7 +2165,7 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd)
 	if (IS_ERR(s))
 		return s;
 
-	error = do_path_lookup(dfd, s->name, LOOKUP_PARENT, nd);
+	error = ginfo_lookup(dfd, s, LOOKUP_PARENT, nd);
 	if (error) {
 		putname(s);
 		return ERR_PTR(error);
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 07/10] vfs: make path_openat take a getname_info pointer
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (5 preceding siblings ...)
  2012-09-07 13:38 ` [PATCH v1 06/10] vfs: turn do_path_lookup into wrapper around getname_info variant Jeff Layton
@ 2012-09-07 13:38 ` Jeff Layton
  2012-09-07 13:38 ` [PATCH v1 08/10] audit: make audit_inode take getname_info Jeff Layton
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:38 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

...and fix up the callers. For do_file_open_root, just declare a
getname_info on the stack and fill out the .name field. For
do_filp_open, make it also take a getname_info pointer, and fix up its
callers to call it appropriately.

For filp_open, add a variant that takes a getname_info pointer and turn
filp_open into a wrapper around it.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/exec.c          |  5 +++--
 fs/internal.h      |  4 ++--
 fs/namei.c         | 28 +++++++++++++++-------------
 fs/open.c          | 33 +++++++++++++++++++++++++++++----
 include/linux/fs.h |  1 +
 kernel/acct.c      |  6 +++---
 mm/swapfile.c      |  4 ++--
 7 files changed, 55 insertions(+), 26 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 35932ea..3cfb75c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -126,7 +126,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
 	if (IS_ERR(tmp))
 		goto out;
 
-	file = do_filp_open(AT_FDCWD, tmp->name, &uselib_flags, LOOKUP_FOLLOW);
+	file = do_filp_open(AT_FDCWD, tmp, &uselib_flags, LOOKUP_FOLLOW);
 	putname(tmp);
 	error = PTR_ERR(file);
 	if (IS_ERR(file))
@@ -761,13 +761,14 @@ struct file *open_exec(const char *name)
 {
 	struct file *file;
 	int err;
+	struct getname_info tmp = { .name = name };
 	static const struct open_flags open_exec_flags = {
 		.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
 		.acc_mode = MAY_EXEC | MAY_OPEN,
 		.intent = LOOKUP_OPEN
 	};
 
-	file = do_filp_open(AT_FDCWD, name, &open_exec_flags, LOOKUP_FOLLOW);
+	file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags, LOOKUP_FOLLOW);
 	if (IS_ERR(file))
 		goto out;
 
diff --git a/fs/internal.h b/fs/internal.h
index 371bcc4..ddc0d2c 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -97,8 +97,8 @@ struct open_flags {
 	int acc_mode;
 	int intent;
 };
-extern struct file *do_filp_open(int dfd, const char *pathname,
-		const struct open_flags *op, int lookup_flags);
+extern struct file *do_filp_open(int dfd, struct getname_info *ginfo,
+		const struct open_flags *op, int flags);
 extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
 		const char *, const struct open_flags *, int lookup_flags);
 
diff --git a/fs/namei.c b/fs/namei.c
index 774f942..9ef0d00 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2663,7 +2663,7 @@ out_dput:
  */
 static int do_last(struct nameidata *nd, struct path *path,
 		   struct file *file, const struct open_flags *op,
-		   int *opened, const char *pathname)
+		   int *opened, struct getname_info *ginfo)
 {
 	struct dentry *dir = nd->path.dentry;
 	int open_flag = op->open_flag;
@@ -2675,6 +2675,7 @@ static int do_last(struct nameidata *nd, struct path *path,
 	struct path save_parent = { .dentry = NULL, .mnt = NULL };
 	bool retried = false;
 	int error;
+	const char *pathname = ginfo->name;
 
 	nd->flags &= ~LOOKUP_PARENT;
 	nd->flags |= op->intent;
@@ -2909,7 +2910,7 @@ stale_open:
 	goto retry_lookup;
 }
 
-static struct file *path_openat(int dfd, const char *pathname,
+static struct file *path_openat(int dfd, struct getname_info *ginfo,
 		struct nameidata *nd, const struct open_flags *op, int flags)
 {
 	struct file *base = NULL;
@@ -2924,16 +2925,16 @@ static struct file *path_openat(int dfd, const char *pathname,
 
 	file->f_flags = op->open_flag;
 
-	error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
+	error = path_init(dfd, ginfo->name, flags | LOOKUP_PARENT, nd, &base);
 	if (unlikely(error))
 		goto out;
 
 	current->total_link_count = 0;
-	error = link_path_walk(pathname, nd);
+	error = link_path_walk(ginfo->name, nd);
 	if (unlikely(error))
 		goto out;
 
-	error = do_last(nd, &path, file, op, &opened, pathname);
+	error = do_last(nd, &path, file, op, &opened, ginfo);
 	while (unlikely(error > 0)) { /* trailing symlink */
 		struct path link = path;
 		void *cookie;
@@ -2951,7 +2952,7 @@ static struct file *path_openat(int dfd, const char *pathname,
 		error = follow_link(&link, nd, &cookie);
 		if (unlikely(error))
 			break;
-		error = do_last(nd, &path, file, op, &opened, pathname);
+		error = do_last(nd, &path, file, op, &opened, ginfo);
 		put_link(nd, &link, cookie);
 	}
 out:
@@ -2975,17 +2976,17 @@ out:
 	return file;
 }
 
-struct file *do_filp_open(int dfd, const char *pathname,
+struct file *do_filp_open(int dfd, struct getname_info *ginfo,
 		const struct open_flags *op, int flags)
 {
 	struct nameidata nd;
 	struct file *filp;
 
-	filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
+	filp = path_openat(dfd, ginfo, &nd, op, flags | LOOKUP_RCU);
 	if (unlikely(filp == ERR_PTR(-ECHILD)))
-		filp = path_openat(dfd, pathname, &nd, op, flags);
+		filp = path_openat(dfd, ginfo, &nd, op, flags);
 	if (unlikely(filp == ERR_PTR(-ESTALE)))
-		filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
+		filp = path_openat(dfd, ginfo, &nd, op, flags | LOOKUP_REVAL);
 	return filp;
 }
 
@@ -2994,6 +2995,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
 {
 	struct nameidata nd;
 	struct file *file;
+	struct getname_info ginfo = { .name = name };
 
 	nd.root.mnt = mnt;
 	nd.root.dentry = dentry;
@@ -3003,11 +3005,11 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
 	if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN)
 		return ERR_PTR(-ELOOP);
 
-	file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU);
+	file = path_openat(-1, &ginfo, &nd, op, flags | LOOKUP_RCU);
 	if (unlikely(file == ERR_PTR(-ECHILD)))
-		file = path_openat(-1, name, &nd, op, flags);
+		file = path_openat(-1, &ginfo, &nd, op, flags);
 	if (unlikely(file == ERR_PTR(-ESTALE)))
-		file = path_openat(-1, name, &nd, op, flags | LOOKUP_REVAL);
+		file = path_openat(-1, &ginfo, &nd, op, flags | LOOKUP_REVAL);
 	return file;
 }
 
diff --git a/fs/open.c b/fs/open.c
index 3796d0c..d0f225e 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -909,6 +909,24 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 }
 
 /**
+ * filp_open_ginfo - open file and return file pointer
+ *
+ * @ginfo:	getname_info containing path to open
+ * @flags:	open flags as per the open(2) second argument
+ * @mode:	mode for the new file if O_CREAT is set, else ignored
+ *
+ * This is the helper to open a file from kernelspace if you really
+ * have to.  But in generally you should not do this, so please move
+ * along, nothing to see here..
+ */
+struct file *filp_open_ginfo(struct getname_info *ginfo, int flags, umode_t mode)
+{
+	struct open_flags op;
+	int lookup = build_open_flags(flags, mode, &op);
+	return do_filp_open(AT_FDCWD, ginfo, &op, lookup);
+}
+
+/**
  * filp_open - open file and return file pointer
  *
  * @filename:	path to open
@@ -921,9 +939,16 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
  */
 struct file *filp_open(const char *filename, int flags, umode_t mode)
 {
-	struct open_flags op;
-	int lookup = build_open_flags(flags, mode, &op);
-	return do_filp_open(AT_FDCWD, filename, &op, lookup);
+	struct file *filp;
+	struct getname_info *ginfo = kzalloc(sizeof(*ginfo), GFP_KERNEL);
+
+	if (!ginfo)
+		return ERR_PTR(-ENOMEM);
+
+	ginfo->name = filename;
+	filp = filp_open_ginfo(ginfo, flags, mode);
+	putname(ginfo);
+	return filp;
 }
 EXPORT_SYMBOL(filp_open);
 
@@ -951,7 +976,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 	if (!IS_ERR(tmp)) {
 		fd = get_unused_fd_flags(flags);
 		if (fd >= 0) {
-			struct file *f = do_filp_open(dfd, tmp->name, &op, lookup);
+			struct file *f = do_filp_open(dfd, tmp, &op, lookup);
 			if (IS_ERR(f)) {
 				put_unused_fd(fd);
 				fd = PTR_ERR(f);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1b0d0fb..a37b0c1 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2200,6 +2200,7 @@ extern int do_fallocate(struct file *file, int mode, loff_t offset,
 			loff_t len);
 extern long do_sys_open(int dfd, const char __user *filename, int flags,
 			umode_t mode);
+extern struct file *filp_open_ginfo(struct getname_info *, int, umode_t);
 extern struct file *filp_open(const char *, int, umode_t);
 extern struct file *file_open_root(struct dentry *, struct vfsmount *,
 				   const char *, int);
diff --git a/kernel/acct.c b/kernel/acct.c
index 3943608..a6b3e14 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -193,7 +193,7 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file,
 	}
 }
 
-static int acct_on(const char *name)
+static int acct_on(struct getname_info *pathname)
 {
 	struct file *file;
 	struct vfsmount *mnt;
@@ -201,7 +201,7 @@ static int acct_on(const char *name)
 	struct bsd_acct_struct *acct = NULL;
 
 	/* Difference from BSD - they don't do O_APPEND */
-	file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0);
+	file = filp_open_ginfo(pathname, O_WRONLY|O_APPEND|O_LARGEFILE, 0);
 	if (IS_ERR(file))
 		return PTR_ERR(file);
 
@@ -263,7 +263,7 @@ SYSCALL_DEFINE1(acct, const char __user *, name)
 		struct getname_info *tmp = getname(name);
 		if (IS_ERR(tmp))
 			return (PTR_ERR(tmp));
-		error = acct_on(tmp->name);
+		error = acct_on(tmp);
 		putname(tmp);
 	} else {
 		struct bsd_acct_struct *acct;
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 8dbf46d..0f8b1a4 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1498,7 +1498,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 	if (IS_ERR(pathname))
 		goto out;
 
-	victim = filp_open(pathname->name, O_RDWR|O_LARGEFILE, 0);
+	victim = filp_open_ginfo(pathname, O_RDWR|O_LARGEFILE, 0);
 	err = PTR_ERR(victim);
 	if (IS_ERR(victim))
 		goto out;
@@ -1966,7 +1966,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 		name = NULL;
 		goto bad_swap;
 	}
-	swap_file = filp_open(name->name, O_RDWR|O_LARGEFILE, 0);
+	swap_file = filp_open_ginfo(name, O_RDWR|O_LARGEFILE, 0);
 	if (IS_ERR(swap_file)) {
 		error = PTR_ERR(swap_file);
 		swap_file = NULL;
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 08/10] audit: make audit_inode take getname_info
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (6 preceding siblings ...)
  2012-09-07 13:38 ` [PATCH v1 07/10] vfs: make path_openat take a getname_info pointer Jeff Layton
@ 2012-09-07 13:38 ` Jeff Layton
  2012-09-07 13:38 ` [PATCH v1 09/10] vfs: embed getname_info inside of names_cache allocation if possible Jeff Layton
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:38 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

Keep a pointer to the audit_names "slot" in getname_info.

Have all of the audit_inode callers pass a getname_info ponter to
audit_inode instead of a string pointer. If the aname field is already
populated, then we can skip walking the list altogether and just use it
directly.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/namei.c            | 16 +++++++---------
 include/linux/audit.h | 10 +++++-----
 include/linux/fs.h    |  6 ++++--
 ipc/mqueue.c          |  4 ++--
 kernel/auditsc.c      | 25 +++++++++++++++++++++----
 5 files changed, 39 insertions(+), 22 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 9ef0d00..ac57c42 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2000,8 +2000,7 @@ static int ginfo_lookup(int dfd, struct getname_info *ginfo,
 						flags | LOOKUP_REVAL, nd);
 
 	if (likely(!retval))
-		audit_inode(ginfo->name, nd->path.dentry,
-						flags & LOOKUP_PARENT);
+		audit_inode(ginfo, nd->path.dentry, flags & LOOKUP_PARENT);
 	return retval;
 }
 
@@ -2675,7 +2674,6 @@ static int do_last(struct nameidata *nd, struct path *path,
 	struct path save_parent = { .dentry = NULL, .mnt = NULL };
 	bool retried = false;
 	int error;
-	const char *pathname = ginfo->name;
 
 	nd->flags &= ~LOOKUP_PARENT;
 	nd->flags |= op->intent;
@@ -2691,7 +2689,7 @@ static int do_last(struct nameidata *nd, struct path *path,
 		error = complete_walk(nd);
 		if (error)
 			return error;
-		audit_inode(pathname, nd->path.dentry, 0);
+		audit_inode(ginfo, nd->path.dentry, 0);
 		if (open_flag & O_CREAT) {
 			error = -EISDIR;
 			goto out;
@@ -2701,7 +2699,7 @@ static int do_last(struct nameidata *nd, struct path *path,
 		error = complete_walk(nd);
 		if (error)
 			return error;
-		audit_inode(pathname, dir, 0);
+		audit_inode(ginfo, dir, 0);
 		goto finish_open;
 	}
 
@@ -2730,7 +2728,7 @@ static int do_last(struct nameidata *nd, struct path *path,
 		if (error)
 			return error;
 
-		audit_inode(pathname, dir, 0);
+		audit_inode(ginfo, dir, 0);
 		error = -EISDIR;
 		/* trailing slashes? */
 		if (nd->last.name[nd->last.len])
@@ -2760,7 +2758,7 @@ retry_lookup:
 		    !S_ISREG(file->f_path.dentry->d_inode->i_mode))
 			will_truncate = false;
 
-		audit_inode(pathname, file->f_path.dentry, 0);
+		audit_inode(ginfo, file->f_path.dentry, 0);
 		goto opened;
 	}
 
@@ -2777,7 +2775,7 @@ retry_lookup:
 	 * create/update audit record if it already exists.
 	 */
 	if (path->dentry->d_inode)
-		audit_inode(pathname, path->dentry, 0);
+		audit_inode(ginfo, path->dentry, 0);
 
 	/*
 	 * If atomic_open() acquired write access it is dropped now due to
@@ -2842,7 +2840,7 @@ finish_lookup:
 	error = -ENOTDIR;
 	if ((nd->flags & LOOKUP_DIRECTORY) && !nd->inode->i_op->lookup)
 		goto out;
-	audit_inode(pathname, nd->path.dentry, 0);
+	audit_inode(ginfo, nd->path.dentry, 0);
 finish_open:
 	if (!S_ISREG(nd->inode->i_mode))
 		will_truncate = false;
diff --git a/include/linux/audit.h b/include/linux/audit.h
index ea58de3..481cf75 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -472,7 +472,7 @@ extern void __audit_syscall_exit(int ret_success, long ret_value);
 extern struct getname_info *__audit_reusename(const __user char *uptr);
 extern void __audit_getname(struct getname_info *ginfo);
 extern void audit_putname(struct getname_info *ginfo);
-extern void __audit_inode(const char *name, const struct dentry *dentry,
+extern void __audit_inode(struct getname_info *ginfo, const struct dentry *dentry,
 				unsigned int parent);
 extern void __audit_inode_child(const struct inode *parent,
 				const struct dentry *dentry,
@@ -517,10 +517,10 @@ static inline void audit_getname(struct getname_info *name)
 	if (unlikely(!audit_dummy_context()))
 		__audit_getname(name);
 }
-static inline void audit_inode(const char *name, const struct dentry *dentry,
+static inline void audit_inode(struct getname_info *ginfo, const struct dentry *dentry,
 				unsigned int parent) {
 	if (unlikely(!audit_dummy_context()))
-		__audit_inode(name, dentry, parent);
+		__audit_inode(ginfo, dentry, parent);
 }
 static inline void audit_inode_child(const struct inode *parent,
 				     const struct dentry *dentry,
@@ -652,9 +652,9 @@ extern int audit_signals;
 #define audit_reusename(g) ({ NULL; })
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
-#define __audit_inode(n,d,p) do { ; } while (0)
+#define __audit_inode(g,d,p) do { ; } while (0)
 #define __audit_inode_child(p,d) do { ; } while (0)
-#define audit_inode(n,d,p) do { ; } while (0)
+#define audit_inode(g,d,p) do { ; } while (0)
 #define audit_inode_child(p,d,t) do { ; } while (0)
 #define audit_core_dumps(i) do { ; } while (0)
 #define audit_seccomp(i,s,c) do { ; } while (0)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a37b0c1..4ce38f2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2189,9 +2189,11 @@ static inline int break_lease(struct inode *inode, unsigned int mode)
 #endif /* CONFIG_FILE_LOCKING */
 
 /* fs/open.c */
+struct audit_names;
 struct getname_info {
-	const char *name;		/* pointer to actual string */
-	const __user char *uptr;	/* original userland pointer */
+	const char		*name;	/* pointer to actual string */
+	const __user char	*uptr;	/* original userland pointer */
+	struct audit_names	*aname;
 };
 
 extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 2071a27..07d489e 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -805,7 +805,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
 
 	if (oflag & O_CREAT) {
 		if (path.dentry->d_inode) {	/* entry already exists */
-			audit_inode(name->name, path.dentry, 0);
+			audit_inode(name, path.dentry, 0);
 			if (oflag & O_EXCL) {
 				error = -EEXIST;
 				goto out;
@@ -825,7 +825,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
 			error = -ENOENT;
 			goto out;
 		}
-		audit_inode(name->name, path.dentry, 0);
+		audit_inode(name, path.dentry, 0);
 		filp = do_open(&path, oflag);
 	}
 
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 22121bb..4e634fb 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2084,6 +2084,7 @@ void __audit_getname(struct getname_info *ginfo)
 	n->ginfo = ginfo;
 	n->name_len = AUDIT_NAME_FULL;
 	n->name_put = true;
+	ginfo->aname = n;
 
 	if (!context->pwd.dentry)
 		get_fs_pwd(current->fs, &context->pwd);
@@ -2170,11 +2171,11 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent
 
 /**
  * __audit_inode - store the inode and device from a lookup
- * @name: name being audited
+ * @ginfo: getname_info for name being audited
  * @dentry: dentry being audited
  * @parent: does this dentry represent the parent?
  */
-void __audit_inode(const char *name, const struct dentry *dentry,
+void __audit_inode(struct getname_info *ginfo, const struct dentry *dentry,
 		   unsigned int parent)
 {
 	struct audit_context *context = current->audit_context;
@@ -2184,12 +2185,28 @@ void __audit_inode(const char *name, const struct dentry *dentry,
 	if (!context->in_syscall)
 		return;
 
-	if (!name)
+	if (!ginfo || !ginfo->name)
 		goto out_alloc;
 
+	/*
+	 * If we have a pointer to an audit_names entry already, then we can
+	 * just use it directly if the type is correct.
+	 */
+	n = ginfo->aname;
+	if (n) {
+		if (parent) {
+			if (n->type == AUDIT_TYPE_PARENT ||
+			    n->type == AUDIT_TYPE_UNKNOWN)
+				goto out;
+		} else {
+			if (n->type != AUDIT_TYPE_PARENT)
+				goto out;
+		}
+	}
+
 	list_for_each_entry_reverse(n, &context->names_list, list) {
 		/* does the name pointer match? */
-		if (!n->ginfo || n->ginfo->name != name)
+		if (!n->ginfo || n->ginfo->name != ginfo->name)
 			continue;
 
 		/* match the correct record type */
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 09/10] vfs: embed getname_info inside of names_cache allocation if possible
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (7 preceding siblings ...)
  2012-09-07 13:38 ` [PATCH v1 08/10] audit: make audit_inode take getname_info Jeff Layton
@ 2012-09-07 13:38 ` Jeff Layton
  2012-09-07 13:38 ` [PATCH v1 10/10] vfs: unexport getname and putname symbols Jeff Layton
  2012-09-07 21:26 ` [PATCH v1 00/10] vfs: getname/putname overhaul Andi Kleen
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:38 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

In the common case where a name is much smaller than PATH_MAX, an extra
allocation for struct getname_info is unnecessary. Before allocating a
separate one, try to embed the getname_info inside the buffer first. If
it turns out that that's not long enough, then fall back to allocating a
separate getname_info struct and redoing the copy.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/namei.c         | 69 ++++++++++++++++++++++++++++++++++++++----------------
 include/linux/fs.h |  1 +
 2 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index ac57c42..52feb98 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -119,36 +119,65 @@
  */
 void final_putname(struct getname_info *ginfo)
 {
-	__putname(ginfo->name);
-	kfree(ginfo);
+	if (ginfo->separate) {
+		__putname(ginfo->name);
+		kfree(ginfo);
+	} else {
+		__putname(ginfo);
+	}
 }
 
+#define EMBEDDED_NAME_MAX	(PATH_MAX - sizeof(struct getname_info))
+
 static struct getname_info *
 getname_flags(const char __user *filename, int flags, int *empty)
 {
 	struct getname_info *result, *err;
-	char *kname;
 	int len;
+	long max;
+	char *kname;
 
-	/* FIXME: create dedicated slabcache? */
-	result = kzalloc(sizeof(*result), GFP_KERNEL);
+	result = __getname();
 	if (unlikely(!result))
 		return ERR_PTR(-ENOMEM);
 
-	kname = __getname();
-	if (unlikely(!kname)) {
-		err = ERR_PTR(-ENOMEM);
-		goto error_free_ginfo;
-	}
-
+	/*
+	 * First, try to embed the getname_info inside the names_cache
+	 * allocation
+	 */
+	kname = (char *)result + sizeof(*result);
 	result->name = kname;
-	result->uptr = filename;
-	len = strncpy_from_user(kname, filename, PATH_MAX);
+	result->separate = false;
+	max = EMBEDDED_NAME_MAX;
+
+recopy:
+	len = strncpy_from_user(kname, filename, max);
 	if (unlikely(len < 0)) {
 		err = ERR_PTR(len);
 		goto error;
 	}
 
+	/*
+	 * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a
+	 * separate getname_info struct so we can dedicate the entire
+	 * names_cache allocation for the pathname, and re-do the copy from
+	 * userland.
+	 */
+	if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) {
+		kname = (char *)result;
+
+		result = kzalloc(sizeof(*result), GFP_KERNEL);
+		if (!result) {
+			err = ERR_PTR(-ENOMEM);
+			result = (struct getname_info *)kname;
+			goto error;
+		}
+		result->name = kname;
+		result->separate = true;
+		max = PATH_MAX;
+		goto recopy;
+	}
+
 	/* The empty path is special. */
 	if (unlikely(!len)) {
 		if (empty)
@@ -159,15 +188,15 @@ getname_flags(const char __user *filename, int flags, int *empty)
 	}
 
 	err = ERR_PTR(-ENAMETOOLONG);
-	if (likely(len < PATH_MAX)) {
-		audit_getname(result);
-		return result;
-	}
+	if (unlikely(len >= PATH_MAX))
+		goto error;
+
+	result->uptr = filename;
+	audit_getname(result);
+	return result;
 
 error:
-	__putname(kname);
-error_free_ginfo:
-	kfree(result);
+	final_putname(result);
 	return err;
 }
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4ce38f2..a3c2c17 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2194,6 +2194,7 @@ struct getname_info {
 	const char		*name;	/* pointer to actual string */
 	const __user char	*uptr;	/* original userland pointer */
 	struct audit_names	*aname;
+	bool			separate; /* should "name" be freed? */
 };
 
 extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 10/10] vfs: unexport getname and putname symbols
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (8 preceding siblings ...)
  2012-09-07 13:38 ` [PATCH v1 09/10] vfs: embed getname_info inside of names_cache allocation if possible Jeff Layton
@ 2012-09-07 13:38 ` Jeff Layton
  2012-09-07 21:26 ` [PATCH v1 00/10] vfs: getname/putname overhaul Andi Kleen
  10 siblings, 0 replies; 16+ messages in thread
From: Jeff Layton @ 2012-09-07 13:38 UTC (permalink / raw)
  To: viro; +Cc: eparis, linux-audit, linux-fsdevel, linux-kernel

I see no callers in module code.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/namei.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 52feb98..6f9040c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -205,7 +205,6 @@ getname(const char __user * filename)
 {
 	return getname_flags(filename, 0, NULL);
 }
-EXPORT_SYMBOL(getname);
 
 void putname(struct getname_info *ginfo)
 {
@@ -215,7 +214,6 @@ void putname(struct getname_info *ginfo)
 #endif
 	final_putname(ginfo);
 }
-EXPORT_SYMBOL(putname);
 
 static int check_acl(struct inode *inode, int mask)
 {
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 00/10] vfs: getname/putname overhaul
  2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
                   ` (9 preceding siblings ...)
  2012-09-07 13:38 ` [PATCH v1 10/10] vfs: unexport getname and putname symbols Jeff Layton
@ 2012-09-07 21:26 ` Andi Kleen
  2012-09-08  0:54   ` Jeff Layton
  10 siblings, 1 reply; 16+ messages in thread
From: Andi Kleen @ 2012-09-07 21:26 UTC (permalink / raw)
  To: Jeff Layton; +Cc: viro, eparis, linux-audit, linux-fsdevel, linux-kernel

Jeff Layton <jlayton@redhat.com> writes:

> This patchset is a first pass at overhauling the getname/putname
> interface to use a struct. The idea here is to add a new getname_info
> struct that allow us to pass around some auxillary info along with
> the string that getname() returns.

Couldn't you just use some of the free pointers in struct page?
(lru etc.)

-Andi

-- 
ak@linux.intel.com -- Speaking for myself only

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 00/10] vfs: getname/putname overhaul
  2012-09-07 21:26 ` [PATCH v1 00/10] vfs: getname/putname overhaul Andi Kleen
@ 2012-09-08  0:54   ` Jeff Layton
  2012-09-08  3:08     ` Andi Kleen
  0 siblings, 1 reply; 16+ messages in thread
From: Jeff Layton @ 2012-09-08  0:54 UTC (permalink / raw)
  To: Andi Kleen; +Cc: viro, eparis, linux-audit, linux-fsdevel, linux-kernel

On Fri, 07 Sep 2012 14:26:56 -0700
Andi Kleen <andi@firstfloor.org> wrote:

> Jeff Layton <jlayton@redhat.com> writes:
> 
> > This patchset is a first pass at overhauling the getname/putname
> > interface to use a struct. The idea here is to add a new getname_info
> > struct that allow us to pass around some auxillary info along with
> > the string that getname() returns.
> 
> Couldn't you just use some of the free pointers in struct page?
> (lru etc.)
> 
> -Andi
> 

We could do that if these were page allocations. They're not, however.
__getname() does a PATH_MAX size allocation out of a slabcache. I get
the impression that at one point, this was done with page allocations.
I assume that this was changed over to a slabcache since not all arches
have a 4k PAGE_SIZE.

That's not a bad idea though. We could switch __getname over to doing a
page allocation instead, but it would mean quite a bit of wasted space
on arches with >4k pages. That said, these allocations are generally
quite short-lived so maybe that's not such a big deal.
-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 00/10] vfs: getname/putname overhaul
  2012-09-08  0:54   ` Jeff Layton
@ 2012-09-08  3:08     ` Andi Kleen
  2012-09-08 11:24       ` Jeff Layton
  0 siblings, 1 reply; 16+ messages in thread
From: Andi Kleen @ 2012-09-08  3:08 UTC (permalink / raw)
  To: Jeff Layton
  Cc: Andi Kleen, viro, eparis, linux-audit, linux-fsdevel, linux-kernel

On Fri, Sep 07, 2012 at 08:54:18PM -0400, Jeff Layton wrote:
> On Fri, 07 Sep 2012 14:26:56 -0700
> Andi Kleen <andi@firstfloor.org> wrote:
> 
> > Jeff Layton <jlayton@redhat.com> writes:
> > 
> > > This patchset is a first pass at overhauling the getname/putname
> > > interface to use a struct. The idea here is to add a new getname_info
> > > struct that allow us to pass around some auxillary info along with
> > > the string that getname() returns.
> > 
> > Couldn't you just use some of the free pointers in struct page?
> > (lru etc.)
> > 
> > -Andi
> > 
> 
> We could do that if these were page allocations. They're not, however.
> __getname() does a PATH_MAX size allocation out of a slabcache. I get

Ok I suppose slab is faster. In this case it's better to track
separately I agree.

-Andi

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 00/10] vfs: getname/putname overhaul
  2012-09-08  3:08     ` Andi Kleen
@ 2012-09-08 11:24       ` Jeff Layton
  2012-09-08 15:38         ` Andi Kleen
  0 siblings, 1 reply; 16+ messages in thread
From: Jeff Layton @ 2012-09-08 11:24 UTC (permalink / raw)
  To: Andi Kleen; +Cc: viro, eparis, linux-audit, linux-fsdevel, linux-kernel

On Sat, 8 Sep 2012 05:08:50 +0200
Andi Kleen <andi@firstfloor.org> wrote:

> On Fri, Sep 07, 2012 at 08:54:18PM -0400, Jeff Layton wrote:
> > On Fri, 07 Sep 2012 14:26:56 -0700
> > Andi Kleen <andi@firstfloor.org> wrote:
> > 
> > > Jeff Layton <jlayton@redhat.com> writes:
> > > 
> > > > This patchset is a first pass at overhauling the getname/putname
> > > > interface to use a struct. The idea here is to add a new getname_info
> > > > struct that allow us to pass around some auxillary info along with
> > > > the string that getname() returns.
> > > 
> > > Couldn't you just use some of the free pointers in struct page?
> > > (lru etc.)
> > > 
> > > -Andi
> > > 
> > 
> > We could do that if these were page allocations. They're not, however.
> > __getname() does a PATH_MAX size allocation out of a slabcache. I get
> 
> Ok I suppose slab is faster. In this case it's better to track
> separately I agree.
> 

Ummm...stupid question...

I could see that allocating out of the slab would mean less waste when
you have >4k pages, but why would it be faster than just allocating a
page directly?

Also, by "track separately" do you mean that you think I should drop
patch 9 in this series and just do two allocations for a getname in all
cases?

-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 00/10] vfs: getname/putname overhaul
  2012-09-08 11:24       ` Jeff Layton
@ 2012-09-08 15:38         ` Andi Kleen
  0 siblings, 0 replies; 16+ messages in thread
From: Andi Kleen @ 2012-09-08 15:38 UTC (permalink / raw)
  To: Jeff Layton
  Cc: Andi Kleen, viro, eparis, linux-audit, linux-fsdevel, linux-kernel

> I could see that allocating out of the slab would mean less waste when
> you have >4k pages, but why would it be faster than just allocating a
> page directly?

The slab allocator is far more optimized than the page allocator 
for the fast path (in fact page could need some major diet in its fast path)

At least that was the established wisdom some time ago, haven't recently
rechecked it.


> Also, by "track separately" do you mean that you think I should drop
> patch 9 in this series and just do two allocations for a getname in all
> cases?

Just meant what you already do in the patchkit.

-Andi


-- 
ak@linux.intel.com -- Speaking for myself only.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2012-09-08 15:38 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-09-07 13:37 [PATCH v1 00/10] vfs: getname/putname overhaul Jeff Layton
2012-09-07 13:37 ` [PATCH v1 01/10] vfs: allocate page instead of names_cache buffer in mount_block_root Jeff Layton
2012-09-07 13:37 ` [PATCH v1 02/10] vfs: make dir_name arg to do_mount a const char * Jeff Layton
2012-09-07 13:37 ` [PATCH v1 03/10] acct: constify the name arg to acct_on Jeff Layton
2012-09-07 13:37 ` [PATCH v1 04/10] vfs: define getname_info struct and have getname() return it Jeff Layton
2012-09-07 13:38 ` [PATCH v1 05/10] audit: allow audit code to satisfy getname requests from its names_list Jeff Layton
2012-09-07 13:38 ` [PATCH v1 06/10] vfs: turn do_path_lookup into wrapper around getname_info variant Jeff Layton
2012-09-07 13:38 ` [PATCH v1 07/10] vfs: make path_openat take a getname_info pointer Jeff Layton
2012-09-07 13:38 ` [PATCH v1 08/10] audit: make audit_inode take getname_info Jeff Layton
2012-09-07 13:38 ` [PATCH v1 09/10] vfs: embed getname_info inside of names_cache allocation if possible Jeff Layton
2012-09-07 13:38 ` [PATCH v1 10/10] vfs: unexport getname and putname symbols Jeff Layton
2012-09-07 21:26 ` [PATCH v1 00/10] vfs: getname/putname overhaul Andi Kleen
2012-09-08  0:54   ` Jeff Layton
2012-09-08  3:08     ` Andi Kleen
2012-09-08 11:24       ` Jeff Layton
2012-09-08 15:38         ` Andi Kleen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).