diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/namei.c | 69 |
1 files changed, 49 insertions, 20 deletions
diff --git a/fs/namei.c b/fs/namei.c index 80b162b142f9..d1895f308156 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -119,40 +119,69 @@ | |||
| 119 | */ | 119 | */ |
| 120 | void final_putname(struct filename *name) | 120 | void final_putname(struct filename *name) |
| 121 | { | 121 | { |
| 122 | __putname(name->name); | 122 | if (name->separate) { |
| 123 | kfree(name); | 123 | __putname(name->name); |
| 124 | kfree(name); | ||
| 125 | } else { | ||
| 126 | __putname(name); | ||
| 127 | } | ||
| 124 | } | 128 | } |
| 125 | 129 | ||
| 130 | #define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) | ||
| 131 | |||
| 126 | static struct filename * | 132 | static struct filename * |
| 127 | getname_flags(const char __user *filename, int flags, int *empty) | 133 | getname_flags(const char __user *filename, int flags, int *empty) |
| 128 | { | 134 | { |
| 129 | struct filename *result, *err; | 135 | struct filename *result, *err; |
| 130 | char *kname; | ||
| 131 | int len; | 136 | int len; |
| 137 | long max; | ||
| 138 | char *kname; | ||
| 132 | 139 | ||
| 133 | result = audit_reusename(filename); | 140 | result = audit_reusename(filename); |
| 134 | if (result) | 141 | if (result) |
| 135 | return result; | 142 | return result; |
| 136 | 143 | ||
| 137 | /* FIXME: create dedicated slabcache? */ | 144 | result = __getname(); |
| 138 | result = kzalloc(sizeof(*result), GFP_KERNEL); | ||
| 139 | if (unlikely(!result)) | 145 | if (unlikely(!result)) |
| 140 | return ERR_PTR(-ENOMEM); | 146 | return ERR_PTR(-ENOMEM); |
| 141 | 147 | ||
| 142 | kname = __getname(); | 148 | /* |
| 143 | if (unlikely(!kname)) { | 149 | * First, try to embed the struct filename inside the names_cache |
| 144 | err = ERR_PTR(-ENOMEM); | 150 | * allocation |
| 145 | goto error_free_name; | 151 | */ |
| 146 | } | 152 | kname = (char *)result + sizeof(*result); |
| 147 | |||
| 148 | result->name = kname; | 153 | result->name = kname; |
| 149 | result->uptr = filename; | 154 | result->separate = false; |
| 150 | len = strncpy_from_user(kname, filename, PATH_MAX); | 155 | max = EMBEDDED_NAME_MAX; |
| 156 | |||
| 157 | recopy: | ||
| 158 | len = strncpy_from_user(kname, filename, max); | ||
| 151 | if (unlikely(len < 0)) { | 159 | if (unlikely(len < 0)) { |
| 152 | err = ERR_PTR(len); | 160 | err = ERR_PTR(len); |
| 153 | goto error; | 161 | goto error; |
| 154 | } | 162 | } |
| 155 | 163 | ||
| 164 | /* | ||
| 165 | * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a | ||
| 166 | * separate struct filename so we can dedicate the entire | ||
| 167 | * names_cache allocation for the pathname, and re-do the copy from | ||
| 168 | * userland. | ||
| 169 | */ | ||
| 170 | if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) { | ||
| 171 | kname = (char *)result; | ||
| 172 | |||
| 173 | result = kzalloc(sizeof(*result), GFP_KERNEL); | ||
| 174 | if (!result) { | ||
| 175 | err = ERR_PTR(-ENOMEM); | ||
| 176 | result = (struct filename *)kname; | ||
| 177 | goto error; | ||
| 178 | } | ||
| 179 | result->name = kname; | ||
| 180 | result->separate = true; | ||
| 181 | max = PATH_MAX; | ||
| 182 | goto recopy; | ||
| 183 | } | ||
| 184 | |||
| 156 | /* The empty path is special. */ | 185 | /* The empty path is special. */ |
| 157 | if (unlikely(!len)) { | 186 | if (unlikely(!len)) { |
| 158 | if (empty) | 187 | if (empty) |
| @@ -163,15 +192,15 @@ getname_flags(const char __user *filename, int flags, int *empty) | |||
| 163 | } | 192 | } |
| 164 | 193 | ||
| 165 | err = ERR_PTR(-ENAMETOOLONG); | 194 | err = ERR_PTR(-ENAMETOOLONG); |
| 166 | if (likely(len < PATH_MAX)) { | 195 | if (unlikely(len >= PATH_MAX)) |
| 167 | audit_getname(result); | 196 | goto error; |
| 168 | return result; | 197 | |
| 169 | } | 198 | result->uptr = filename; |
| 199 | audit_getname(result); | ||
| 200 | return result; | ||
| 170 | 201 | ||
| 171 | error: | 202 | error: |
| 172 | __putname(kname); | 203 | final_putname(result); |
| 173 | error_free_name: | ||
| 174 | kfree(result); | ||
| 175 | return err; | 204 | return err; |
| 176 | } | 205 | } |
| 177 | 206 | ||
