diff options
Diffstat (limited to 'fs/kernfs/file.c')
-rw-r--r-- | fs/kernfs/file.c | 51 |
1 files changed, 29 insertions, 22 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 7247252ee9b1..e1574008adc9 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c | |||
@@ -190,15 +190,16 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of, | |||
190 | char *buf; | 190 | char *buf; |
191 | 191 | ||
192 | buf = of->prealloc_buf; | 192 | buf = of->prealloc_buf; |
193 | if (!buf) | 193 | if (buf) |
194 | mutex_lock(&of->prealloc_mutex); | ||
195 | else | ||
194 | buf = kmalloc(len, GFP_KERNEL); | 196 | buf = kmalloc(len, GFP_KERNEL); |
195 | if (!buf) | 197 | if (!buf) |
196 | return -ENOMEM; | 198 | return -ENOMEM; |
197 | 199 | ||
198 | /* | 200 | /* |
199 | * @of->mutex nests outside active ref and is used both to ensure that | 201 | * @of->mutex nests outside active ref and is used both to ensure that |
200 | * the ops aren't called concurrently for the same open file, and | 202 | * the ops aren't called concurrently for the same open file. |
201 | * to provide exclusive access to ->prealloc_buf (when that exists). | ||
202 | */ | 203 | */ |
203 | mutex_lock(&of->mutex); | 204 | mutex_lock(&of->mutex); |
204 | if (!kernfs_get_active(of->kn)) { | 205 | if (!kernfs_get_active(of->kn)) { |
@@ -214,21 +215,23 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of, | |||
214 | else | 215 | else |
215 | len = -EINVAL; | 216 | len = -EINVAL; |
216 | 217 | ||
218 | kernfs_put_active(of->kn); | ||
219 | mutex_unlock(&of->mutex); | ||
220 | |||
217 | if (len < 0) | 221 | if (len < 0) |
218 | goto out_unlock; | 222 | goto out_free; |
219 | 223 | ||
220 | if (copy_to_user(user_buf, buf, len)) { | 224 | if (copy_to_user(user_buf, buf, len)) { |
221 | len = -EFAULT; | 225 | len = -EFAULT; |
222 | goto out_unlock; | 226 | goto out_free; |
223 | } | 227 | } |
224 | 228 | ||
225 | *ppos += len; | 229 | *ppos += len; |
226 | 230 | ||
227 | out_unlock: | ||
228 | kernfs_put_active(of->kn); | ||
229 | mutex_unlock(&of->mutex); | ||
230 | out_free: | 231 | out_free: |
231 | if (buf != of->prealloc_buf) | 232 | if (buf == of->prealloc_buf) |
233 | mutex_unlock(&of->prealloc_mutex); | ||
234 | else | ||
232 | kfree(buf); | 235 | kfree(buf); |
233 | return len; | 236 | return len; |
234 | } | 237 | } |
@@ -284,15 +287,22 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, | |||
284 | } | 287 | } |
285 | 288 | ||
286 | buf = of->prealloc_buf; | 289 | buf = of->prealloc_buf; |
287 | if (!buf) | 290 | if (buf) |
291 | mutex_lock(&of->prealloc_mutex); | ||
292 | else | ||
288 | buf = kmalloc(len + 1, GFP_KERNEL); | 293 | buf = kmalloc(len + 1, GFP_KERNEL); |
289 | if (!buf) | 294 | if (!buf) |
290 | return -ENOMEM; | 295 | return -ENOMEM; |
291 | 296 | ||
297 | if (copy_from_user(buf, user_buf, len)) { | ||
298 | len = -EFAULT; | ||
299 | goto out_free; | ||
300 | } | ||
301 | buf[len] = '\0'; /* guarantee string termination */ | ||
302 | |||
292 | /* | 303 | /* |
293 | * @of->mutex nests outside active ref and is used both to ensure that | 304 | * @of->mutex nests outside active ref and is used both to ensure that |
294 | * the ops aren't called concurrently for the same open file, and | 305 | * the ops aren't called concurrently for the same open file. |
295 | * to provide exclusive access to ->prealloc_buf (when that exists). | ||
296 | */ | 306 | */ |
297 | mutex_lock(&of->mutex); | 307 | mutex_lock(&of->mutex); |
298 | if (!kernfs_get_active(of->kn)) { | 308 | if (!kernfs_get_active(of->kn)) { |
@@ -301,26 +311,22 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, | |||
301 | goto out_free; | 311 | goto out_free; |
302 | } | 312 | } |
303 | 313 | ||
304 | if (copy_from_user(buf, user_buf, len)) { | ||
305 | len = -EFAULT; | ||
306 | goto out_unlock; | ||
307 | } | ||
308 | buf[len] = '\0'; /* guarantee string termination */ | ||
309 | |||
310 | ops = kernfs_ops(of->kn); | 314 | ops = kernfs_ops(of->kn); |
311 | if (ops->write) | 315 | if (ops->write) |
312 | len = ops->write(of, buf, len, *ppos); | 316 | len = ops->write(of, buf, len, *ppos); |
313 | else | 317 | else |
314 | len = -EINVAL; | 318 | len = -EINVAL; |
315 | 319 | ||
320 | kernfs_put_active(of->kn); | ||
321 | mutex_unlock(&of->mutex); | ||
322 | |||
316 | if (len > 0) | 323 | if (len > 0) |
317 | *ppos += len; | 324 | *ppos += len; |
318 | 325 | ||
319 | out_unlock: | ||
320 | kernfs_put_active(of->kn); | ||
321 | mutex_unlock(&of->mutex); | ||
322 | out_free: | 326 | out_free: |
323 | if (buf != of->prealloc_buf) | 327 | if (buf == of->prealloc_buf) |
328 | mutex_unlock(&of->prealloc_mutex); | ||
329 | else | ||
324 | kfree(buf); | 330 | kfree(buf); |
325 | return len; | 331 | return len; |
326 | } | 332 | } |
@@ -687,6 +693,7 @@ static int kernfs_fop_open(struct inode *inode, struct file *file) | |||
687 | error = -ENOMEM; | 693 | error = -ENOMEM; |
688 | if (!of->prealloc_buf) | 694 | if (!of->prealloc_buf) |
689 | goto err_free; | 695 | goto err_free; |
696 | mutex_init(&of->prealloc_mutex); | ||
690 | } | 697 | } |
691 | 698 | ||
692 | /* | 699 | /* |