Merge branch 'pmtimer-overflow' into release
[safe/jmp/linux-2.6] / drivers / mtd / mtdchar.c
index 61a994e..e9ec59e 100644 (file)
@@ -1,18 +1,18 @@
 /*
- * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $
- *
  * Character-device access to raw MTD devices.
  *
  */
 
 #include <linux/device.h>
 #include <linux/fs.h>
+#include <linux/mm.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/smp_lock.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
@@ -26,12 +26,11 @@ static void mtd_notify_add(struct mtd_info* mtd)
        if (!mtd)
                return;
 
-       class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
-                           NULL, "mtd%d", mtd->index);
+       device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
+                     NULL, "mtd%d", mtd->index);
 
-       class_device_create(mtd_class, NULL,
-                           MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
-                           NULL, "mtd%dro", mtd->index);
+       device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
+                     NULL, "mtd%dro", mtd->index);
 }
 
 static void mtd_notify_remove(struct mtd_info* mtd)
@@ -39,8 +38,8 @@ static void mtd_notify_remove(struct mtd_info* mtd)
        if (!mtd)
                return;
 
-       class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
-       class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
+       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
+       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
 }
 
 static struct mtd_notifier notifier = {
@@ -87,6 +86,7 @@ static int mtd_open(struct inode *inode, struct file *file)
 {
        int minor = iminor(inode);
        int devnum = minor >> 1;
+       int ret = 0;
        struct mtd_info *mtd;
        struct mtd_file_info *mfi;
 
@@ -96,34 +96,42 @@ static int mtd_open(struct inode *inode, struct file *file)
                return -ENODEV;
 
        /* You can't open the RO devices RW */
-       if ((file->f_mode & 2) && (minor & 1))
+       if ((file->f_mode & FMODE_WRITE) && (minor & 1))
                return -EACCES;
 
+       lock_kernel();
        mtd = get_mtd_device(NULL, devnum);
 
-       if (IS_ERR(mtd))
-               return PTR_ERR(mtd);
+       if (IS_ERR(mtd)) {
+               ret = PTR_ERR(mtd);
+               goto out;
+       }
 
        if (MTD_ABSENT == mtd->type) {
                put_mtd_device(mtd);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto out;
        }
 
        /* You can't open it RW if it's not a writeable device */
-       if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
+       if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
                put_mtd_device(mtd);
-               return -EACCES;
+               ret = -EACCES;
+               goto out;
        }
 
        mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
        if (!mfi) {
                put_mtd_device(mtd);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out;
        }
        mfi->mtd = mtd;
        file->private_data = mfi;
 
-       return 0;
+out:
+       unlock_kernel();
+       return ret;
 } /* mtd_open */
 
 /*====================================================================*/
@@ -135,7 +143,8 @@ static int mtd_close(struct inode *inode, struct file *file)
 
        DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 
-       if (mtd->sync)
+       /* Only sync if opened RW */
+       if ((file->f_mode & FMODE_WRITE) && mtd->sync)
                mtd->sync(mtd);
 
        put_mtd_device(mtd);
@@ -339,7 +348,7 @@ static void mtdchar_erase_callback (struct erase_info *instr)
        wake_up((wait_queue_head_t *)instr->priv);
 }
 
-#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
+#ifdef CONFIG_HAVE_MTD_OTP
 static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
 {
        struct mtd_info *mtd = mfi->mtd;
@@ -399,16 +408,20 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case MEMGETREGIONINFO:
        {
-               struct region_info_user ur;
+               uint32_t ur_idx;
+               struct mtd_erase_region_info *kr;
+               struct region_info_user *ur = (struct region_info_user *) argp;
 
-               if (copy_from_user(&ur, argp, sizeof(struct region_info_user)))
+               if (get_user(ur_idx, &(ur->regionindex)))
                        return -EFAULT;
 
-               if (ur.regionindex >= mtd->numeraseregions)
-                       return -EINVAL;
-               if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]),
-                               sizeof(struct mtd_erase_region_info)))
+               kr = &(mtd->eraseregions[ur_idx]);
+
+               if (put_user(kr->offset, &(ur->offset))
+                   || put_user(kr->erasesize, &(ur->erasesize))
+                   || put_user(kr->numblocks, &(ur->numblocks)))
                        return -EFAULT;
