diff options
Diffstat (limited to 'drivers/mtd/mtdchar.c')
| -rw-r--r-- | drivers/mtd/mtdchar.c | 47 |
1 files changed, 30 insertions, 17 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 684bfa39e4e..a0f54e80670 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -32,6 +32,7 @@ #include <linux/mount.h> #include <linux/blkpg.h> #include <linux/magic.h> +#include <linux/major.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/mtd/map.h> @@ -323,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c default: ret = mtd_write(mtd, *ppos, len, &retlen, kbuf); } + + /* + * Return -ENOSPC only if no data could be written at all. + * Otherwise just return the number of bytes that actually + * have been written. + */ + if ((ret == -ENOSPC) && (total_retlen)) + break; + if (!ret) { *ppos += retlen; total_retlen += retlen; @@ -558,13 +568,18 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, { struct mtd_write_req req; struct mtd_oob_ops ops; - void __user *usr_data, *usr_oob; + const void __user *usr_data, *usr_oob; int ret; - if (copy_from_user(&req, argp, sizeof(req)) || - !access_ok(VERIFY_READ, req.usr_data, req.len) || - !access_ok(VERIFY_READ, req.usr_oob, req.ooblen)) + if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; + + usr_data = (const void __user *)(uintptr_t)req.usr_data; + usr_oob = (const void __user *)(uintptr_t)req.usr_oob; + if (!access_ok(VERIFY_READ, usr_data, req.len) || + !access_ok(VERIFY_READ, usr_oob, req.ooblen)) + return -EFAULT; + if (!mtd->_write_oob) return -EOPNOTSUPP; @@ -573,10 +588,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, ops.ooblen = (size_t)req.ooblen; ops.ooboffs = 0; - usr_data = (void __user *)(uintptr_t)req.usr_data; - usr_oob = (void __user *)(uintptr_t)req.usr_oob; - - if (req.usr_data) { + if (usr_data) { ops.datbuf = memdup_user(usr_data, ops.len); if (IS_ERR(ops.datbuf)) return PTR_ERR(ops.datbuf); @@ -584,7 +596,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, ops.datbuf = NULL; } - if (req.usr_oob) { + if (usr_oob) { ops.oobbuf = memdup_user(usr_oob, ops.ooblen); if (IS_ERR(ops.oobbuf)) { kfree(ops.datbuf); @@ -888,25 +900,26 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) case OTPGETREGIONINFO: { struct otp_info *buf = kmalloc(4096, GFP_KERNEL); + size_t retlen; if (!buf) return -ENOMEM; switch (mfi->mode) { case MTD_FILE_MODE_OTP_FACTORY: - ret = mtd_get_fact_prot_info(mtd, buf, 4096); + ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf); break; case MTD_FILE_MODE_OTP_USER: - ret = mtd_get_user_prot_info(mtd, buf, 4096); + ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf); break; default: ret = -EINVAL; break; } - if (ret >= 0) { + if (!ret) { if (cmd == OTPGETREGIONCOUNT) { - int nbr = ret / sizeof(struct otp_info); + int nbr = retlen / sizeof(struct otp_info); ret = copy_to_user(argp, &nbr, sizeof(int)); } else - ret = copy_to_user(argp, buf, ret); + ret = copy_to_user(argp, buf, retlen); if (ret) ret = -EFAULT; } @@ -1099,7 +1112,7 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file, return (unsigned long) -EINVAL; ret = mtd_get_unmapped_area(mtd, len, offset, flags); - return ret == -EOPNOTSUPP ? -ENOSYS : ret; + return ret == -EOPNOTSUPP ? -ENODEV : ret; } #endif @@ -1124,9 +1137,9 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma) #endif return vm_iomap_memory(vma, map->phys, map->size); } - return -ENOSYS; + return -ENODEV; #else - return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS; + return vma->vm_flags & VM_SHARED ? 0 : -EACCES; #endif } |
