From: viro@www.linux.org.uk beginning of fs side work: * new superblock method - ->valid_dev(dev) that checks if particular dev_t value is OK for device inodes on that fs. We don't even try to call ->mknod() if that check fails. Invariant: for any device inode we expect inode->i_sb->s_op->valid_dev(inode->i_rdev) to be true. * helpers for that method - simple_valid_dev() and old_valid_dev(). The former is "everything goes" (ramfs et.al.), the latter is "major and minor are below 256" (i.e. what filesystem should use if it doesn't want to deal with new dev_t values). * super_operations instances updated * new helpers: old_encode_dev() and old_decode_dev(). dev_t -> u16 and u16 -> dev_t resp; currently these are no-ops, places that use current formar (minor in bits 0--7, major in bits 8--15) will switch to these before we widen dev_t. They are supposed to be used by filesystems that have old_valid_dev() as ->valid_dev(); when/if a filesystem switches to wider on-disk dev_t representation it should change its ->valid_dev() along with encoding. arch/ia64/sn/io/hwgfs/ramfs.c | 1 + fs/cifs/cifsfs.c | 1 + fs/coda/inode.c | 1 + fs/devfs/base.c | 1 + fs/ext2/super.c | 1 + fs/ext3/super.c | 1 + fs/hpfs/super.c | 1 + fs/hugetlbfs/inode.c | 1 + fs/intermezzo/inode.c | 1 + fs/intermezzo/vfs.c | 6 +++++- fs/jffs/inode-v23.c | 1 + fs/jffs2/super.c | 3 ++- fs/jfs/super.c | 1 + fs/libfs.c | 27 +++++++++++++++++++++++++++ fs/minix/inode.c | 1 + fs/namei.c | 5 ++++- fs/ncpfs/inode.c | 1 + fs/nfs/inode.c | 1 + fs/ramfs/inode.c | 1 + fs/reiserfs/super.c | 2 +- fs/smbfs/inode.c | 1 + fs/sysv/inode.c | 1 + fs/udf/super.c | 1 + fs/ufs/super.c | 1 + fs/xfs/linux/xfs_super.c | 1 + include/linux/fs.h | 6 +++++- mm/shmem.c | 1 + 27 files changed, 65 insertions(+), 5 deletions(-) diff -puN arch/ia64/sn/io/hwgfs/ramfs.c~large-dev_t-2nd-09 arch/ia64/sn/io/hwgfs/ramfs.c --- 25/arch/ia64/sn/io/hwgfs/ramfs.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/arch/ia64/sn/io/hwgfs/ramfs.c 2003-09-05 00:50:05.000000000 -0700 @@ -164,6 +164,7 @@ static struct inode_operations hwgfs_dir static struct super_operations hwgfs_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, + .valid_dev = old_valid_dev, }; static int hwgfs_fill_super(struct super_block * sb, void * data, int silent) diff -puN fs/cifs/cifsfs.c~large-dev_t-2nd-09 fs/cifs/cifsfs.c --- 25/fs/cifs/cifsfs.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/cifs/cifsfs.c 2003-09-05 00:50:05.000000000 -0700 @@ -370,6 +370,7 @@ struct super_operations cifs_super_ops = us with the same number of releases (closes) as opens */ .show_options = cifs_show_options, /* .umount_begin = cifs_umount_begin, *//* consider adding in the future */ + .valid_dev = old_valid_dev, }; static struct super_block * diff -puN fs/coda/inode.c~large-dev_t-2nd-09 fs/coda/inode.c --- 25/fs/coda/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/coda/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -91,6 +91,7 @@ struct super_operations coda_super_opera .clear_inode = coda_clear_inode, .put_super = coda_put_super, .statfs = coda_statfs, + .valid_dev = old_valid_dev, }; static int get_device_index(struct coda_mount_data *data) diff -puN fs/devfs/base.c~large-dev_t-2nd-09 fs/devfs/base.c --- 25/fs/devfs/base.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/devfs/base.c 2003-09-05 00:50:05.000000000 -0700 @@ -1965,6 +1965,7 @@ static struct super_operations devfs_sop .drop_inode = generic_delete_inode, .clear_inode = devfs_clear_inode, .statfs = simple_statfs, + .valid_dev = simple_valid_dev, }; diff -puN fs/ext2/super.c~large-dev_t-2nd-09 fs/ext2/super.c --- 25/fs/ext2/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/ext2/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -233,6 +233,7 @@ static struct super_operations ext2_sops .statfs = ext2_statfs, .remount_fs = ext2_remount, .clear_inode = ext2_clear_inode, + .valid_dev = old_valid_dev, }; /* Yes, most of these are left as NULL!! diff -puN fs/ext3/super.c~large-dev_t-2nd-09 fs/ext3/super.c --- 25/fs/ext3/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/ext3/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -582,6 +582,7 @@ static struct super_operations ext3_sops .statfs = ext3_statfs, .remount_fs = ext3_remount, .clear_inode = ext3_clear_inode, + .valid_dev = old_valid_dev, }; struct dentry *ext3_get_parent(struct dentry *child); diff -puN fs/hpfs/super.c~large-dev_t-2nd-09 fs/hpfs/super.c --- 25/fs/hpfs/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/hpfs/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -215,6 +215,7 @@ static struct super_operations hpfs_sops .put_super = hpfs_put_super, .statfs = hpfs_statfs, .remount_fs = hpfs_remount_fs, + .valid_dev = old_valid_dev, }; /* diff -puN fs/hugetlbfs/inode.c~large-dev_t-2nd-09 fs/hugetlbfs/inode.c --- 25/fs/hugetlbfs/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/hugetlbfs/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -617,6 +617,7 @@ static struct super_operations hugetlbfs .statfs = hugetlbfs_statfs, .drop_inode = hugetlbfs_drop_inode, .put_super = hugetlbfs_put_super, + .valid_dev = simple_valid_dev, }; static int diff -puN fs/intermezzo/inode.c~large-dev_t-2nd-09 fs/intermezzo/inode.c --- 25/fs/intermezzo/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/intermezzo/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -174,6 +174,7 @@ exit: struct super_operations presto_super_ops = { .read_inode = presto_read_inode, .put_super = presto_put_super, + .valid_dev = old_valid_dev, }; diff -puN fs/intermezzo/vfs.c~large-dev_t-2nd-09 fs/intermezzo/vfs.c --- 25/fs/intermezzo/vfs.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/intermezzo/vfs.c 2003-09-05 00:50:05.000000000 -0700 @@ -1584,7 +1584,11 @@ int presto_do_mknod(struct presto_file_s goto exit_lock2; } - error = iops->mknod(dir->d_inode, dentry, mode, dev); + if (!dir->d_inode->i_sb->s_op->valid_dev || + !dir->d_inode->i_sb->s_op->valid_dev(dev)) + error = -EINVAL; + else + error = iops->mknod(dir->d_inode, dentry, mode, dev); if (error) { EXIT; goto exit_commit; diff -puN fs/jffs2/super.c~large-dev_t-2nd-09 fs/jffs2/super.c --- 25/fs/jffs2/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/jffs2/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -66,7 +66,8 @@ static struct super_operations jffs2_sup .write_super = jffs2_write_super, .statfs = jffs2_statfs, .remount_fs = jffs2_remount_fs, - .clear_inode = jffs2_clear_inode + .clear_inode = jffs2_clear_inode, + .valid_dev = old_valid_dev, }; static int jffs2_sb_compare(struct super_block *sb, void *data) diff -puN fs/jffs/inode-v23.c~large-dev_t-2nd-09 fs/jffs/inode-v23.c --- 25/fs/jffs/inode-v23.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/jffs/inode-v23.c 2003-09-05 00:50:05.000000000 -0700 @@ -1783,6 +1783,7 @@ static struct super_operations jffs_ops .put_super = jffs_put_super, .write_super = jffs_write_super, .statfs = jffs_statfs, + .valid_dev = old_valid_dev, }; static struct super_block *jffs_get_sb(struct file_system_type *fs_type, diff -puN fs/jfs/super.c~large-dev_t-2nd-09 fs/jfs/super.c --- 25/fs/jfs/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/jfs/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -431,6 +431,7 @@ static struct super_operations jfs_super .unlockfs = jfs_unlockfs, .statfs = jfs_statfs, .remount_fs = jfs_remount, + .valid_dev = old_valid_dev, }; static struct export_operations jfs_export_operations = { diff -puN fs/libfs.c~large-dev_t-2nd-09 fs/libfs.c --- 25/fs/libfs.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/libfs.c 2003-09-05 00:50:05.000000000 -0700 @@ -3,6 +3,7 @@ * Library for filesystems writers. */ +#include #include #include #include @@ -428,3 +429,29 @@ void simple_release_fs(struct vfsmount * spin_unlock(&pin_fs_lock); mntput(mnt); } + +/* acceptable for old filesystems */ +int old_valid_dev(dev_t dev) +{ + return MAJOR(dev) < 256 && MINOR(dev) < 256; +} +EXPORT_SYMBOL(old_valid_dev); + +u16 old_encode_dev(dev_t dev) +{ + return (MAJOR(dev) << 8) | MINOR(dev); +} +EXPORT_SYMBOL(old_encode_dev); + +dev_t old_decode_dev(u16 val) +{ + return MKDEV((val >> 8) & 255, val & 255); +} +EXPORT_SYMBOL(old_decode_dev); + +/* anything goes */ +int simple_valid_dev(dev_t dev) +{ + return 1; +} +EXPORT_SYMBOL(simple_valid_dev); diff -puN fs/minix/inode.c~large-dev_t-2nd-09 fs/minix/inode.c --- 25/fs/minix/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/minix/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -101,6 +101,7 @@ static struct super_operations minix_sop .put_super = minix_put_super, .statfs = minix_statfs, .remount_fs = minix_remount, + .valid_dev = old_valid_dev, }; static int minix_remount (struct super_block * sb, int * flags, char * data) diff -puN fs/namei.c~large-dev_t-2nd-09 fs/namei.c --- 25/fs/namei.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/namei.c 2003-09-05 00:50:05.000000000 -0700 @@ -1437,7 +1437,10 @@ int vfs_mknod(struct inode *dir, struct return error; DQUOT_INIT(dir); - error = dir->i_op->mknod(dir, dentry, mode, dev); + if (!dir->i_sb->s_op->valid_dev || !dir->i_sb->s_op->valid_dev(dev)) + error = -EINVAL; + else + error = dir->i_op->mknod(dir, dentry, mode, dev); if (!error) { inode_dir_notify(dir, DN_CREATE); security_inode_post_mknod(dir, dentry, mode, dev); diff -puN fs/ncpfs/inode.c~large-dev_t-2nd-09 fs/ncpfs/inode.c --- 25/fs/ncpfs/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/ncpfs/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -93,6 +93,7 @@ static struct super_operations ncp_sops .delete_inode = ncp_delete_inode, .put_super = ncp_put_super, .statfs = ncp_statfs, + .valid_dev = old_valid_dev, }; extern struct dentry_operations ncp_root_dentry_operations; diff -puN fs/nfs/inode.c~large-dev_t-2nd-09 fs/nfs/inode.c --- 25/fs/nfs/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/nfs/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -66,6 +66,7 @@ static struct super_operations nfs_sops .clear_inode = nfs_clear_inode, .umount_begin = nfs_umount_begin, .show_options = nfs_show_options, + .valid_dev = old_valid_dev, }; /* diff -puN fs/ramfs/inode.c~large-dev_t-2nd-09 fs/ramfs/inode.c --- 25/fs/ramfs/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/ramfs/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -167,6 +167,7 @@ static struct inode_operations ramfs_dir static struct super_operations ramfs_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, + .valid_dev = simple_valid_dev, }; static int ramfs_fill_super(struct super_block * sb, void * data, int silent) diff -puN fs/reiserfs/super.c~large-dev_t-2nd-09 fs/reiserfs/super.c --- 25/fs/reiserfs/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/reiserfs/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -483,7 +483,7 @@ struct super_operations reiserfs_sops = .unlockfs = reiserfs_unlockfs, .statfs = reiserfs_statfs, .remount_fs = reiserfs_remount, - + .valid_dev = old_valid_dev, }; static struct export_operations reiserfs_export_ops = { diff -puN fs/smbfs/inode.c~large-dev_t-2nd-09 fs/smbfs/inode.c --- 25/fs/smbfs/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/smbfs/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -101,6 +101,7 @@ static struct super_operations smb_sops .put_super = smb_put_super, .statfs = smb_statfs, .show_options = smb_show_options, + .valid_dev = old_valid_dev, }; diff -puN fs/sysv/inode.c~large-dev_t-2nd-09 fs/sysv/inode.c --- 25/fs/sysv/inode.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/sysv/inode.c 2003-09-05 00:50:05.000000000 -0700 @@ -322,6 +322,7 @@ struct super_operations sysv_sops = { .put_super = sysv_put_super, .write_super = sysv_write_super, .statfs = sysv_statfs, + .valid_dev = old_valid_dev, }; int __init sysv_init_icache(void) diff -puN fs/udf/super.c~large-dev_t-2nd-09 fs/udf/super.c --- 25/fs/udf/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/udf/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -167,6 +167,7 @@ static struct super_operations udf_sb_op .write_super = udf_write_super, .statfs = udf_statfs, .remount_fs = udf_remount_fs, + .valid_dev = old_valid_dev, }; struct udf_options diff -puN fs/ufs/super.c~large-dev_t-2nd-09 fs/ufs/super.c --- 25/fs/ufs/super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/ufs/super.c 2003-09-05 00:50:05.000000000 -0700 @@ -1052,6 +1052,7 @@ static struct super_operations ufs_super .write_super = ufs_write_super, .statfs = ufs_statfs, .remount_fs = ufs_remount, + .valid_dev = old_valid_dev, }; static struct super_block *ufs_get_sb(struct file_system_type *fs_type, diff -puN fs/xfs/linux/xfs_super.c~large-dev_t-2nd-09 fs/xfs/linux/xfs_super.c --- 25/fs/xfs/linux/xfs_super.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/fs/xfs/linux/xfs_super.c 2003-09-05 00:50:05.000000000 -0700 @@ -822,6 +822,7 @@ STATIC struct super_operations linvfs_so .statfs = linvfs_statfs, .remount_fs = linvfs_remount, .show_options = linvfs_show_options, + .valid_dev = old_valid_dev, }; STATIC struct quotactl_ops linvfs_qops = { diff -puN include/linux/fs.h~large-dev_t-2nd-09 include/linux/fs.h --- 25/include/linux/fs.h~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/include/linux/fs.h 2003-09-05 00:50:05.000000000 -0700 @@ -867,8 +867,8 @@ struct super_operations { int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); - int (*show_options)(struct seq_file *, struct vfsmount *); + int (*valid_dev)(dev_t); }; /* Inode state bits. Protected by inode_lock. */ @@ -1389,6 +1389,10 @@ struct tree_descr { char *name; struct f extern int simple_fill_super(struct super_block *, int, struct tree_descr *); extern int simple_pin_fs(char *name, struct vfsmount **mount, int *count); extern void simple_release_fs(struct vfsmount **mount, int *count); +extern int simple_valid_dev(dev_t); +extern int old_valid_dev(dev_t); +extern u16 old_encode_dev(dev_t); +extern dev_t old_decode_dev(u16); extern int inode_change_ok(struct inode *, struct iattr *); extern int inode_setattr(struct inode *, struct iattr *); diff -puN mm/shmem.c~large-dev_t-2nd-09 mm/shmem.c --- 25/mm/shmem.c~large-dev_t-2nd-09 2003-09-05 00:50:05.000000000 -0700 +++ 25-akpm/mm/shmem.c 2003-09-05 00:50:05.000000000 -0700 @@ -1811,6 +1811,7 @@ static struct super_operations shmem_ops .delete_inode = shmem_delete_inode, .drop_inode = generic_delete_inode, .put_super = shmem_put_super, + .valid_dev = simple_valid_dev, }; static struct vm_operations_struct shmem_vm_ops = { _