+
                break;
        }
 
@@ -419,8 +432,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                info.erasesize  = mtd->erasesize;
                info.writesize  = mtd->writesize;
                info.oobsize    = mtd->oobsize;
-               info.ecctype    = mtd->ecctype;
-               info.eccsize    = mtd->eccsize;
+               /* The below fields are obsolete */
+               info.ecctype    = -1;
+               info.eccsize    = 0;
                if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
                        return -EFAULT;
                break;
@@ -429,23 +443,27 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        {
                struct erase_info *erase;
 
-               if(!(file->f_mode & 2))
+               if(!(file->f_mode & FMODE_WRITE))
                        return -EPERM;
 
                erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
                if (!erase)
                        ret = -ENOMEM;
                else {
+                       struct erase_info_user einfo;
+
                        wait_queue_head_t waitq;
                        DECLARE_WAITQUEUE(wait, current);
 
                        init_waitqueue_head(&waitq);
 
-                       if (copy_from_user(&erase->addr, argp,
+                       if (copy_from_user(&einfo, argp,
                                    sizeof(struct erase_info_user))) {
                                kfree(erase);
                                return -EFAULT;
                        }
+                       erase->addr = einfo.start;
+                       erase->len = einfo.length;
                        erase->mtd = mtd;
                        erase->callback = mtdchar_erase_callback;
                        erase->priv = (unsigned long)&waitq;
@@ -480,8 +498,10 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        {
                struct mtd_oob_buf buf;
                struct mtd_oob_ops ops;
+               struct mtd_oob_buf __user *user_buf = argp;
+               uint32_t retlen;
 
-               if(!(file->f_mode & 2))
+               if(!(file->f_mode & FMODE_WRITE))
                        return -EPERM;
 
                if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
@@ -519,8 +539,10 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                buf.start &= ~(mtd->oobsize - 1);
                ret = mtd->write_oob(mtd, buf.start, &ops);
 
-               if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen,
-                                sizeof(uint32_t)))
+               if (ops.oobretlen > 0xFFFFFFFFU)
+                       ret = -EOVERFLOW;
+               retlen = ops.oobretlen;
+               if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
                        ret = -EFAULT;
 
                kfree(ops.oobbuf);
@@ -552,7 +574,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                ops.datbuf = NULL;
                ops.mode = MTD_OOB_PLACE;
 
-               if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
+               if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
                        return -EINVAL;
 
                ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
@@ -574,29 +596,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case MEMLOCK:
        {
-               struct erase_info_user info;
+               struct erase_info_user einfo;
 
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&einfo, argp, sizeof(einfo)))
                        return -EFAULT;
 
                if (!mtd->lock)
                        ret = -EOPNOTSUPP;
                else
-                       ret = mtd->lock(mtd, info.start, info.length);
+                       ret = mtd->lock(mtd, einfo.start, einfo.length);
                break;
        }
 
        case MEMUNLOCK:
        {
-               struct erase_info_user info;
+               struct erase_info_user einfo;
 
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&einfo, argp, sizeof(einfo)))
                        return -EFAULT;
 
                if (!mtd->unlock)
                        ret = -EOPNOTSUPP;
                else
-                       ret = mtd->unlock(mtd, info.start, info.length);
+                       ret = mtd->unlock(mtd, einfo.start, einfo.length);
                break;
        }
 
@@ -647,7 +669,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
-#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
+#ifdef CONFIG_HAVE_MTD_OTP
        case OTPSELECT:
        {
                int mode;
@@ -696,15 +718,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case OTPLOCK:
        {
-               struct otp_info info;
+               struct otp_info oinfo;
 
                if (mfi->mode != MTD_MODE_OTP_USER)
                        return -EINVAL;
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
                        return -EFAULT;
                if (!mtd->lock_user_prot_reg)
                        return -EOPNOTSUPP;
-               ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
+               ret = mtd->lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
                break;
        }
 #endif