diff options
author | Andy Whitcroft <apw@canonical.com> | 2011-11-02 04:44:39 -0400 |
---|---|---|
committer | Christoph Hellwig <hch@serles.lst.de> | 2011-11-02 07:53:42 -0400 |
commit | 1fa1e7f615f4d3ae436fa319af6e4eebdd4026a8 (patch) | |
tree | aa3521aaa762424cc7fb38dbf924a34dac1b03c2 /fs/namei.c | |
parent | 32096ea1aac14e6f29d4744924092eca52b937b0 (diff) |
readlinkat: ensure we return ENOENT for the empty pathname for normal lookups
Since the commit below which added O_PATH support to the *at() calls, the
error return for readlink/readlinkat for the empty pathname has switched
from ENOENT to EINVAL:
commit 65cfc6722361570bfe255698d9cd4dccaf47570d
Author: Al Viro <viro@zeniv.linux.org.uk>
Date: Sun Mar 13 15:56:26 2011 -0400
readlinkat(), fchownat() and fstatat() with empty relative pathnames
This is both unexpected for userspace and makes readlink/readlinkat
inconsistant with all other interfaces; and inconsistant with our stated
return for these pathnames.
As the readlinkat call does not have a flags parameter we cannot use the
AT_EMPTY_PATH approach used in the other calls. Therefore expose whether
the original path is infact entry via a new user_path_at_empty() path
lookup function. Use this to determine whether to default to EINVAL or
ENOENT for failures.
Addresses http://bugs.launchpad.net/bugs/817187
[akpm@linux-foundation.org: remove unused getname_flags()]
Signed-off-by: Andy Whitcroft <apw@canonical.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: <stable@kernel.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/fs/namei.c b/fs/namei.c index 7657be4352bf..ac6d214da827 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -137,7 +137,7 @@ static int do_getname(const char __user *filename, char *page) | |||
137 | return retval; | 137 | return retval; |
138 | } | 138 | } |
139 | 139 | ||
140 | static char *getname_flags(const char __user * filename, int flags) | 140 | static char *getname_flags(const char __user *filename, int flags, int *empty) |
141 | { | 141 | { |
142 | char *tmp, *result; | 142 | char *tmp, *result; |
143 | 143 | ||
@@ -148,6 +148,8 @@ static char *getname_flags(const char __user * filename, int flags) | |||
148 | 148 | ||
149 | result = tmp; | 149 | result = tmp; |
150 | if (retval < 0) { | 150 | if (retval < 0) { |
151 | if (retval == -ENOENT && empty) | ||
152 | *empty = 1; | ||
151 | if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) { | 153 | if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) { |
152 | __putname(tmp); | 154 | __putname(tmp); |
153 | result = ERR_PTR(retval); | 155 | result = ERR_PTR(retval); |
@@ -160,7 +162,7 @@ static char *getname_flags(const char __user * filename, int flags) | |||
160 | 162 | ||
161 | char *getname(const char __user * filename) | 163 | char *getname(const char __user * filename) |
162 | { | 164 | { |
163 | return getname_flags(filename, 0); | 165 | return getname_flags(filename, 0, 0); |
164 | } | 166 | } |
165 | 167 | ||
166 | #ifdef CONFIG_AUDITSYSCALL | 168 | #ifdef CONFIG_AUDITSYSCALL |
@@ -1798,11 +1800,11 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) | |||
1798 | return __lookup_hash(&this, base, NULL); | 1800 | return __lookup_hash(&this, base, NULL); |
1799 | } | 1801 | } |
1800 | 1802 | ||
1801 | int user_path_at(int dfd, const char __user *name, unsigned flags, | 1803 | int user_path_at_empty(int dfd, const char __user *name, unsigned flags, |
1802 | struct path *path) | 1804 | struct path *path, int *empty) |
1803 | { | 1805 | { |
1804 | struct nameidata nd; | 1806 | struct nameidata nd; |
1805 | char *tmp = getname_flags(name, flags); | 1807 | char *tmp = getname_flags(name, flags, empty); |
1806 | int err = PTR_ERR(tmp); | 1808 | int err = PTR_ERR(tmp); |
1807 | if (!IS_ERR(tmp)) { | 1809 | if (!IS_ERR(tmp)) { |
1808 | 1810 | ||
@@ -1816,6 +1818,12 @@ int user_path_at(int dfd, const char __user *name, unsigned flags, | |||
1816 | return err; | 1818 | return err; |
1817 | } | 1819 | } |
1818 | 1820 | ||
1821 | int user_path_at(int dfd, const char __user *name, unsigned flags, | ||
1822 | struct path *path) | ||
1823 | { | ||
1824 | return user_path_at_empty(dfd, name, flags, path, 0); | ||
1825 | } | ||
1826 | |||
1819 | static int user_path_parent(int dfd, const char __user *path, | 1827 | static int user_path_parent(int dfd, const char __user *path, |
1820 | struct nameidata *nd, char **name) | 1828 | struct nameidata *nd, char **name) |
1821 | { | 1829 | { |