diff options
Diffstat (limited to 'fs/configfs/file.c')
-rw-r--r-- | fs/configfs/file.c | 255 |
1 files changed, 246 insertions, 9 deletions
diff --git a/fs/configfs/file.c b/fs/configfs/file.c index d39099ea7df7..3687187c8ea5 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/module.h> | 28 | #include <linux/module.h> |
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/mutex.h> | 30 | #include <linux/mutex.h> |
31 | #include <linux/vmalloc.h> | ||
31 | #include <asm/uaccess.h> | 32 | #include <asm/uaccess.h> |
32 | 33 | ||
33 | #include <linux/configfs.h> | 34 | #include <linux/configfs.h> |
@@ -48,6 +49,10 @@ struct configfs_buffer { | |||
48 | struct configfs_item_operations * ops; | 49 | struct configfs_item_operations * ops; |
49 | struct mutex mutex; | 50 | struct mutex mutex; |
50 | int needs_read_fill; | 51 | int needs_read_fill; |
52 | bool read_in_progress; | ||
53 | bool write_in_progress; | ||
54 | char *bin_buffer; | ||
55 | int bin_buffer_size; | ||
51 | }; | 56 | }; |
52 | 57 | ||
53 | 58 | ||
@@ -123,6 +128,87 @@ out: | |||
123 | return retval; | 128 | return retval; |
124 | } | 129 | } |
125 | 130 | ||
131 | /** | ||
132 | * configfs_read_bin_file - read a binary attribute. | ||
133 | * @file: file pointer. | ||
134 | * @buf: buffer to fill. | ||
135 | * @count: number of bytes to read. | ||
136 | * @ppos: starting offset in file. | ||
137 | * | ||
138 | * Userspace wants to read a binary attribute file. The attribute | ||
139 | * descriptor is in the file's ->d_fsdata. The target item is in the | ||
140 | * directory's ->d_fsdata. | ||
141 | * | ||
142 | * We check whether we need to refill the buffer. If so we will | ||
143 | * call the attributes' attr->read() twice. The first time we | ||
144 | * will pass a NULL as a buffer pointer, which the attributes' method | ||
145 | * will use to return the size of the buffer required. If no error | ||
146 | * occurs we will allocate the buffer using vmalloc and call | ||
147 | * attr->read() again passing that buffer as an argument. | ||
148 | * Then we just copy to user-space using simple_read_from_buffer. | ||
149 | */ | ||
150 | |||
151 | static ssize_t | ||
152 | configfs_read_bin_file(struct file *file, char __user *buf, | ||
153 | size_t count, loff_t *ppos) | ||
154 | { | ||
155 | struct configfs_buffer *buffer = file->private_data; | ||
156 | struct dentry *dentry = file->f_path.dentry; | ||
157 | struct config_item *item = to_item(dentry->d_parent); | ||
158 | struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); | ||
159 | ssize_t retval = 0; | ||
160 | ssize_t len = min_t(size_t, count, PAGE_SIZE); | ||
161 | |||
162 | mutex_lock(&buffer->mutex); | ||
163 | |||
164 | /* we don't support switching read/write modes */ | ||
165 | if (buffer->write_in_progress) { | ||
166 | retval = -ETXTBSY; | ||
167 | goto out; | ||
168 | } | ||
169 | buffer->read_in_progress = 1; | ||
170 | |||
171 | if (buffer->needs_read_fill) { | ||
172 | /* perform first read with buf == NULL to get extent */ | ||
173 | len = bin_attr->read(item, NULL, 0); | ||
174 | if (len <= 0) { | ||
175 | retval = len; | ||
176 | goto out; | ||
177 | } | ||
178 | |||
179 | /* do not exceed the maximum value */ | ||
180 | if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) { | ||
181 | retval = -EFBIG; | ||
182 | goto out; | ||
183 | } | ||
184 | |||
185 | buffer->bin_buffer = vmalloc(len); | ||
186 | if (buffer->bin_buffer == NULL) { | ||
187 | retval = -ENOMEM; | ||
188 | goto out; | ||
189 | } | ||
190 | buffer->bin_buffer_size = len; | ||
191 | |||
192 | /* perform second read to fill buffer */ | ||
193 | len = bin_attr->read(item, buffer->bin_buffer, len); | ||
194 | if (len < 0) { | ||
195 | retval = len; | ||
196 | vfree(buffer->bin_buffer); | ||
197 | buffer->bin_buffer_size = 0; | ||
198 | buffer->bin_buffer = NULL; | ||
199 | goto out; | ||
200 | } | ||
201 | |||
202 | buffer->needs_read_fill = 0; | ||
203 | } | ||
204 | |||
205 | retval = simple_read_from_buffer(buf, count, ppos, buffer->bin_buffer, | ||
206 | buffer->bin_buffer_size); | ||
207 | out: | ||
208 | mutex_unlock(&buffer->mutex); | ||
209 | return retval; | ||
210 | } | ||
211 | |||
126 | 212 | ||
127 | /** | 213 | /** |
128 | * fill_write_buffer - copy buffer from userspace. | 214 | * fill_write_buffer - copy buffer from userspace. |
@@ -209,10 +295,80 @@ configfs_write_file(struct file *file, const char __user *buf, size_t count, lof | |||
209 | return len; | 295 | return len; |
210 | } | 296 | } |
211 | 297 | ||
212 | static int check_perm(struct inode * inode, struct file * file) | 298 | /** |
299 | * configfs_write_bin_file - write a binary attribute. | ||
300 | * @file: file pointer | ||
301 | * @buf: data to write | ||
302 | * @count: number of bytes | ||
303 | * @ppos: starting offset | ||
304 | * | ||
305 | * Writing to a binary attribute file is similar to a normal read. | ||
306 | * We buffer the consecutive writes (binary attribute files do not | ||
307 | * support lseek) in a continuously growing buffer, but we don't | ||
308 | * commit until the close of the file. | ||
309 | */ | ||
310 | |||
311 | static ssize_t | ||
312 | configfs_write_bin_file(struct file *file, const char __user *buf, | ||
313 | size_t count, loff_t *ppos) | ||
314 | { | ||
315 | struct configfs_buffer *buffer = file->private_data; | ||
316 | struct dentry *dentry = file->f_path.dentry; | ||
317 | struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); | ||
318 | void *tbuf = NULL; | ||
319 | ssize_t len; | ||
320 | |||
321 | mutex_lock(&buffer->mutex); | ||
322 | |||
323 | /* we don't support switching read/write modes */ | ||
324 | if (buffer->read_in_progress) { | ||
325 | len = -ETXTBSY; | ||
326 | goto out; | ||
327 | } | ||
328 | buffer->write_in_progress = 1; | ||
329 | |||
330 | /* buffer grows? */ | ||
331 | if (*ppos + count > buffer->bin_buffer_size) { | ||
332 | |||
333 | if (bin_attr->cb_max_size && | ||
334 | *ppos + count > bin_attr->cb_max_size) { | ||
335 | len = -EFBIG; | ||
336 | } | ||
337 | |||
338 | tbuf = vmalloc(*ppos + count); | ||
339 | if (tbuf == NULL) { | ||
340 | len = -ENOMEM; | ||
341 | goto out; | ||
342 | } | ||
343 | |||
344 | /* copy old contents */ | ||
345 | if (buffer->bin_buffer) { | ||
346 | memcpy(tbuf, buffer->bin_buffer, | ||
347 | buffer->bin_buffer_size); | ||
348 | vfree(buffer->bin_buffer); | ||
349 | } | ||
350 | |||
351 | /* clear the new area */ | ||
352 | memset(tbuf + buffer->bin_buffer_size, 0, | ||
353 | *ppos + count - buffer->bin_buffer_size); | ||
354 | buffer->bin_buffer = tbuf; | ||
355 | buffer->bin_buffer_size = *ppos + count; | ||
356 | } | ||
357 | |||
358 | len = simple_write_to_buffer(buffer->bin_buffer, | ||
359 | buffer->bin_buffer_size, ppos, buf, count); | ||
360 | if (len > 0) | ||
361 | *ppos += len; | ||
362 | out: | ||
363 | mutex_unlock(&buffer->mutex); | ||
364 | return len; | ||
365 | } | ||
366 | |||
367 | static int check_perm(struct inode * inode, struct file * file, int type) | ||
213 | { | 368 | { |
214 | struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); | 369 | struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); |
215 | struct configfs_attribute * attr = to_attr(file->f_path.dentry); | 370 | struct configfs_attribute * attr = to_attr(file->f_path.dentry); |
371 | struct configfs_bin_attribute *bin_attr = NULL; | ||
216 | struct configfs_buffer * buffer; | 372 | struct configfs_buffer * buffer; |
217 | struct configfs_item_operations * ops = NULL; | 373 | struct configfs_item_operations * ops = NULL; |
218 | int error = 0; | 374 | int error = 0; |
@@ -220,6 +376,9 @@ static int check_perm(struct inode * inode, struct file * file) | |||
220 | if (!item || !attr) | 376 | if (!item || !attr) |
221 | goto Einval; | 377 | goto Einval; |
222 | 378 | ||
379 | if (type & CONFIGFS_ITEM_BIN_ATTR) | ||
380 | bin_attr = to_bin_attr(file->f_path.dentry); | ||
381 | |||
223 | /* Grab the module reference for this attribute if we have one */ | 382 | /* Grab the module reference for this attribute if we have one */ |
224 | if (!try_module_get(attr->ca_owner)) { | 383 | if (!try_module_get(attr->ca_owner)) { |
225 | error = -ENODEV; | 384 | error = -ENODEV; |
@@ -236,9 +395,14 @@ static int check_perm(struct inode * inode, struct file * file) | |||
236 | * and we must have a store method. | 395 | * and we must have a store method. |
237 | */ | 396 | */ |
238 | if (file->f_mode & FMODE_WRITE) { | 397 | if (file->f_mode & FMODE_WRITE) { |
239 | if (!(inode->i_mode & S_IWUGO) || !attr->store) | 398 | if (!(inode->i_mode & S_IWUGO)) |
399 | goto Eaccess; | ||
400 | |||
401 | if ((type & CONFIGFS_ITEM_ATTR) && !attr->store) | ||
240 | goto Eaccess; | 402 | goto Eaccess; |
241 | 403 | ||
404 | if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write) | ||
405 | goto Eaccess; | ||
242 | } | 406 | } |
243 | 407 | ||
244 | /* File needs read support. | 408 | /* File needs read support. |
@@ -246,7 +410,13 @@ static int check_perm(struct inode * inode, struct file * file) | |||
246 | * must be a show method for it. | 410 | * must be a show method for it. |
247 | */ | 411 | */ |
248 | if (file->f_mode & FMODE_READ) { | 412 | if (file->f_mode & FMODE_READ) { |
249 | if (!(inode->i_mode & S_IRUGO) || !attr->show) | 413 | if (!(inode->i_mode & S_IRUGO)) |
414 | goto Eaccess; | ||
415 | |||
416 | if ((type & CONFIGFS_ITEM_ATTR) && !attr->show) | ||
417 | goto Eaccess; | ||
418 | |||
419 | if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read) | ||
250 | goto Eaccess; | 420 | goto Eaccess; |
251 | } | 421 | } |
252 | 422 | ||
@@ -260,6 +430,8 @@ static int check_perm(struct inode * inode, struct file * file) | |||
260 | } | 430 | } |
261 | mutex_init(&buffer->mutex); | 431 | mutex_init(&buffer->mutex); |
262 | buffer->needs_read_fill = 1; | 432 | buffer->needs_read_fill = 1; |
433 | buffer->read_in_progress = 0; | ||
434 | buffer->write_in_progress = 0; | ||
263 | buffer->ops = ops; | 435 | buffer->ops = ops; |
264 | file->private_data = buffer; | 436 | file->private_data = buffer; |
265 | goto Done; | 437 | goto Done; |
@@ -277,12 +449,7 @@ static int check_perm(struct inode * inode, struct file * file) | |||
277 | return error; | 449 | return error; |
278 | } | 450 | } |
279 | 451 | ||
280 | static int configfs_open_file(struct inode * inode, struct file * filp) | 452 | static int configfs_release(struct inode *inode, struct file *filp) |
281 | { | ||
282 | return check_perm(inode,filp); | ||
283 | } | ||
284 | |||
285 | static int configfs_release(struct inode * inode, struct file * filp) | ||
286 | { | 453 | { |
287 | struct config_item * item = to_item(filp->f_path.dentry->d_parent); | 454 | struct config_item * item = to_item(filp->f_path.dentry->d_parent); |
288 | struct configfs_attribute * attr = to_attr(filp->f_path.dentry); | 455 | struct configfs_attribute * attr = to_attr(filp->f_path.dentry); |
@@ -303,6 +470,47 @@ static int configfs_release(struct inode * inode, struct file * filp) | |||
303 | return 0; | 470 | return 0; |
304 | } | 471 | } |
305 | 472 | ||
473 | static int configfs_open_file(struct inode *inode, struct file *filp) | ||
474 | { | ||
475 | return check_perm(inode, filp, CONFIGFS_ITEM_ATTR); | ||
476 | } | ||
477 | |||
478 | static int configfs_open_bin_file(struct inode *inode, struct file *filp) | ||
479 | { | ||
480 | return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR); | ||
481 | } | ||
482 | |||
483 | static int configfs_release_bin_file(struct inode *inode, struct file *filp) | ||
484 | { | ||
485 | struct configfs_buffer *buffer = filp->private_data; | ||
486 | struct dentry *dentry = filp->f_path.dentry; | ||
487 | struct config_item *item = to_item(dentry->d_parent); | ||
488 | struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); | ||
489 | ssize_t len = 0; | ||
490 | int ret; | ||
491 | |||
492 | buffer->read_in_progress = 0; | ||
493 | |||
494 | if (buffer->write_in_progress) { | ||
495 | buffer->write_in_progress = 0; | ||
496 | |||
497 | len = bin_attr->write(item, buffer->bin_buffer, | ||
498 | buffer->bin_buffer_size); | ||
499 | |||
500 | /* vfree on NULL is safe */ | ||
501 | vfree(buffer->bin_buffer); | ||
502 | buffer->bin_buffer = NULL; | ||
503 | buffer->bin_buffer_size = 0; | ||
504 | buffer->needs_read_fill = 1; | ||
505 | } | ||
506 | |||
507 | ret = configfs_release(inode, filp); | ||
508 | if (len < 0) | ||
509 | return len; | ||
510 | return ret; | ||
511 | } | ||
512 | |||
513 | |||
306 | const struct file_operations configfs_file_operations = { | 514 | const struct file_operations configfs_file_operations = { |
307 | .read = configfs_read_file, | 515 | .read = configfs_read_file, |
308 | .write = configfs_write_file, | 516 | .write = configfs_write_file, |
@@ -311,6 +519,14 @@ const struct file_operations configfs_file_operations = { | |||
311 | .release = configfs_release, | 519 | .release = configfs_release, |
312 | }; | 520 | }; |
313 | 521 | ||
522 | const struct file_operations configfs_bin_file_operations = { | ||
523 | .read = configfs_read_bin_file, | ||
524 | .write = configfs_write_bin_file, | ||
525 | .llseek = NULL, /* bin file is not seekable */ | ||
526 | .open = configfs_open_bin_file, | ||
527 | .release = configfs_release_bin_file, | ||
528 | }; | ||
529 | |||
314 | /** | 530 | /** |
315 | * configfs_create_file - create an attribute file for an item. | 531 | * configfs_create_file - create an attribute file for an item. |
316 | * @item: item we're creating for. | 532 | * @item: item we're creating for. |
@@ -332,3 +548,24 @@ int configfs_create_file(struct config_item * item, const struct configfs_attrib | |||
332 | return error; | 548 | return error; |
333 | } | 549 | } |
334 | 550 | ||
551 | /** | ||
552 | * configfs_create_bin_file - create a binary attribute file for an item. | ||
553 | * @item: item we're creating for. | ||
554 | * @attr: atrribute descriptor. | ||
555 | */ | ||
556 | |||
557 | int configfs_create_bin_file(struct config_item *item, | ||
558 | const struct configfs_bin_attribute *bin_attr) | ||
559 | { | ||
560 | struct dentry *dir = item->ci_dentry; | ||
561 | struct configfs_dirent *parent_sd = dir->d_fsdata; | ||
562 | umode_t mode = (bin_attr->cb_attr.ca_mode & S_IALLUGO) | S_IFREG; | ||
563 | int error = 0; | ||
564 | |||
565 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_NORMAL); | ||
566 | error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode, | ||
567 | CONFIGFS_ITEM_BIN_ATTR); | ||
568 | mutex_unlock(&dir->d_inode->i_mutex); | ||
569 | |||
570 | return error; | ||
571 | } | ||