#include <linux/capability.h>#include <linux/blkdev.h>#include <linux/gfp.h>#include <linux/blkpg.h>#include <linux/hdreg.h>#include <linux/backing-dev.h>#include <linux/buffer_head.h>#include <linux/blktrace_api.h>#include <asm/uaccess.h>staticintblkpg_ioctl(structblock_device*bdev,structblkpg_ioctl_arg__user*arg){structblock_device*bdevp;structgendisk*disk;structhd_struct*part;structblkpg_ioctl_arga;structblkpg_partitionp;structdisk_part_iterpiter;longlongstart,length;intpartno;if(!capable(CAP_SYS_ADMIN))return-EACCES;if(copy_from_user(&a,arg,sizeof(structblkpg_ioctl_arg)))return-EFAULT;if(copy_from_user(&p,a.data,sizeof(structblkpg_partition)))return-EFAULT;disk=bdev->bd_disk;if(bdev!=bdev->bd_contains)return-EINVAL;partno=p.pno;if(partno<=0)return-EINVAL;switch(a.op){caseBLKPG_ADD_PARTITION:start=p.start>>9;length=p.length>>9;/* check for fit in a hd_struct */if(sizeof(sector_t)==sizeof(long)&&sizeof(longlong)>sizeof(long)){longpstart=start,plength=length;if(pstart!=start||plength!=length||pstart<0||plength<0)return-EINVAL;}mutex_lock(&bdev->bd_mutex);/* overlap? */disk_part_iter_init(&piter,disk,DISK_PITER_INCL_EMPTY);while((part=disk_part_iter_next(&piter))){if(!(start+length<=part->start_sect||start>=part->start_sect+part->nr_sects)){disk_part_iter_exit(&piter);mutex_unlock(&bdev->bd_mutex);return-EBUSY;}}disk_part_iter_exit(&piter);/* all seems OK */part=add_partition(disk,partno,start,length,ADDPART_FLAG_NONE,NULL);mutex_unlock(&bdev->bd_mutex);returnIS_ERR(part)?PTR_ERR(part):0;caseBLKPG_DEL_PARTITION:part=disk_get_part(disk,partno);if(!part)return-ENXIO;bdevp=bdget(part_devt(part));disk_put_part(part);if(!bdevp)return-ENOMEM;mutex_lock(&bdevp->bd_mutex);if(bdevp->bd_openers){mutex_unlock(&bdevp->bd_mutex);bdput(bdevp);return-EBUSY;}/* all seems OK */fsync_bdev(bdevp);invalidate_bdev(bdevp);mutex_lock_nested(&bdev->bd_mutex,1);delete_partition(disk,partno);mutex_unlock(&bdev->bd_mutex);mutex_unlock(&bdevp->bd_mutex);bdput(bdevp);return0;default:return-EINVAL;}}staticintblkdev_reread_part(structblock_device*bdev){structgendisk*disk=bdev->bd_disk;intres;if(!disk_partitionable(disk)||bdev!=bdev->bd_contains)return-EINVAL;if(!capable(CAP_SYS_ADMIN))return-EACCES;if(!mutex_trylock(&bdev->bd_mutex))return-EBUSY;res=rescan_partitions(disk,bdev);mutex_unlock(&bdev->bd_mutex);returnres;}staticintblk_ioctl_discard(structblock_device*bdev,uint64_tstart,uint64_tlen,intsecure){unsignedlongflags=0;if(start&511)return-EINVAL;if(len&511)return-EINVAL;start>>=9;len>>=9;if(start+len>(i_size_read(bdev->bd_inode)>>9))return-EINVAL;if(secure)flags|=BLKDEV_DISCARD_SECURE;returnblkdev_issue_discard(bdev,start,len,GFP_KERNEL,flags);}staticintput_ushort(unsignedlongarg,unsignedshortval){returnput_user(val,(unsignedshort__user*)arg);}staticintput_int(unsignedlongarg,intval){returnput_user(val,(int__user*)arg);}staticintput_uint(unsignedlongarg,unsignedintval){returnput_user(val,(unsignedint__user*)arg);}staticintput_long(unsignedlongarg,longval){returnput_user(val,(long__user*)arg);}staticintput_ulong(unsignedlongarg,unsignedlongval){returnput_user(val,(unsignedlong__user*)arg);}staticintput_u64(unsignedlongarg,u64val){returnput_user(val,(u64__user*)arg);}int__blkdev_driver_ioctl(structblock_device*bdev,fmode_tmode,unsignedcmd,unsignedlongarg){structgendisk*disk=bdev->bd_disk;if(disk->fops->ioctl)returndisk->fops->ioctl(bdev,mode,cmd,arg);return-ENOTTY;}/* * For the record: _GPL here is only because somebody decided to slap it * on the previous export. Sheer idiocy, since it wasn't copyrightable * at all and could be open-coded without any exports by anybody who cares. */EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);/* * always keep this in sync with compat_blkdev_ioctl() */intblkdev_ioctl(structblock_device*bdev,fmode_tmode,unsignedcmd,unsignedlongarg){structgendisk*disk=bdev->bd_disk;structbacking_dev_info*bdi;loff_tsize;intret,n;switch(cmd){caseBLKFLSBUF:if(!capable(CAP_SYS_ADMIN))return-EACCES;ret=__blkdev_driver_ioctl(bdev,mode,cmd,arg);/* -EINVAL to handle old uncorrected drivers */if(ret!=-EINVAL&&ret!=-ENOTTY)returnret;fsync_bdev(bdev);invalidate_bdev(bdev);return0;caseBLKROSET:ret=__blkdev_driver_ioctl(bdev,mode,cmd,arg);/* -EINVAL to handle old uncorrected drivers */if(ret!=-EINVAL&&ret!=-ENOTTY)returnret;if(!capable(CAP_SYS_ADMIN))return-EACCES;if(get_user(n,(int__user*)(arg)))return-EFAULT;set_device_ro(bdev,n);return0;caseBLKDISCARD:caseBLKSECDISCARD:{uint64_trange[2];if(!(mode&FMODE_WRITE))return-EBADF;if(copy_from_user(range,(void__user*)arg,sizeof(range)))return-EFAULT;returnblk_ioctl_discard(bdev,range[0],range[1],cmd==BLKSECDISCARD);}caseHDIO_GETGEO:{structhd_geometrygeo;if(!arg)return-EINVAL;if(!disk->fops->getgeo)return-ENOTTY;/* * We need to set the startsect first, the driver may * want to override it. */memset(&geo,0,sizeof(geo));geo.start=get_start_sect(bdev);ret=disk->fops->getgeo(bdev,&geo);if(ret)returnret;if(copy_to_user((structhd_geometry__user*)arg,&geo,sizeof(geo)))return-EFAULT;return0;}caseBLKRAGET:caseBLKFRAGET:if(!arg)return-EINVAL;bdi=blk_get_backing_dev_info(bdev);if(bdi==NULL)return-ENOTTY;returnput_long(arg,(bdi->ra_pages*PAGE_CACHE_SIZE)/512);caseBLKROGET:returnput_int(arg,bdev_read_only(bdev)!=0);caseBLKBSZGET:/* get block device soft block size (cf. BLKSSZGET) */returnput_int(arg,block_size(bdev));caseBLKSSZGET:/* get block device logical block size */returnput_int(arg,bdev_logical_block_size(bdev));caseBLKPBSZGET:/* get block device physical block size */returnput_uint(arg,bdev_physical_block_size(bdev));caseBLKIOMIN:returnput_uint(arg,bdev_io_min(bdev));caseBLKIOOPT:returnput_uint(arg,bdev_io_opt(bdev));caseBLKALIGNOFF:returnput_int(arg,bdev_alignment_offset(bdev));caseBLKDISCARDZEROES:returnput_uint(arg,bdev_discard_zeroes_data(bdev));caseBLKSECTGET:returnput_ushort(arg,queue_max_sectors(bdev_get_queue(bdev)));caseBLKRASET:caseBLKFRASET:if(!capable(CAP_SYS_ADMIN))return-EACCES;bdi=blk_get_backing_dev_info(bdev);if(bdi==NULL)return-ENOTTY;bdi->ra_pages=(arg*512)/PAGE_CACHE_SIZE;return0;caseBLKBSZSET:/* set the logical block size */if(!capable(CAP_SYS_ADMIN))return-EACCES;if(!arg)return-EINVAL;if(get_user(n,(int__user*)arg))return-EFAULT;if(!(mode&FMODE_EXCL)){bdgrab(bdev);if(blkdev_get(bdev,mode|FMODE_EXCL,&bdev)<0)return-EBUSY;}ret=set_blocksize(bdev,n);if(!(mode&FMODE_EXCL))blkdev_put(bdev,mode|FMODE_EXCL);returnret;caseBLKPG:ret=blkpg_ioctl(bdev,(structblkpg_ioctl_arg__user*)arg);break;caseBLKRRPART:ret=blkdev_reread_part(bdev);break;caseBLKGETSIZE:size=i_size_read(bdev->bd_inode);if((size>>9)>~0UL)return-EFBIG;returnput_ulong(arg,size>>9);caseBLKGETSIZE64:returnput_u64(arg,i_size_read(bdev->bd_inode));caseBLKTRACESTART:caseBLKTRACESTOP:caseBLKTRACESETUP:caseBLKTRACETEARDOWN:ret=blk_trace_ioctl(bdev,cmd,(char__user*)arg);break;default:ret=__blkdev_driver_ioctl(bdev,mode,cmd,arg);}returnret;}EXPORT_SYMBOL_GPL(blkdev_ioctl);