diff options
Diffstat (limited to 'drivers/mtd/ubi/cdev.c')
| -rw-r--r-- | drivers/mtd/ubi/cdev.c | 184 |
1 files changed, 133 insertions, 51 deletions
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 98cf31ed0814..e63c8fc3df3a 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c | |||
| @@ -40,9 +40,9 @@ | |||
| 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/uaccess.h> |
| 43 | #include <linux/smp_lock.h> | 43 | #include <linux/compat.h> |
| 44 | #include <linux/math64.h> | ||
| 44 | #include <mtd/ubi-user.h> | 45 | #include <mtd/ubi-user.h> |
| 45 | #include <asm/div64.h> | ||
| 46 | #include "ubi.h" | 46 | #include "ubi.h" |
| 47 | 47 | ||
| 48 | /** | 48 | /** |
| @@ -195,7 +195,6 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
| 195 | int err, lnum, off, len, tbuf_size; | 195 | int err, lnum, off, len, tbuf_size; |
| 196 | size_t count_save = count; | 196 | size_t count_save = count; |
| 197 | void *tbuf; | 197 | void *tbuf; |
| 198 | uint64_t tmp; | ||
| 199 | 198 | ||
| 200 | dbg_gen("read %zd bytes from offset %lld of volume %d", | 199 | dbg_gen("read %zd bytes from offset %lld of volume %d", |
| 201 | count, *offp, vol->vol_id); | 200 | count, *offp, vol->vol_id); |
| @@ -225,10 +224,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
| 225 | return -ENOMEM; | 224 | return -ENOMEM; |
| 226 | 225 | ||
| 227 | len = count > tbuf_size ? tbuf_size : count; | 226 | len = count > tbuf_size ? tbuf_size : count; |
| 228 | 227 | lnum = div_u64_rem(*offp, vol->usable_leb_size, &off); | |
| 229 | tmp = *offp; | ||
| 230 | off = do_div(tmp, vol->usable_leb_size); | ||
| 231 | lnum = tmp; | ||
| 232 | 228 | ||
| 233 | do { | 229 | do { |
| 234 | cond_resched(); | 230 | cond_resched(); |
| @@ -263,12 +259,9 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, | |||
| 263 | return err ? err : count_save - count; | 259 | return err ? err : count_save - count; |
| 264 | } | 260 | } |
| 265 | 261 | ||
| 266 | #ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO | ||
| 267 | |||
| 268 | /* | 262 | /* |
| 269 | * This function allows to directly write to dynamic UBI volumes, without | 263 | * This function allows to directly write to dynamic UBI volumes, without |
| 270 | * issuing the volume update operation. Available only as a debugging feature. | 264 | * issuing the volume update operation. |
| 271 | * Very useful for testing UBI. | ||
| 272 | */ | 265 | */ |
| 273 | static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | 266 | static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, |
| 274 | size_t count, loff_t *offp) | 267 | size_t count, loff_t *offp) |
| @@ -279,7 +272,9 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
| 279 | int lnum, off, len, tbuf_size, err = 0; | 272 | int lnum, off, len, tbuf_size, err = 0; |
| 280 | size_t count_save = count; | 273 | size_t count_save = count; |
| 281 | char *tbuf; | 274 | char *tbuf; |
| 282 | uint64_t tmp; | 275 | |
| 276 | if (!vol->direct_writes) | ||
| 277 | return -EPERM; | ||
| 283 | 278 | ||
| 284 | dbg_gen("requested: write %zd bytes to offset %lld of volume %u", | 279 | dbg_gen("requested: write %zd bytes to offset %lld of volume %u", |
| 285 | count, *offp, vol->vol_id); | 280 | count, *offp, vol->vol_id); |
| @@ -287,10 +282,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
| 287 | if (vol->vol_type == UBI_STATIC_VOLUME) | 282 | if (vol->vol_type == UBI_STATIC_VOLUME) |
| 288 | return -EROFS; | 283 | return -EROFS; |
| 289 | 284 | ||
| 290 | tmp = *offp; | 285 | lnum = div_u64_rem(*offp, vol->usable_leb_size, &off); |
| 291 | off = do_div(tmp, vol->usable_leb_size); | ||
| 292 | lnum = tmp; | ||
| 293 | |||
| 294 | if (off & (ubi->min_io_size - 1)) { | 286 | if (off & (ubi->min_io_size - 1)) { |
| 295 | dbg_err("unaligned position"); | 287 | dbg_err("unaligned position"); |
| 296 | return -EINVAL; | 288 | return -EINVAL; |
| @@ -347,10 +339,6 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, | |||
| 347 | return err ? err : count_save - count; | 339 | return err ? err : count_save - count; |
| 348 | } | 340 | } |
| 349 | 341 | ||
| 350 | #else | ||
| 351 | #define vol_cdev_direct_write(file, buf, count, offp) (-EPERM) | ||
| 352 | #endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */ | ||
| 353 | |||
| 354 | static ssize_t vol_cdev_write(struct file *file, const char __user *buf, | 342 | static ssize_t vol_cdev_write(struct file *file, const char __user *buf, |
| 355 | size_t count, loff_t *offp) | 343 | size_t count, loff_t *offp) |
| 356 | { | 344 | { |
| @@ -402,8 +390,8 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, | |||
| 402 | return count; | 390 | return count; |
| 403 | } | 391 | } |
| 404 | 392 | ||
| 405 | static int vol_cdev_ioctl(struct inode *inode, struct file *file, | 393 | static long vol_cdev_ioctl(struct file *file, unsigned int cmd, |
| 406 | unsigned int cmd, unsigned long arg) | 394 | unsigned long arg) |
| 407 | { | 395 | { |
| 408 | int err = 0; | 396 | int err = 0; |
| 409 | struct ubi_volume_desc *desc = file->private_data; | 397 | struct ubi_volume_desc *desc = file->private_data; |
| @@ -487,7 +475,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 487 | break; | 475 | break; |
| 488 | } | 476 | } |
| 489 | 477 | ||
| 490 | #ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO | ||
| 491 | /* Logical eraseblock erasure command */ | 478 | /* Logical eraseblock erasure command */ |
| 492 | case UBI_IOCEBER: | 479 | case UBI_IOCEBER: |
| 493 | { | 480 | { |
| @@ -518,13 +505,77 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 518 | err = ubi_wl_flush(ubi); | 505 | err = ubi_wl_flush(ubi); |
| 519 | break; | 506 | break; |
| 520 | } | 507 | } |
| 521 | #endif | 508 | |
| 509 | /* Logical eraseblock map command */ | ||
| 510 | case UBI_IOCEBMAP: | ||
| 511 | { | ||
| 512 | struct ubi_map_req req; | ||
| 513 | |||
| 514 | err = copy_from_user(&req, argp, sizeof(struct ubi_map_req)); | ||
| 515 | if (err) { | ||
| 516 | err = -EFAULT; | ||
| 517 | break; | ||
| 518 | } | ||
| 519 | err = ubi_leb_map(desc, req.lnum, req.dtype); | ||
| 520 | break; | ||
| 521 | } | ||
| 522 | |||
| 523 | /* Logical eraseblock un-map command */ | ||
| 524 | case UBI_IOCEBUNMAP: | ||
| 525 | { | ||
| 526 | int32_t lnum; | ||
| 527 | |||
| 528 | err = get_user(lnum, (__user int32_t *)argp); | ||
| 529 | if (err) { | ||
| 530 | err = -EFAULT; | ||
| 531 | break; | ||
| 532 | } | ||
| 533 | err = ubi_leb_unmap(desc, lnum); | ||
| 534 | break; | ||
| 535 | } | ||
| 536 | |||
| 537 | /* Check if logical eraseblock is mapped command */ | ||
| 538 | case UBI_IOCEBISMAP: | ||
| 539 | { | ||
| 540 | int32_t lnum; | ||
| 541 | |||
| 542 | err = get_user(lnum, (__user int32_t *)argp); | ||
| 543 | if (err) { | ||
| 544 | err = -EFAULT; | ||
| 545 | break; | ||
| 546 | } | ||
| 547 | err = ubi_is_mapped(desc, lnum); | ||
| 548 | break; | ||
| 549 | } | ||
| 550 | |||
| 551 | /* Set volume property command*/ | ||
| 552 | case UBI_IOCSETPROP: | ||
| 553 | { | ||
| 554 | struct ubi_set_prop_req req; | ||
| 555 | |||
| 556 | err = copy_from_user(&req, argp, | ||
| 557 | sizeof(struct ubi_set_prop_req)); | ||
| 558 | if (err) { | ||
| 559 | err = -EFAULT; | ||
| 560 | break; | ||
| 561 | } | ||
| 562 | switch (req.property) { | ||
| 563 | case UBI_PROP_DIRECT_WRITE: | ||
| 564 | mutex_lock(&ubi->volumes_mutex); | ||
| 565 | desc->vol->direct_writes = !!req.value; | ||
| 566 | mutex_unlock(&ubi->volumes_mutex); | ||
| 567 | break; | ||
| 568 | default: | ||
| 569 | err = -EINVAL; | ||
| 570 | break; | ||
| 571 | } | ||
| 572 | break; | ||
| 573 | } | ||
| 522 | 574 | ||
| 523 | default: | 575 | default: |
| 524 | err = -ENOTTY; | 576 | err = -ENOTTY; |
| 525 | break; | 577 | break; |
| 526 | } | 578 | } |
| 527 | |||
| 528 | return err; | 579 | return err; |
| 529 | } | 580 | } |
| 530 | 581 | ||
| @@ -762,8 +813,8 @@ out_free: | |||
| 762 | return err; | 813 | return err; |
| 763 | } | 814 | } |
| 764 | 815 | ||
| 765 | static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | 816 | static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, |
| 766 | unsigned int cmd, unsigned long arg) | 817 | unsigned long arg) |
| 767 | { | 818 | { |
| 768 | int err = 0; | 819 | int err = 0; |
| 769 | struct ubi_device *ubi; | 820 | struct ubi_device *ubi; |
| @@ -773,7 +824,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 773 | if (!capable(CAP_SYS_RESOURCE)) | 824 | if (!capable(CAP_SYS_RESOURCE)) |
| 774 | return -EPERM; | 825 | return -EPERM; |
| 775 | 826 | ||
| 776 | ubi = ubi_get_by_major(imajor(inode)); | 827 | ubi = ubi_get_by_major(imajor(file->f_mapping->host)); |
| 777 | if (!ubi) | 828 | if (!ubi) |
| 778 | return -ENODEV; | 829 | return -ENODEV; |
| 779 | 830 | ||
| @@ -843,7 +894,6 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 843 | case UBI_IOCRSVOL: | 894 | case UBI_IOCRSVOL: |
| 844 | { | 895 | { |
| 845 | int pebs; | 896 | int pebs; |
| 846 | uint64_t tmp; | ||
| 847 | struct ubi_rsvol_req req; | 897 | struct ubi_rsvol_req req; |
| 848 | 898 | ||
| 849 | dbg_gen("re-size volume"); | 899 | dbg_gen("re-size volume"); |
| @@ -863,9 +913,8 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 863 | break; | 913 | break; |
| 864 | } | 914 | } |
| 865 | 915 | ||
| 866 | tmp = req.bytes; | 916 | pebs = div_u64(req.bytes + desc->vol->usable_leb_size - 1, |
| 867 | pebs = !!do_div(tmp, desc->vol->usable_leb_size); | 917 | desc->vol->usable_leb_size); |
| 868 | pebs += tmp; | ||
| 869 | 918 | ||
| 870 | mutex_lock(&ubi->volumes_mutex); | 919 | mutex_lock(&ubi->volumes_mutex); |
| 871 | err = ubi_resize_volume(desc, pebs); | 920 | err = ubi_resize_volume(desc, pebs); |
| @@ -909,8 +958,8 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 909 | return err; | 958 | return err; |
| 910 | } | 959 | } |
| 911 | 960 | ||
| 912 | static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, | 961 | static long ctrl_cdev_ioctl(struct file *file, unsigned int cmd, |
| 913 | unsigned int cmd, unsigned long arg) | 962 | unsigned long arg) |
| 914 | { | 963 | { |
| 915 | int err = 0; | 964 | int err = 0; |
| 916 | void __user *argp = (void __user *)arg; | 965 | void __user *argp = (void __user *)arg; |
| @@ -986,26 +1035,59 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, | |||
| 986 | return err; | 1035 | return err; |
| 987 | } | 1036 | } |
| 988 | 1037 | ||
| 989 | /* UBI control character device operations */ | 1038 | #ifdef CONFIG_COMPAT |
| 990 | struct file_operations ubi_ctrl_cdev_operations = { | 1039 | static long vol_cdev_compat_ioctl(struct file *file, unsigned int cmd, |
| 991 | .ioctl = ctrl_cdev_ioctl, | 1040 | unsigned long arg) |
| 992 | .owner = THIS_MODULE, | 1041 | { |
| 1042 | unsigned long translated_arg = (unsigned long)compat_ptr(arg); | ||
| 1043 | |||
| 1044 | return vol_cdev_ioctl(file, cmd, translated_arg); | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | static long ubi_cdev_compat_ioctl(struct file *file, unsigned int cmd, | ||
| 1048 | unsigned long arg) | ||
| 1049 | { | ||
| 1050 | unsigned long translated_arg = (unsigned long)compat_ptr(arg); | ||
| 1051 | |||
| 1052 | return ubi_cdev_ioctl(file, cmd, translated_arg); | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | static long ctrl_cdev_compat_ioctl(struct file *file, unsigned int cmd, | ||
| 1056 | unsigned long arg) | ||
| 1057 | { | ||
| 1058 | unsigned long translated_arg = (unsigned long)compat_ptr(arg); | ||
| 1059 | |||
| 1060 | return ctrl_cdev_ioctl(file, cmd, translated_arg); | ||
| 1061 | } | ||
| 1062 | #else | ||
| 1063 | #define vol_cdev_compat_ioctl NULL | ||
| 1064 | #define ubi_cdev_compat_ioctl NULL | ||
| 1065 | #define ctrl_cdev_compat_ioctl NULL | ||
| 1066 | #endif | ||
| 1067 | |||
| 1068 | /* UBI volume character device operations */ | ||
| 1069 | const struct file_operations ubi_vol_cdev_operations = { | ||
| 1070 | .owner = THIS_MODULE, | ||
| 1071 | .open = vol_cdev_open, | ||
| 1072 | .release = vol_cdev_release, | ||
| 1073 | .llseek = vol_cdev_llseek, | ||
| 1074 | .read = vol_cdev_read, | ||
| 1075 | .write = vol_cdev_write, | ||
| 1076 | .unlocked_ioctl = vol_cdev_ioctl, | ||
| 1077 | .compat_ioctl = vol_cdev_compat_ioctl, | ||
| 993 | }; | 1078 | }; |
| 994 | 1079 | ||
| 995 | /* UBI character device operations */ | 1080 | /* UBI character device operations */ |
| 996 | struct file_operations ubi_cdev_operations = { | 1081 | const struct file_operations ubi_cdev_operations = { |
| 997 | .owner = THIS_MODULE, | 1082 | .owner = THIS_MODULE, |
| 998 | .ioctl = ubi_cdev_ioctl, | 1083 | .llseek = no_llseek, |
| 999 | .llseek = no_llseek, | 1084 | .unlocked_ioctl = ubi_cdev_ioctl, |
| 1085 | .compat_ioctl = ubi_cdev_compat_ioctl, | ||
| 1000 | }; | 1086 | }; |
| 1001 | 1087 | ||
| 1002 | /* UBI volume character device operations */ | 1088 | /* UBI control character device operations */ |
| 1003 | struct file_operations ubi_vol_cdev_operations = { | 1089 | const struct file_operations ubi_ctrl_cdev_operations = { |
| 1004 | .owner = THIS_MODULE, | 1090 | .owner = THIS_MODULE, |
| 1005 | .open = vol_cdev_open, | 1091 | .unlocked_ioctl = ctrl_cdev_ioctl, |
| 1006 | .release = vol_cdev_release, | 1092 | .compat_ioctl = ctrl_cdev_compat_ioctl, |
| 1007 | .llseek = vol_cdev_llseek, | ||
| 1008 | .read = vol_cdev_read, | ||
| 1009 | .write = vol_cdev_write, | ||
| 1010 | .ioctl = vol_cdev_ioctl, | ||
| 1011 | }; | 1093 | }; |
