aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/kernfs/file.c63
-rw-r--r--include/linux/kernfs.h1
2 files changed, 33 insertions, 31 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index ddcb471b9cc9..8034706a7af8 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -253,55 +253,50 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
253{ 253{
254 struct kernfs_open_file *of = kernfs_of(file); 254 struct kernfs_open_file *of = kernfs_of(file);
255 const struct kernfs_ops *ops; 255 const struct kernfs_ops *ops;
256 char *buf = NULL; 256 size_t len;
257 ssize_t len; 257 char *buf;
258
259 /*
260 * @of->mutex nests outside active ref and is just to ensure that
261 * the ops aren't called concurrently for the same open file.
262 */
263 mutex_lock(&of->mutex);
264 if (!kernfs_get_active(of->kn)) {
265 mutex_unlock(&of->mutex);
266 return -ENODEV;
267 }
268
269 ops = kernfs_ops(of->kn);
270 if (!ops->write) {
271 len = -EINVAL;
272 goto out_unlock;
273 }
274 258
275 if (ops->atomic_write_len) { 259 if (of->atomic_write_len) {
276 len = count; 260 len = count;
277 if (len > ops->atomic_write_len) { 261 if (len > of->atomic_write_len)
278 len = -E2BIG; 262 return -E2BIG;
279 goto out_unlock;
280 }
281 } else { 263 } else {
282 len = min_t(size_t, count, PAGE_SIZE); 264 len = min_t(size_t, count, PAGE_SIZE);
283 } 265 }
284 266
285 buf = kmalloc(len + 1, GFP_KERNEL); 267 buf = kmalloc(len + 1, GFP_KERNEL);
286 if (!buf) { 268 if (!buf)
287 len = -ENOMEM; 269 return -ENOMEM;
288 goto out_unlock;
289 }
290 270
291 if (copy_from_user(buf, user_buf, len)) { 271 if (copy_from_user(buf, user_buf, len)) {
292 len = -EFAULT; 272 len = -EFAULT;
293 goto out_unlock; 273 goto out_free;
294 } 274 }
295 buf[len] = '\0'; /* guarantee string termination */ 275 buf[len] = '\0'; /* guarantee string termination */
296 276
297 len = ops->write(of, buf, len, *ppos); 277 /*
298out_unlock: 278 * @of->mutex nests outside active ref and is just to ensure that
279 * the ops aren't called concurrently for the same open file.
280 */
281 mutex_lock(&of->mutex);
282 if (!kernfs_get_active(of->kn)) {
283 mutex_unlock(&of->mutex);
284 len = -ENODEV;
285 goto out_free;
286 }
287
288 ops = kernfs_ops(of->kn);
289 if (ops->write)
290 len = ops->write(of, buf, len, *ppos);
291 else
292 len = -EINVAL;
293
299 kernfs_put_active(of->kn); 294 kernfs_put_active(of->kn);
300 mutex_unlock(&of->mutex); 295 mutex_unlock(&of->mutex);
301 296
302 if (len > 0) 297 if (len > 0)
303 *ppos += len; 298 *ppos += len;
304 299out_free:
305 kfree(buf); 300 kfree(buf);
306 return len; 301 return len;
307} 302}
@@ -666,6 +661,12 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
666 of->file = file; 661 of->file = file;
667 662
668 /* 663 /*
664 * Write path needs to atomic_write_len outside active reference.
665 * Cache it in open_file. See kernfs_fop_write() for details.
666 */
667 of->atomic_write_len = ops->atomic_write_len;
668
669 /*
669 * Always instantiate seq_file even if read access doesn't use 670 * Always instantiate seq_file even if read access doesn't use
670 * seq_file or is not requested. This unifies private data access 671 * seq_file or is not requested. This unifies private data access
671 * and readable regular files are the vast majority anyway. 672 * and readable regular files are the vast majority anyway.
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 09669d092748..b0122dc6f96a 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -158,6 +158,7 @@ struct kernfs_open_file {
158 int event; 158 int event;
159 struct list_head list; 159 struct list_head list;
160 160
161 size_t atomic_write_len;
161 bool mmapped; 162 bool mmapped;
162 const struct vm_operations_struct *vm_ops; 163 const struct vm_operations_struct *vm_ops;
163}; 164};