diff options
Diffstat (limited to 'drivers/mtd/mtdchar.c')
-rw-r--r-- | drivers/mtd/mtdchar.c | 105 |
1 files changed, 91 insertions, 14 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b081cb84351..8bb5e4a66328 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -15,12 +15,15 @@ | |||
15 | #include <linux/smp_lock.h> | 15 | #include <linux/smp_lock.h> |
16 | #include <linux/backing-dev.h> | 16 | #include <linux/backing-dev.h> |
17 | #include <linux/compat.h> | 17 | #include <linux/compat.h> |
18 | #include <linux/mount.h> | ||
18 | 19 | ||
19 | #include <linux/mtd/mtd.h> | 20 | #include <linux/mtd/mtd.h> |
20 | #include <linux/mtd/compatmac.h> | 21 | #include <linux/mtd/compatmac.h> |
21 | 22 | ||
22 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
23 | 24 | ||
25 | #define MTD_INODE_FS_MAGIC 0x11307854 | ||
26 | static struct vfsmount *mtd_inode_mnt __read_mostly; | ||
24 | 27 | ||
25 | /* | 28 | /* |
26 | * Data structure to hold the pointer to the mtd device as well | 29 | * Data structure to hold the pointer to the mtd device as well |
@@ -28,6 +31,7 @@ | |||
28 | */ | 31 | */ |
29 | struct mtd_file_info { | 32 | struct mtd_file_info { |
30 | struct mtd_info *mtd; | 33 | struct mtd_info *mtd; |
34 | struct inode *ino; | ||
31 | enum mtd_file_modes mode; | 35 | enum mtd_file_modes mode; |
32 | }; | 36 | }; |
33 | 37 | ||
@@ -64,12 +68,10 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
64 | int ret = 0; | 68 | int ret = 0; |
65 | struct mtd_info *mtd; | 69 | struct mtd_info *mtd; |
66 | struct mtd_file_info *mfi; | 70 | struct mtd_file_info *mfi; |
71 | struct inode *mtd_ino; | ||
67 | 72 | ||
68 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); | 73 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); |
69 | 74 | ||
70 | if (devnum >= MAX_MTD_DEVICES) | ||
71 | return -ENODEV; | ||
72 | |||
73 | /* You can't open the RO devices RW */ | 75 | /* You can't open the RO devices RW */ |
74 | if ((file->f_mode & FMODE_WRITE) && (minor & 1)) | 76 | if ((file->f_mode & FMODE_WRITE) && (minor & 1)) |
75 | return -EACCES; | 77 | return -EACCES; |
@@ -88,11 +90,23 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
88 | goto out; | 90 | goto out; |
89 | } | 91 | } |
90 | 92 | ||
91 | if (mtd->backing_dev_info) | 93 | mtd_ino = iget_locked(mtd_inode_mnt->mnt_sb, devnum); |
92 | file->f_mapping->backing_dev_info = mtd->backing_dev_info; | 94 | if (!mtd_ino) { |
95 | put_mtd_device(mtd); | ||
96 | ret = -ENOMEM; | ||
97 | goto out; | ||
98 | } | ||
99 | if (mtd_ino->i_state & I_NEW) { | ||
100 | mtd_ino->i_private = mtd; | ||
101 | mtd_ino->i_mode = S_IFCHR; | ||
102 | mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info; | ||
103 | unlock_new_inode(mtd_ino); | ||
104 | } | ||
105 | file->f_mapping = mtd_ino->i_mapping; | ||
93 | 106 | ||
94 | /* You can't open it RW if it's not a writeable device */ | 107 | /* You can't open it RW if it's not a writeable device */ |
95 | if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { | 108 | if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { |
109 | iput(mtd_ino); | ||
96 | put_mtd_device(mtd); | 110 | put_mtd_device(mtd); |
97 | ret = -EACCES; | 111 | ret = -EACCES; |
98 | goto out; | 112 | goto out; |
@@ -100,10 +114,12 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
100 | 114 | ||
101 | mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); | 115 | mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); |
102 | if (!mfi) { | 116 | if (!mfi) { |
117 | iput(mtd_ino); | ||
103 | put_mtd_device(mtd); | 118 | put_mtd_device(mtd); |
104 | ret = -ENOMEM; | 119 | ret = -ENOMEM; |
105 | goto out; | 120 | goto out; |
106 | } | 121 | } |
122 | mfi->ino = mtd_ino; | ||
107 | mfi->mtd = mtd; | 123 | mfi->mtd = mtd; |
108 | file->private_data = mfi; | 124 | file->private_data = mfi; |
109 | 125 | ||
@@ -125,6 +141,8 @@ static int mtd_close(struct inode *inode, struct file *file) | |||
125 | if ((file->f_mode & FMODE_WRITE) && mtd->sync) | 141 | if ((file->f_mode & FMODE_WRITE) && mtd->sync) |
126 | mtd->sync(mtd); | 142 | mtd->sync(mtd); |
127 | 143 | ||
144 | iput(mfi->ino); | ||
145 | |||
128 | put_mtd_device(mtd); | 146 | put_mtd_device(mtd); |
129 | file->private_data = NULL; | 147 | file->private_data = NULL; |
130 | kfree(mfi); | 148 | kfree(mfi); |
@@ -373,7 +391,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, | |||
373 | if (!mtd->write_oob) | 391 | if (!mtd->write_oob) |
374 | ret = -EOPNOTSUPP; | 392 | ret = -EOPNOTSUPP; |
375 | else | 393 | else |
376 | ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT; | 394 | ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT; |
377 | 395 | ||
378 | if (ret) | 396 | if (ret) |
379 | return ret; | 397 | return ret; |
@@ -482,7 +500,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
482 | { | 500 | { |
483 | uint32_t ur_idx; | 501 | uint32_t ur_idx; |
484 | struct mtd_erase_region_info *kr; | 502 | struct mtd_erase_region_info *kr; |
485 | struct region_info_user *ur = (struct region_info_user *) argp; | 503 | struct region_info_user __user *ur = argp; |
486 | 504 | ||
487 | if (get_user(ur_idx, &(ur->regionindex))) | 505 | if (get_user(ur_idx, &(ur->regionindex))) |
488 | return -EFAULT; | 506 | return -EFAULT; |
@@ -954,22 +972,81 @@ static const struct file_operations mtd_fops = { | |||
954 | #endif | 972 | #endif |
955 | }; | 973 | }; |
956 | 974 | ||
975 | static int mtd_inodefs_get_sb(struct file_system_type *fs_type, int flags, | ||
976 | const char *dev_name, void *data, | ||
977 | struct vfsmount *mnt) | ||
978 | { | ||
979 | return get_sb_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC, | ||
980 | mnt); | ||
981 | } | ||
982 | |||
983 | static struct file_system_type mtd_inodefs_type = { | ||
984 | .name = "mtd_inodefs", | ||
985 | .get_sb = mtd_inodefs_get_sb, | ||
986 | .kill_sb = kill_anon_super, | ||
987 | }; | ||
988 | |||
989 | static void mtdchar_notify_add(struct mtd_info *mtd) | ||
990 | { | ||
991 | } | ||
992 | |||
993 | static void mtdchar_notify_remove(struct mtd_info *mtd) | ||
994 | { | ||
995 | struct inode *mtd_ino = ilookup(mtd_inode_mnt->mnt_sb, mtd->index); | ||
996 | |||
997 | if (mtd_ino) { | ||
998 | /* Destroy the inode if it exists */ | ||
999 | mtd_ino->i_nlink = 0; | ||
1000 | iput(mtd_ino); | ||
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | static struct mtd_notifier mtdchar_notifier = { | ||
1005 | .add = mtdchar_notify_add, | ||
1006 | .remove = mtdchar_notify_remove, | ||
1007 | }; | ||
1008 | |||
957 | static int __init init_mtdchar(void) | 1009 | static int __init init_mtdchar(void) |
958 | { | 1010 | { |
959 | int status; | 1011 | int ret; |
960 | 1012 | ||
961 | status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops); | 1013 | ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, |
962 | if (status < 0) { | 1014 | "mtd", &mtd_fops); |
963 | printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", | 1015 | if (ret < 0) { |
964 | MTD_CHAR_MAJOR); | 1016 | pr_notice("Can't allocate major number %d for " |
1017 | "Memory Technology Devices.\n", MTD_CHAR_MAJOR); | ||
1018 | return ret; | ||
965 | } | 1019 | } |
966 | 1020 | ||
967 | return status; | 1021 | ret = register_filesystem(&mtd_inodefs_type); |
1022 | if (ret) { | ||
1023 | pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret); | ||
1024 | goto err_unregister_chdev; | ||
1025 | } | ||
1026 | |||
1027 | mtd_inode_mnt = kern_mount(&mtd_inodefs_type); | ||
1028 | if (IS_ERR(mtd_inode_mnt)) { | ||
1029 | ret = PTR_ERR(mtd_inode_mnt); | ||
1030 | pr_notice("Error mounting mtd_inodefs filesystem: %d\n", ret); | ||
1031 | goto err_unregister_filesystem; | ||
1032 | } | ||
1033 | register_mtd_user(&mtdchar_notifier); | ||
1034 | |||
1035 | return ret; | ||
1036 | |||
1037 | err_unregister_filesystem: | ||
1038 | unregister_filesystem(&mtd_inodefs_type); | ||
1039 | err_unregister_chdev: | ||
1040 | __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); | ||
1041 | return ret; | ||
968 | } | 1042 | } |
969 | 1043 | ||
970 | static void __exit cleanup_mtdchar(void) | 1044 | static void __exit cleanup_mtdchar(void) |
971 | { | 1045 | { |
972 | unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); | 1046 | unregister_mtd_user(&mtdchar_notifier); |
1047 | mntput(mtd_inode_mnt); | ||
1048 | unregister_filesystem(&mtd_inodefs_type); | ||
1049 | __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); | ||
973 | } | 1050 | } |
974 | 1051 | ||
975 | module_init(init_mtdchar); | 1052 | module_init(init_mtdchar); |