diff options
Diffstat (limited to 'drivers/mtd/ubi/cdev.c')
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 244 |
1 files changed, 181 insertions, 63 deletions
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index fe4da1e96c52..9d6aae5449b6 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c | |||
@@ -28,6 +28,11 @@ | |||
28 | * | 28 | * |
29 | * Major and minor numbers are assigned dynamically to both UBI and volume | 29 | * Major and minor numbers are assigned dynamically to both UBI and volume |
30 | * character devices. | 30 | * character devices. |
31 | * | ||
32 | * Well, there is the third kind of character devices - the UBI control | ||
33 | * character device, which allows to manipulate by UBI devices - create and | ||
34 | * delete them. In other words, it is used for attaching and detaching MTD | ||
35 | * devices. | ||
31 | */ | 36 | */ |
32 | 37 | ||
33 | #include <linux/module.h> | 38 | #include <linux/module.h> |
@@ -39,34 +44,6 @@ | |||
39 | #include <asm/div64.h> | 44 | #include <asm/div64.h> |
40 | #include "ubi.h" | 45 | #include "ubi.h" |
41 | 46 | ||
42 | /* | ||
43 | * Maximum sequence numbers of UBI and volume character device IOCTLs (direct | ||
44 | * logical eraseblock erase is a debug-only feature). | ||
45 | */ | ||
46 | #define UBI_CDEV_IOC_MAX_SEQ 2 | ||
47 | #ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO | ||
48 | #define VOL_CDEV_IOC_MAX_SEQ 1 | ||
49 | #else | ||
50 | #define VOL_CDEV_IOC_MAX_SEQ 2 | ||
51 | #endif | ||
52 | |||
53 | /** | ||
54 | * major_to_device - get UBI device object by character device major number. | ||
55 | * @major: major number | ||
56 | * | ||
57 | * This function returns a pointer to the UBI device object. | ||
58 | */ | ||
59 | static struct ubi_device *major_to_device(int major) | ||
60 | { | ||
61 | int i; | ||
62 | |||
63 | for (i = 0; i < ubi_devices_cnt; i++) | ||
64 | if (ubi_devices[i] && ubi_devices[i]->major == major) | ||
65 | return ubi_devices[i]; | ||
66 | BUG(); | ||
67 | return NULL; | ||
68 | } | ||
69 | |||
70 | /** | 47 | /** |
71 | * get_exclusive - get exclusive access to an UBI volume. | 48 | * get_exclusive - get exclusive access to an UBI volume. |
72 | * @desc: volume descriptor | 49 | * @desc: volume descriptor |
@@ -124,9 +101,11 @@ static void revoke_exclusive(struct ubi_volume_desc *desc, int mode) | |||
124 | static int vol_cdev_open(struct inode *inode, struct file *file) | 101 | static int vol_cdev_open(struct inode *inode, struct file *file) |
125 | { | 102 | { |
126 | struct ubi_volume_desc *desc; | 103 | struct ubi_volume_desc *desc; |
127 | const struct ubi_device *ubi = major_to_device(imajor(inode)); | 104 | int vol_id = iminor(inode) - 1, mode, ubi_num; |
128 | int vol_id = iminor(inode) - 1; | 105 | |
129 | int mode; | 106 | ubi_num = ubi_major2num(imajor(inode)); |
107 | if (ubi_num < 0) | ||
108 | return ubi_num; | ||
130 | 109 | ||
131 | if (file->f_mode & FMODE_WRITE) | 110 | if (file->f_mode & FMODE_WRITE) |
132 | mode = UBI_READWRITE; | 111 | mode = UBI_READWRITE; |
@@ -135,7 +114,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file) | |||
135 | 114 | ||
136 | dbg_msg("open volume %d, mode %d", vol_id, mode); | 115 | dbg_msg("open volume %d, mode %d", vol_id, mode); |
137 | 116 | ||
138 | desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); | 117 | desc = ubi_open_volume(ubi_num, vol_id, mode); |
139 | if (IS_ERR(desc)) | 118 | if (IS_ERR(desc)) |
140 | return PTR_ERR(desc); | 119 | return PTR_ERR(desc); |
141 | 120 | ||
@@ -153,8 +132,15 @@ static int vol_cdev_release(struct inode *inode, struct file *file) | |||
153 | if (vol->updating) { | 132 | if (vol->updating) { |
154 | ubi_warn("update of volume %d not finished, volume is damaged", | 133 | ubi_warn("update of volume %d not finished, volume is damaged", |
155 | vol->vol_id); | 134 | vol->vol_id); |
135 | ubi_assert(!vol->changing_leb); | ||
156 | vol->updating = 0; | 136 | vol->updating = 0; |
157 | vfree(vol->upd_buf); | 137 | vfree(vol->upd_buf); |
138 | } else if (vol->changing_leb) { | ||
139 | dbg_msg("only %lld of %lld bytes received for atomic LEB change" | ||
140 | " for volume %d:%d, cancel", vol->upd_received, | ||
141 | vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); | ||
142 | vol->changing_leb = 0; | ||
143 | vfree(vol->upd_buf); | ||
158 | } | 144 | } |
159 | 145 | ||
160 | ubi_close_volume(desc); | 146 | ubi_close_volume(desc); |
@@ -205,13 +191,13 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
205 | struct ubi_volume_desc *desc = file->private_data; | 191 | struct ubi_volume_desc *desc = file->private_data; |
206 | struct ubi_volume *vol = desc->vol; | 192 | struct ubi_volume *vol = desc->vol; |
207 | struct ubi_device *ubi = vol->ubi; | 193 | struct ubi_device *ubi = vol->ubi; |
208 | int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size; | 194 | int err, lnum, off, len, tbuf_size; |
209 | size_t count_save = count; | 195 | size_t count_save = count; |
210 | void *tbuf; | 196 | void *tbuf; |
211 | uint64_t tmp; | 197 | uint64_t tmp; |
212 | 198 | ||
213 | dbg_msg("read %zd bytes from offset %lld of volume %d", | 199 | dbg_msg("read %zd bytes from offset %lld of volume %d", |
214 | count, *offp, vol_id); | 200 | count, *offp, vol->vol_id); |
215 | 201 | ||
216 | if (vol->updating) { | 202 | if (vol->updating) { |
217 | dbg_err("updating"); | 203 | dbg_err("updating"); |
@@ -225,7 +211,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
225 | return 0; | 211 | return 0; |
226 | 212 | ||
227 | if (vol->corrupted) | 213 | if (vol->corrupted) |
228 | dbg_msg("read from corrupted volume %d", vol_id); | 214 | dbg_msg("read from corrupted volume %d", vol->vol_id); |
229 | 215 | ||
230 | if (*offp + count > vol->used_bytes) | 216 | if (*offp + count > vol->used_bytes) |
231 | count_save = count = vol->used_bytes - *offp; | 217 | count_save = count = vol->used_bytes - *offp; |
@@ -249,7 +235,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
249 | if (off + len >= vol->usable_leb_size) | 235 | if (off + len >= vol->usable_leb_size) |
250 | len = vol->usable_leb_size - off; | 236 | len = vol->usable_leb_size - off; |
251 | 237 | ||
252 | err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0); | 238 | err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0); |
253 | if (err) | 239 | if (err) |
254 | break; | 240 | break; |
255 | 241 | ||
@@ -289,13 +275,13 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
289 | struct ubi_volume_desc *desc = file->private_data; | 275 | struct ubi_volume_desc *desc = file->private_data; |
290 | struct ubi_volume *vol = desc->vol; | 276 | struct ubi_volume *vol = desc->vol; |
291 | struct ubi_device *ubi = vol->ubi; | 277 | struct ubi_device *ubi = vol->ubi; |
292 | int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0; | 278 | int lnum, off, len, tbuf_size, err = 0; |
293 | size_t count_save = count; | 279 | size_t count_save = count; |
294 | char *tbuf; | 280 | char *tbuf; |
295 | uint64_t tmp; | 281 | uint64_t tmp; |
296 | 282 | ||
297 | dbg_msg("requested: write %zd bytes to offset %lld of volume %u", | 283 | dbg_msg("requested: write %zd bytes to offset %lld of volume %u", |
298 | count, *offp, desc->vol->vol_id); | 284 | count, *offp, vol->vol_id); |
299 | 285 | ||
300 | if (vol->vol_type == UBI_STATIC_VOLUME) | 286 | if (vol->vol_type == UBI_STATIC_VOLUME) |
301 | return -EROFS; | 287 | return -EROFS; |
@@ -339,7 +325,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
339 | break; | 325 | break; |
340 | } | 326 | } |
341 | 327 | ||
342 | err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len, | 328 | err = ubi_eba_write_leb(ubi, vol, lnum, tbuf, off, len, |
343 | UBI_UNKNOWN); | 329 | UBI_UNKNOWN); |
344 | if (err) | 330 | if (err) |
345 | break; | 331 | break; |
@@ -372,22 +358,32 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, | |||
372 | struct ubi_volume *vol = desc->vol; | 358 | struct ubi_volume *vol = desc->vol; |
373 | struct ubi_device *ubi = vol->ubi; | 359 | struct ubi_device *ubi = vol->ubi; |
374 | 360 | ||
375 | if (!vol->updating) | 361 | if (!vol->updating && !vol->changing_leb) |
376 | return vol_cdev_direct_write(file, buf, count, offp); | 362 | return vol_cdev_direct_write(file, buf, count, offp); |
377 | 363 | ||
378 | err = ubi_more_update_data(ubi, vol->vol_id, buf, count); | 364 | if (vol->updating) |
365 | err = ubi_more_update_data(ubi, vol, buf, count); | ||
366 | else | ||
367 | err = ubi_more_leb_change_data(ubi, vol, buf, count); | ||
368 | |||
379 | if (err < 0) { | 369 | if (err < 0) { |
380 | ubi_err("cannot write %zd bytes of update data", count); | 370 | ubi_err("cannot accept more %zd bytes of data, error %d", |
371 | count, err); | ||
381 | return err; | 372 | return err; |
382 | } | 373 | } |
383 | 374 | ||
384 | if (err) { | 375 | if (err) { |
385 | /* | 376 | /* |
386 | * Update is finished, @err contains number of actually written | 377 | * The operation is finished, @err contains number of actually |
387 | * bytes now. | 378 | * written bytes. |
388 | */ | 379 | */ |
389 | count = err; | 380 | count = err; |
390 | 381 | ||
382 | if (vol->changing_leb) { | ||
383 | revoke_exclusive(desc, UBI_READWRITE); | ||
384 | return count; | ||
385 | } | ||
386 | |||
391 | err = ubi_check_volume(ubi, vol->vol_id); | 387 | err = ubi_check_volume(ubi, vol->vol_id); |
392 | if (err < 0) | 388 | if (err < 0) |
393 | return err; | 389 | return err; |
@@ -402,7 +398,6 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, | |||
402 | revoke_exclusive(desc, UBI_READWRITE); | 398 | revoke_exclusive(desc, UBI_READWRITE); |
403 | } | 399 | } |
404 | 400 | ||
405 | *offp += count; | ||
406 | return count; | 401 | return count; |
407 | } | 402 | } |
408 | 403 | ||
@@ -447,11 +442,46 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
447 | if (err < 0) | 442 | if (err < 0) |
448 | break; | 443 | break; |
449 | 444 | ||
450 | err = ubi_start_update(ubi, vol->vol_id, bytes); | 445 | err = ubi_start_update(ubi, vol, bytes); |
451 | if (bytes == 0) | 446 | if (bytes == 0) |
452 | revoke_exclusive(desc, UBI_READWRITE); | 447 | revoke_exclusive(desc, UBI_READWRITE); |
448 | break; | ||
449 | } | ||
450 | |||
451 | /* Atomic logical eraseblock change command */ | ||
452 | case UBI_IOCEBCH: | ||
453 | { | ||
454 | struct ubi_leb_change_req req; | ||
455 | |||
456 | err = copy_from_user(&req, argp, | ||
457 | sizeof(struct ubi_leb_change_req)); | ||
458 | if (err) { | ||
459 | err = -EFAULT; | ||
460 | break; | ||
461 | } | ||
462 | |||
463 | if (desc->mode == UBI_READONLY || | ||
464 | vol->vol_type == UBI_STATIC_VOLUME) { | ||
465 | err = -EROFS; | ||
466 | break; | ||
467 | } | ||
468 | |||
469 | /* Validate the request */ | ||
470 | err = -EINVAL; | ||
471 | if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || | ||
472 | req.bytes < 0 || req.lnum >= vol->usable_leb_size) | ||
473 | break; | ||
474 | if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM && | ||
475 | req.dtype != UBI_UNKNOWN) | ||
476 | break; | ||
477 | |||
478 | err = get_exclusive(desc); | ||
479 | if (err < 0) | ||
480 | break; | ||
453 | 481 | ||
454 | file->f_pos = 0; | 482 | err = ubi_start_leb_change(ubi, vol, &req); |
483 | if (req.bytes == 0) | ||
484 | revoke_exclusive(desc, UBI_READWRITE); | ||
455 | break; | 485 | break; |
456 | } | 486 | } |
457 | 487 | ||
@@ -467,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
467 | break; | 497 | break; |
468 | } | 498 | } |
469 | 499 | ||
470 | if (desc->mode == UBI_READONLY) { | 500 | if (desc->mode == UBI_READONLY || |
501 | vol->vol_type == UBI_STATIC_VOLUME) { | ||
471 | err = -EROFS; | 502 | err = -EROFS; |
472 | break; | 503 | break; |
473 | } | 504 | } |
@@ -477,13 +508,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
477 | break; | 508 | break; |
478 | } | 509 | } |
479 | 510 | ||
480 | if (vol->vol_type != UBI_DYNAMIC_VOLUME) { | ||
481 | err = -EROFS; | ||
482 | break; | ||
483 | } | ||
484 | |||
485 | dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); | 511 | dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); |
486 | err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum); | 512 | err = ubi_eba_unmap_leb(ubi, vol, lnum); |
487 | if (err) | 513 | if (err) |
488 | break; | 514 | break; |
489 | 515 | ||
@@ -580,9 +606,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
580 | if (!capable(CAP_SYS_RESOURCE)) | 606 | if (!capable(CAP_SYS_RESOURCE)) |
581 | return -EPERM; | 607 | return -EPERM; |
582 | 608 | ||
583 | ubi = major_to_device(imajor(inode)); | 609 | ubi = ubi_get_by_major(imajor(inode)); |
584 | if (IS_ERR(ubi)) | 610 | if (!ubi) |
585 | return PTR_ERR(ubi); | 611 | return -ENODEV; |
586 | 612 | ||
587 | switch (cmd) { | 613 | switch (cmd) { |
588 | /* Create volume command */ | 614 | /* Create volume command */ |
@@ -591,8 +617,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
591 | struct ubi_mkvol_req req; | 617 | struct ubi_mkvol_req req; |
592 | 618 | ||
593 | dbg_msg("create volume"); | 619 | dbg_msg("create volume"); |
594 | err = copy_from_user(&req, argp, | 620 | err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); |
595 | sizeof(struct ubi_mkvol_req)); | ||
596 | if (err) { | 621 | if (err) { |
597 | err = -EFAULT; | 622 | err = -EFAULT; |
598 | break; | 623 | break; |
@@ -604,7 +629,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
604 | 629 | ||
605 | req.name[req.name_len] = '\0'; | 630 | req.name[req.name_len] = '\0'; |
606 | 631 | ||
632 | mutex_lock(&ubi->volumes_mutex); | ||
607 | err = ubi_create_volume(ubi, &req); | 633 | err = ubi_create_volume(ubi, &req); |
634 | mutex_unlock(&ubi->volumes_mutex); | ||
608 | if (err) | 635 | if (err) |
609 | break; | 636 | break; |
610 | 637 | ||
@@ -633,10 +660,16 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
633 | break; | 660 | break; |
634 | } | 661 | } |
635 | 662 | ||
663 | mutex_lock(&ubi->volumes_mutex); | ||
636 | err = ubi_remove_volume(desc); | 664 | err = ubi_remove_volume(desc); |
637 | if (err) | 665 | mutex_unlock(&ubi->volumes_mutex); |
638 | ubi_close_volume(desc); | ||
639 | 666 | ||
667 | /* | ||
668 | * The volume is deleted (unless an error occurred), and the | ||
669 | * 'struct ubi_volume' object will be freed when | ||
670 | * 'ubi_close_volume()' will call 'put_device()'. | ||
671 | */ | ||
672 | ubi_close_volume(desc); | ||
640 | break; | 673 | break; |
641 | } | 674 | } |
642 | 675 | ||
@@ -648,8 +681,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
648 | struct ubi_rsvol_req req; | 681 | struct ubi_rsvol_req req; |
649 | 682 | ||
650 | dbg_msg("re-size volume"); | 683 | dbg_msg("re-size volume"); |
651 | err = copy_from_user(&req, argp, | 684 | err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); |
652 | sizeof(struct ubi_rsvol_req)); | ||
653 | if (err) { | 685 | if (err) { |
654 | err = -EFAULT; | 686 | err = -EFAULT; |
655 | break; | 687 | break; |
@@ -669,7 +701,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
669 | pebs = !!do_div(tmp, desc->vol->usable_leb_size); | 701 | pebs = !!do_div(tmp, desc->vol->usable_leb_size); |
670 | pebs += tmp; | 702 | pebs += tmp; |
671 | 703 | ||
704 | mutex_lock(&ubi->volumes_mutex); | ||
672 | err = ubi_resize_volume(desc, pebs); | 705 | err = ubi_resize_volume(desc, pebs); |
706 | mutex_unlock(&ubi->volumes_mutex); | ||
673 | ubi_close_volume(desc); | 707 | ubi_close_volume(desc); |
674 | break; | 708 | break; |
675 | } | 709 | } |
@@ -679,9 +713,93 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
679 | break; | 713 | break; |
680 | } | 714 | } |
681 | 715 | ||
716 | ubi_put_device(ubi); | ||
682 | return err; | 717 | return err; |
683 | } | 718 | } |
684 | 719 | ||
720 | static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, | ||
721 | unsigned int cmd, unsigned long arg) | ||
722 | { | ||
723 | int err = 0; | ||
724 | void __user *argp = (void __user *)arg; | ||
725 | |||
726 | if (!capable(CAP_SYS_RESOURCE)) | ||
727 | return -EPERM; | ||
728 | |||
729 | switch (cmd) { | ||
730 | /* Attach an MTD device command */ | ||
731 | case UBI_IOCATT: | ||
732 | { | ||
733 | struct ubi_attach_req req; | ||
734 | struct mtd_info *mtd; | ||
735 | |||
736 | dbg_msg("attach MTD device"); | ||
737 | err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req)); | ||
738 | if (err) { | ||
739 | err = -EFAULT; | ||
740 | break; | ||
741 | } | ||
742 | |||
743 | if (req.mtd_num < 0 || | ||
744 | (req.ubi_num < 0 && req.ubi_num != UBI_DEV_NUM_AUTO)) { | ||
745 | err = -EINVAL; | ||
746 | break; | ||
747 | } | ||
748 | |||
749 | mtd = get_mtd_device(NULL, req.mtd_num); | ||
750 | if (IS_ERR(mtd)) { | ||
751 | err = PTR_ERR(mtd); | ||
752 | break; | ||
753 | } | ||
754 | |||
755 | /* | ||
756 | * Note, further request verification is done by | ||
757 | * 'ubi_attach_mtd_dev()'. | ||
758 | */ | ||
759 | mutex_lock(&ubi_devices_mutex); | ||
760 | err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset); | ||
761 | mutex_unlock(&ubi_devices_mutex); | ||
762 | if (err < 0) | ||
763 | put_mtd_device(mtd); | ||
764 | else | ||
765 | /* @err contains UBI device number */ | ||
766 | err = put_user(err, (__user int32_t *)argp); | ||
767 | |||
768 | break; | ||
769 | } | ||
770 | |||
771 | /* Detach an MTD device command */ | ||
772 | case UBI_IOCDET: | ||
773 | { | ||
774 | int ubi_num; | ||
775 | |||
776 | dbg_msg("dettach MTD device"); | ||
777 | err = get_user(ubi_num, (__user int32_t *)argp); | ||
778 | if (err) { | ||
779 | err = -EFAULT; | ||
780 | break; | ||
781 | } | ||
782 | |||
783 | mutex_lock(&ubi_devices_mutex); | ||
784 | err = ubi_detach_mtd_dev(ubi_num, 0); | ||
785 | mutex_unlock(&ubi_devices_mutex); | ||
786 | break; | ||
787 | } | ||
788 | |||
789 | default: | ||
790 | err = -ENOTTY; | ||
791 | break; | ||
792 | } | ||
793 | |||
794 | return err; | ||
795 | } | ||
796 | |||
797 | /* UBI control character device operations */ | ||
798 | struct file_operations ubi_ctrl_cdev_operations = { | ||
799 | .ioctl = ctrl_cdev_ioctl, | ||
800 | .owner = THIS_MODULE, | ||
801 | }; | ||
802 | |||
685 | /* UBI character device operations */ | 803 | /* UBI character device operations */ |
686 | struct file_operations ubi_cdev_operations = { | 804 | struct file_operations ubi_cdev_operations = { |
687 | .owner = THIS_MODULE, | 805 | .owner = THIS_MODULE, |