diff options
Diffstat (limited to 'drivers/mtd/ubi/cdev.c')
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 234 |
1 files changed, 212 insertions, 22 deletions
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 89193ba9451e..03c759b4eeb5 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c | |||
@@ -39,9 +39,9 @@ | |||
39 | #include <linux/stat.h> | 39 | #include <linux/stat.h> |
40 | #include <linux/ioctl.h> | 40 | #include <linux/ioctl.h> |
41 | #include <linux/capability.h> | 41 | #include <linux/capability.h> |
42 | #include <linux/uaccess.h> | ||
42 | #include <linux/smp_lock.h> | 43 | #include <linux/smp_lock.h> |
43 | #include <mtd/ubi-user.h> | 44 | #include <mtd/ubi-user.h> |
44 | #include <asm/uaccess.h> | ||
45 | #include <asm/div64.h> | 45 | #include <asm/div64.h> |
46 | #include "ubi.h" | 46 | #include "ubi.h" |
47 | 47 | ||
@@ -116,7 +116,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file) | |||
116 | else | 116 | else |
117 | mode = UBI_READONLY; | 117 | mode = UBI_READONLY; |
118 | 118 | ||
119 | dbg_msg("open volume %d, mode %d", vol_id, mode); | 119 | dbg_gen("open volume %d, mode %d", vol_id, mode); |
120 | 120 | ||
121 | desc = ubi_open_volume(ubi_num, vol_id, mode); | 121 | desc = ubi_open_volume(ubi_num, vol_id, mode); |
122 | unlock_kernel(); | 122 | unlock_kernel(); |
@@ -132,7 +132,7 @@ static int vol_cdev_release(struct inode *inode, struct file *file) | |||
132 | struct ubi_volume_desc *desc = file->private_data; | 132 | struct ubi_volume_desc *desc = file->private_data; |
133 | struct ubi_volume *vol = desc->vol; | 133 | struct ubi_volume *vol = desc->vol; |
134 | 134 | ||
135 | dbg_msg("release volume %d, mode %d", vol->vol_id, desc->mode); | 135 | dbg_gen("release volume %d, mode %d", vol->vol_id, desc->mode); |
136 | 136 | ||
137 | if (vol->updating) { | 137 | if (vol->updating) { |
138 | ubi_warn("update of volume %d not finished, volume is damaged", | 138 | ubi_warn("update of volume %d not finished, volume is damaged", |
@@ -141,7 +141,7 @@ static int vol_cdev_release(struct inode *inode, struct file *file) | |||
141 | vol->updating = 0; | 141 | vol->updating = 0; |
142 | vfree(vol->upd_buf); | 142 | vfree(vol->upd_buf); |
143 | } else if (vol->changing_leb) { | 143 | } else if (vol->changing_leb) { |
144 | dbg_msg("only %lld of %lld bytes received for atomic LEB change" | 144 | dbg_gen("only %lld of %lld bytes received for atomic LEB change" |
145 | " for volume %d:%d, cancel", vol->upd_received, | 145 | " for volume %d:%d, cancel", vol->upd_received, |
146 | vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); | 146 | vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); |
147 | vol->changing_leb = 0; | 147 | vol->changing_leb = 0; |
@@ -183,7 +183,7 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin) | |||
183 | return -EINVAL; | 183 | return -EINVAL; |
184 | } | 184 | } |
185 | 185 | ||
186 | dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld", | 186 | dbg_gen("seek volume %d, offset %lld, origin %d, new offset %lld", |
187 | vol->vol_id, offset, origin, new_offset); | 187 | vol->vol_id, offset, origin, new_offset); |
188 | 188 | ||
189 | file->f_pos = new_offset; | 189 | file->f_pos = new_offset; |
@@ -201,7 +201,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
201 | void *tbuf; | 201 | void *tbuf; |
202 | uint64_t tmp; | 202 | uint64_t tmp; |
203 | 203 | ||
204 | dbg_msg("read %zd bytes from offset %lld of volume %d", | 204 | dbg_gen("read %zd bytes from offset %lld of volume %d", |
205 | count, *offp, vol->vol_id); | 205 | count, *offp, vol->vol_id); |
206 | 206 | ||
207 | if (vol->updating) { | 207 | if (vol->updating) { |
@@ -216,7 +216,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
216 | return 0; | 216 | return 0; |
217 | 217 | ||
218 | if (vol->corrupted) | 218 | if (vol->corrupted) |
219 | dbg_msg("read from corrupted volume %d", vol->vol_id); | 219 | dbg_gen("read from corrupted volume %d", vol->vol_id); |
220 | 220 | ||
221 | if (*offp + count > vol->used_bytes) | 221 | if (*offp + count > vol->used_bytes) |
222 | count_save = count = vol->used_bytes - *offp; | 222 | count_save = count = vol->used_bytes - *offp; |
@@ -285,7 +285,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
285 | char *tbuf; | 285 | char *tbuf; |
286 | uint64_t tmp; | 286 | uint64_t tmp; |
287 | 287 | ||
288 | dbg_msg("requested: write %zd bytes to offset %lld of volume %u", | 288 | dbg_gen("requested: write %zd bytes to offset %lld of volume %u", |
289 | count, *offp, vol->vol_id); | 289 | count, *offp, vol->vol_id); |
290 | 290 | ||
291 | if (vol->vol_type == UBI_STATIC_VOLUME) | 291 | if (vol->vol_type == UBI_STATIC_VOLUME) |
@@ -295,7 +295,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
295 | off = do_div(tmp, vol->usable_leb_size); | 295 | off = do_div(tmp, vol->usable_leb_size); |
296 | lnum = tmp; | 296 | lnum = tmp; |
297 | 297 | ||
298 | if (off % ubi->min_io_size) { | 298 | if (off & (ubi->min_io_size - 1)) { |
299 | dbg_err("unaligned position"); | 299 | dbg_err("unaligned position"); |
300 | return -EINVAL; | 300 | return -EINVAL; |
301 | } | 301 | } |
@@ -304,7 +304,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
304 | count_save = count = vol->used_bytes - *offp; | 304 | count_save = count = vol->used_bytes - *offp; |
305 | 305 | ||
306 | /* We can write only in fractions of the minimum I/O unit */ | 306 | /* We can write only in fractions of the minimum I/O unit */ |
307 | if (count % ubi->min_io_size) { | 307 | if (count & (ubi->min_io_size - 1)) { |
308 | dbg_err("unaligned write length"); | 308 | dbg_err("unaligned write length"); |
309 | return -EINVAL; | 309 | return -EINVAL; |
310 | } | 310 | } |
@@ -352,7 +352,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
352 | } | 352 | } |
353 | 353 | ||
354 | #else | 354 | #else |
355 | #define vol_cdev_direct_write(file, buf, count, offp) -EPERM | 355 | #define vol_cdev_direct_write(file, buf, count, offp) (-EPERM) |
356 | #endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */ | 356 | #endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */ |
357 | 357 | ||
358 | static ssize_t vol_cdev_write(struct file *file, const char __user *buf, | 358 | static ssize_t vol_cdev_write(struct file *file, const char __user *buf, |
@@ -437,7 +437,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
437 | break; | 437 | break; |
438 | } | 438 | } |
439 | 439 | ||
440 | rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad); | 440 | rsvd_bytes = (long long)vol->reserved_pebs * |
441 | ubi->leb_size-vol->data_pad; | ||
441 | if (bytes < 0 || bytes > rsvd_bytes) { | 442 | if (bytes < 0 || bytes > rsvd_bytes) { |
442 | err = -EINVAL; | 443 | err = -EINVAL; |
443 | break; | 444 | break; |
@@ -513,7 +514,7 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
513 | break; | 514 | break; |
514 | } | 515 | } |
515 | 516 | ||
516 | dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); | 517 | dbg_gen("erase LEB %d:%d", vol->vol_id, lnum); |
517 | err = ubi_eba_unmap_leb(ubi, vol, lnum); | 518 | err = ubi_eba_unmap_leb(ubi, vol, lnum); |
518 | if (err) | 519 | if (err) |
519 | break; | 520 | break; |
@@ -564,7 +565,7 @@ static int verify_mkvol_req(const struct ubi_device *ubi, | |||
564 | if (req->alignment > ubi->leb_size) | 565 | if (req->alignment > ubi->leb_size) |
565 | goto bad; | 566 | goto bad; |
566 | 567 | ||
567 | n = req->alignment % ubi->min_io_size; | 568 | n = req->alignment & (ubi->min_io_size - 1); |
568 | if (req->alignment != 1 && n) | 569 | if (req->alignment != 1 && n) |
569 | goto bad; | 570 | goto bad; |
570 | 571 | ||
@@ -573,6 +574,10 @@ static int verify_mkvol_req(const struct ubi_device *ubi, | |||
573 | goto bad; | 574 | goto bad; |
574 | } | 575 | } |
575 | 576 | ||
577 | n = strnlen(req->name, req->name_len + 1); | ||
578 | if (n != req->name_len) | ||
579 | goto bad; | ||
580 | |||
576 | return 0; | 581 | return 0; |
577 | 582 | ||
578 | bad: | 583 | bad: |
@@ -600,6 +605,166 @@ static int verify_rsvol_req(const struct ubi_device *ubi, | |||
600 | return 0; | 605 | return 0; |
601 | } | 606 | } |
602 | 607 | ||
608 | /** | ||
609 | * rename_volumes - rename UBI volumes. | ||
610 | * @ubi: UBI device description object | ||
611 | * @req: volumes re-name request | ||
612 | * | ||
613 | * This is a helper function for the volume re-name IOCTL which validates the | ||
614 | * the request, opens the volume and calls corresponding volumes management | ||
615 | * function. Returns zero in case of success and a negative error code in case | ||
616 | * of failure. | ||
617 | */ | ||
618 | static int rename_volumes(struct ubi_device *ubi, | ||
619 | struct ubi_rnvol_req *req) | ||
620 | { | ||
621 | int i, n, err; | ||
622 | struct list_head rename_list; | ||
623 | struct ubi_rename_entry *re, *re1; | ||
624 | |||
625 | if (req->count < 0 || req->count > UBI_MAX_RNVOL) | ||
626 | return -EINVAL; | ||
627 | |||
628 | if (req->count == 0) | ||
629 | return 0; | ||
630 | |||
631 | /* Validate volume IDs and names in the request */ | ||
632 | for (i = 0; i < req->count; i++) { | ||
633 | if (req->ents[i].vol_id < 0 || | ||
634 | req->ents[i].vol_id >= ubi->vtbl_slots) | ||
635 | return -EINVAL; | ||
636 | if (req->ents[i].name_len < 0) | ||
637 | return -EINVAL; | ||
638 | if (req->ents[i].name_len > UBI_VOL_NAME_MAX) | ||
639 | return -ENAMETOOLONG; | ||
640 | req->ents[i].name[req->ents[i].name_len] = '\0'; | ||
641 | n = strlen(req->ents[i].name); | ||
642 | if (n != req->ents[i].name_len) | ||
643 | err = -EINVAL; | ||
644 | } | ||
645 | |||
646 | /* Make sure volume IDs and names are unique */ | ||
647 | for (i = 0; i < req->count - 1; i++) { | ||
648 | for (n = i + 1; n < req->count; n++) { | ||
649 | if (req->ents[i].vol_id == req->ents[n].vol_id) { | ||
650 | dbg_err("duplicated volume id %d", | ||
651 | req->ents[i].vol_id); | ||
652 | return -EINVAL; | ||
653 | } | ||
654 | if (!strcmp(req->ents[i].name, req->ents[n].name)) { | ||
655 | dbg_err("duplicated volume name \"%s\"", | ||
656 | req->ents[i].name); | ||
657 | return -EINVAL; | ||
658 | } | ||
659 | } | ||
660 | } | ||
661 | |||
662 | /* Create the re-name list */ | ||
663 | INIT_LIST_HEAD(&rename_list); | ||
664 | for (i = 0; i < req->count; i++) { | ||
665 | int vol_id = req->ents[i].vol_id; | ||
666 | int name_len = req->ents[i].name_len; | ||
667 | const char *name = req->ents[i].name; | ||
668 | |||
669 | re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL); | ||
670 | if (!re) { | ||
671 | err = -ENOMEM; | ||
672 | goto out_free; | ||
673 | } | ||
674 | |||
675 | re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE); | ||
676 | if (IS_ERR(re->desc)) { | ||
677 | err = PTR_ERR(re->desc); | ||
678 | dbg_err("cannot open volume %d, error %d", vol_id, err); | ||
679 | kfree(re); | ||
680 | goto out_free; | ||
681 | } | ||
682 | |||
683 | /* Skip this re-naming if the name does not really change */ | ||
684 | if (re->desc->vol->name_len == name_len && | ||
685 | !memcmp(re->desc->vol->name, name, name_len)) { | ||
686 | ubi_close_volume(re->desc); | ||
687 | kfree(re); | ||
688 | continue; | ||
689 | } | ||
690 | |||
691 | re->new_name_len = name_len; | ||
692 | memcpy(re->new_name, name, name_len); | ||
693 | list_add_tail(&re->list, &rename_list); | ||
694 | dbg_msg("will rename volume %d from \"%s\" to \"%s\"", | ||
695 | vol_id, re->desc->vol->name, name); | ||
696 | } | ||
697 | |||
698 | if (list_empty(&rename_list)) | ||
699 | return 0; | ||
700 | |||
701 | /* Find out the volumes which have to be removed */ | ||
702 | list_for_each_entry(re, &rename_list, list) { | ||
703 | struct ubi_volume_desc *desc; | ||
704 | int no_remove_needed = 0; | ||
705 | |||
706 | /* | ||
707 | * Volume @re->vol_id is going to be re-named to | ||
708 | * @re->new_name, while its current name is @name. If a volume | ||
709 | * with name @re->new_name currently exists, it has to be | ||
710 | * removed, unless it is also re-named in the request (@req). | ||
711 | */ | ||
712 | list_for_each_entry(re1, &rename_list, list) { | ||
713 | if (re->new_name_len == re1->desc->vol->name_len && | ||
714 | !memcmp(re->new_name, re1->desc->vol->name, | ||
715 | re1->desc->vol->name_len)) { | ||
716 | no_remove_needed = 1; | ||
717 | break; | ||
718 | } | ||
719 | } | ||
720 | |||
721 | if (no_remove_needed) | ||
722 | continue; | ||
723 | |||
724 | /* | ||
725 | * It seems we need to remove volume with name @re->new_name, | ||
726 | * if it exists. | ||
727 | */ | ||
728 | desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name, UBI_EXCLUSIVE); | ||
729 | if (IS_ERR(desc)) { | ||
730 | err = PTR_ERR(desc); | ||
731 | if (err == -ENODEV) | ||
732 | /* Re-naming into a non-existing volume name */ | ||
733 | continue; | ||
734 | |||
735 | /* The volume exists but busy, or an error occurred */ | ||
736 | dbg_err("cannot open volume \"%s\", error %d", | ||
737 | re->new_name, err); | ||
738 | goto out_free; | ||
739 | } | ||
740 | |||
741 | re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL); | ||
742 | if (!re) { | ||
743 | err = -ENOMEM; | ||
744 | ubi_close_volume(desc); | ||
745 | goto out_free; | ||
746 | } | ||
747 | |||
748 | re->remove = 1; | ||
749 | re->desc = desc; | ||
750 | list_add(&re->list, &rename_list); | ||
751 | dbg_msg("will remove volume %d, name \"%s\"", | ||
752 | re->desc->vol->vol_id, re->desc->vol->name); | ||
753 | } | ||
754 | |||
755 | mutex_lock(&ubi->volumes_mutex); | ||
756 | err = ubi_rename_volumes(ubi, &rename_list); | ||
757 | mutex_unlock(&ubi->volumes_mutex); | ||
758 | |||
759 | out_free: | ||
760 | list_for_each_entry_safe(re, re1, &rename_list, list) { | ||
761 | ubi_close_volume(re->desc); | ||
762 | list_del(&re->list); | ||
763 | kfree(re); | ||
764 | } | ||
765 | return err; | ||
766 | } | ||
767 | |||
603 | static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | 768 | static int ubi_cdev_ioctl(struct inode *inode, struct file *file, |
604 | unsigned int cmd, unsigned long arg) | 769 | unsigned int cmd, unsigned long arg) |
605 | { | 770 | { |
@@ -621,19 +786,18 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
621 | { | 786 | { |
622 | struct ubi_mkvol_req req; | 787 | struct ubi_mkvol_req req; |
623 | 788 | ||
624 | dbg_msg("create volume"); | 789 | dbg_gen("create volume"); |
625 | err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); | 790 | err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); |
626 | if (err) { | 791 | if (err) { |
627 | err = -EFAULT; | 792 | err = -EFAULT; |
628 | break; | 793 | break; |
629 | } | 794 | } |
630 | 795 | ||
796 | req.name[req.name_len] = '\0'; | ||
631 | err = verify_mkvol_req(ubi, &req); | 797 | err = verify_mkvol_req(ubi, &req); |
632 | if (err) | 798 | if (err) |
633 | break; | 799 | break; |
634 | 800 | ||
635 | req.name[req.name_len] = '\0'; | ||
636 | |||
637 | mutex_lock(&ubi->volumes_mutex); | 801 | mutex_lock(&ubi->volumes_mutex); |
638 | err = ubi_create_volume(ubi, &req); | 802 | err = ubi_create_volume(ubi, &req); |
639 | mutex_unlock(&ubi->volumes_mutex); | 803 | mutex_unlock(&ubi->volumes_mutex); |
@@ -652,7 +816,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
652 | { | 816 | { |
653 | int vol_id; | 817 | int vol_id; |
654 | 818 | ||
655 | dbg_msg("remove volume"); | 819 | dbg_gen("remove volume"); |
656 | err = get_user(vol_id, (__user int32_t *)argp); | 820 | err = get_user(vol_id, (__user int32_t *)argp); |
657 | if (err) { | 821 | if (err) { |
658 | err = -EFAULT; | 822 | err = -EFAULT; |
@@ -666,7 +830,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
666 | } | 830 | } |
667 | 831 | ||
668 | mutex_lock(&ubi->volumes_mutex); | 832 | mutex_lock(&ubi->volumes_mutex); |
669 | err = ubi_remove_volume(desc); | 833 | err = ubi_remove_volume(desc, 0); |
670 | mutex_unlock(&ubi->volumes_mutex); | 834 | mutex_unlock(&ubi->volumes_mutex); |
671 | 835 | ||
672 | /* | 836 | /* |
@@ -685,7 +849,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
685 | uint64_t tmp; | 849 | uint64_t tmp; |
686 | struct ubi_rsvol_req req; | 850 | struct ubi_rsvol_req req; |
687 | 851 | ||
688 | dbg_msg("re-size volume"); | 852 | dbg_gen("re-size volume"); |
689 | err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); | 853 | err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); |
690 | if (err) { | 854 | if (err) { |
691 | err = -EFAULT; | 855 | err = -EFAULT; |
@@ -713,6 +877,32 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
713 | break; | 877 | break; |
714 | } | 878 | } |
715 | 879 | ||
880 | /* Re-name volumes command */ | ||
881 | case UBI_IOCRNVOL: | ||
882 | { | ||
883 | struct ubi_rnvol_req *req; | ||
884 | |||
885 | dbg_msg("re-name volumes"); | ||
886 | req = kmalloc(sizeof(struct ubi_rnvol_req), GFP_KERNEL); | ||
887 | if (!req) { | ||
888 | err = -ENOMEM; | ||
889 | break; | ||
890 | }; | ||
891 | |||
892 | err = copy_from_user(req, argp, sizeof(struct ubi_rnvol_req)); | ||
893 | if (err) { | ||
894 | err = -EFAULT; | ||
895 | kfree(req); | ||
896 | break; | ||
897 | } | ||
898 | |||
899 | mutex_lock(&ubi->mult_mutex); | ||
900 | err = rename_volumes(ubi, req); | ||
901 | mutex_unlock(&ubi->mult_mutex); | ||
902 | kfree(req); | ||
903 | break; | ||
904 | } | ||
905 | |||
716 | default: | 906 | default: |
717 | err = -ENOTTY; | 907 | err = -ENOTTY; |
718 | break; | 908 | break; |
@@ -738,7 +928,7 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, | |||
738 | struct ubi_attach_req req; | 928 | struct ubi_attach_req req; |
739 | struct mtd_info *mtd; | 929 | struct mtd_info *mtd; |
740 | 930 | ||
741 | dbg_msg("attach MTD device"); | 931 | dbg_gen("attach MTD device"); |
742 | err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req)); | 932 | err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req)); |
743 | if (err) { | 933 | if (err) { |
744 | err = -EFAULT; | 934 | err = -EFAULT; |
@@ -778,7 +968,7 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, | |||
778 | { | 968 | { |
779 | int ubi_num; | 969 | int ubi_num; |
780 | 970 | ||
781 | dbg_msg("dettach MTD device"); | 971 | dbg_gen("dettach MTD device"); |
782 | err = get_user(ubi_num, (__user int32_t *)argp); | 972 | err = get_user(ubi_num, (__user int32_t *)argp); |
783 | if (err) { | 973 | if (err) { |
784 | err = -EFAULT; | 974 | err = -EFAULT; |