diff options
Diffstat (limited to 'drivers/mtd/ubi/cdev.c')
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 188 |
1 files changed, 187 insertions, 1 deletions
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 7c19918cc914..bc8199c6a9f5 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c | |||
@@ -605,6 +605,166 @@ static int verify_rsvol_req(const struct ubi_device *ubi, | |||
605 | return 0; | 605 | return 0; |
606 | } | 606 | } |
607 | 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 | |||
608 | static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | 768 | static int ubi_cdev_ioctl(struct inode *inode, struct file *file, |
609 | unsigned int cmd, unsigned long arg) | 769 | unsigned int cmd, unsigned long arg) |
610 | { | 770 | { |
@@ -670,7 +830,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
670 | } | 830 | } |
671 | 831 | ||
672 | mutex_lock(&ubi->volumes_mutex); | 832 | mutex_lock(&ubi->volumes_mutex); |
673 | err = ubi_remove_volume(desc); | 833 | err = ubi_remove_volume(desc, 0); |
674 | mutex_unlock(&ubi->volumes_mutex); | 834 | mutex_unlock(&ubi->volumes_mutex); |
675 | 835 | ||
676 | /* | 836 | /* |
@@ -717,6 +877,32 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
717 | break; | 877 | break; |
718 | } | 878 | } |
719 | 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 | |||
720 | default: | 906 | default: |
721 | err = -ENOTTY; | 907 | err = -ENOTTY; |
722 | break; | 908 | break; |