loop.c

来自「linux 内核源代码」· C语言 代码 · 共 1,565 行 · 第 1/3 页

C
1,565
字号
	memset(info, 0, sizeof(*info));	info->lo_number = info64->lo_number;	info->lo_device = info64->lo_device;	info->lo_inode = info64->lo_inode;	info->lo_rdevice = info64->lo_rdevice;	info->lo_offset = info64->lo_offset;	info->lo_encrypt_type = info64->lo_encrypt_type;	info->lo_encrypt_key_size = info64->lo_encrypt_key_size;	info->lo_flags = info64->lo_flags;	info->lo_init[0] = info64->lo_init[0];	info->lo_init[1] = info64->lo_init[1];	if (info->lo_encrypt_type == LO_CRYPT_CRYPTOAPI)		memcpy(info->lo_name, info64->lo_crypt_name, LO_NAME_SIZE);	else		memcpy(info->lo_name, info64->lo_file_name, LO_NAME_SIZE);	memcpy(info->lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE);	/* error in case values were truncated */	if (info->lo_device != info64->lo_device ||	    info->lo_rdevice != info64->lo_rdevice ||	    info->lo_inode != info64->lo_inode ||	    info->lo_offset != info64->lo_offset)		return -EOVERFLOW;	return 0;}static intloop_set_status_old(struct loop_device *lo, const struct loop_info __user *arg){	struct loop_info info;	struct loop_info64 info64;	if (copy_from_user(&info, arg, sizeof (struct loop_info)))		return -EFAULT;	loop_info64_from_old(&info, &info64);	return loop_set_status(lo, &info64);}static intloop_set_status64(struct loop_device *lo, const struct loop_info64 __user *arg){	struct loop_info64 info64;	if (copy_from_user(&info64, arg, sizeof (struct loop_info64)))		return -EFAULT;	return loop_set_status(lo, &info64);}static intloop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) {	struct loop_info info;	struct loop_info64 info64;	int err = 0;	if (!arg)		err = -EINVAL;	if (!err)		err = loop_get_status(lo, &info64);	if (!err)		err = loop_info64_to_old(&info64, &info);	if (!err && copy_to_user(arg, &info, sizeof(info)))		err = -EFAULT;	return err;}static intloop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {	struct loop_info64 info64;	int err = 0;	if (!arg)		err = -EINVAL;	if (!err)		err = loop_get_status(lo, &info64);	if (!err && copy_to_user(arg, &info64, sizeof(info64)))		err = -EFAULT;	return err;}static int lo_ioctl(struct inode * inode, struct file * file,	unsigned int cmd, unsigned long arg){	struct loop_device *lo = inode->i_bdev->bd_disk->private_data;	int err;	mutex_lock(&lo->lo_ctl_mutex);	switch (cmd) {	case LOOP_SET_FD:		err = loop_set_fd(lo, file, inode->i_bdev, arg);		break;	case LOOP_CHANGE_FD:		err = loop_change_fd(lo, file, inode->i_bdev, arg);		break;	case LOOP_CLR_FD:		err = loop_clr_fd(lo, inode->i_bdev);		break;	case LOOP_SET_STATUS:		err = loop_set_status_old(lo, (struct loop_info __user *) arg);		break;	case LOOP_GET_STATUS:		err = loop_get_status_old(lo, (struct loop_info __user *) arg);		break;	case LOOP_SET_STATUS64:		err = loop_set_status64(lo, (struct loop_info64 __user *) arg);		break;	case LOOP_GET_STATUS64:		err = loop_get_status64(lo, (struct loop_info64 __user *) arg);		break;	default:		err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;	}	mutex_unlock(&lo->lo_ctl_mutex);	return err;}#ifdef CONFIG_COMPATstruct compat_loop_info {	compat_int_t	lo_number;      /* ioctl r/o */	compat_dev_t	lo_device;      /* ioctl r/o */	compat_ulong_t	lo_inode;       /* ioctl r/o */	compat_dev_t	lo_rdevice;     /* ioctl r/o */	compat_int_t	lo_offset;	compat_int_t	lo_encrypt_type;	compat_int_t	lo_encrypt_key_size;    /* ioctl w/o */	compat_int_t	lo_flags;       /* ioctl r/o */	char		lo_name[LO_NAME_SIZE];	unsigned char	lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */	compat_ulong_t	lo_init[2];	char		reserved[4];};/* * Transfer 32-bit compatibility structure in userspace to 64-bit loop info * - noinlined to reduce stack space usage in main part of driver */static noinline intloop_info64_from_compat(const struct compat_loop_info __user *arg,			struct loop_info64 *info64){	struct compat_loop_info info;	if (copy_from_user(&info, arg, sizeof(info)))		return -EFAULT;	memset(info64, 0, sizeof(*info64));	info64->lo_number = info.lo_number;	info64->lo_device = info.lo_device;	info64->lo_inode = info.lo_inode;	info64->lo_rdevice = info.lo_rdevice;	info64->lo_offset = info.lo_offset;	info64->lo_sizelimit = 0;	info64->lo_encrypt_type = info.lo_encrypt_type;	info64->lo_encrypt_key_size = info.lo_encrypt_key_size;	info64->lo_flags = info.lo_flags;	info64->lo_init[0] = info.lo_init[0];	info64->lo_init[1] = info.lo_init[1];	if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)		memcpy(info64->lo_crypt_name, info.lo_name, LO_NAME_SIZE);	else		memcpy(info64->lo_file_name, info.lo_name, LO_NAME_SIZE);	memcpy(info64->lo_encrypt_key, info.lo_encrypt_key, LO_KEY_SIZE);	return 0;}/* * Transfer 64-bit loop info to 32-bit compatibility structure in userspace * - noinlined to reduce stack space usage in main part of driver */static noinline intloop_info64_to_compat(const struct loop_info64 *info64,		      struct compat_loop_info __user *arg){	struct compat_loop_info info;	memset(&info, 0, sizeof(info));	info.lo_number = info64->lo_number;	info.lo_device = info64->lo_device;	info.lo_inode = info64->lo_inode;	info.lo_rdevice = info64->lo_rdevice;	info.lo_offset = info64->lo_offset;	info.lo_encrypt_type = info64->lo_encrypt_type;	info.lo_encrypt_key_size = info64->lo_encrypt_key_size;	info.lo_flags = info64->lo_flags;	info.lo_init[0] = info64->lo_init[0];	info.lo_init[1] = info64->lo_init[1];	if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)		memcpy(info.lo_name, info64->lo_crypt_name, LO_NAME_SIZE);	else		memcpy(info.lo_name, info64->lo_file_name, LO_NAME_SIZE);	memcpy(info.lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE);	/* error in case values were truncated */	if (info.lo_device != info64->lo_device ||	    info.lo_rdevice != info64->lo_rdevice ||	    info.lo_inode != info64->lo_inode ||	    info.lo_offset != info64->lo_offset ||	    info.lo_init[0] != info64->lo_init[0] ||	    info.lo_init[1] != info64->lo_init[1])		return -EOVERFLOW;	if (copy_to_user(arg, &info, sizeof(info)))		return -EFAULT;	return 0;}static intloop_set_status_compat(struct loop_device *lo,		       const struct compat_loop_info __user *arg){	struct loop_info64 info64;	int ret;	ret = loop_info64_from_compat(arg, &info64);	if (ret < 0)		return ret;	return loop_set_status(lo, &info64);}static intloop_get_status_compat(struct loop_device *lo,		       struct compat_loop_info __user *arg){	struct loop_info64 info64;	int err = 0;	if (!arg)		err = -EINVAL;	if (!err)		err = loop_get_status(lo, &info64);	if (!err)		err = loop_info64_to_compat(&info64, arg);	return err;}static long lo_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg){	struct inode *inode = file->f_path.dentry->d_inode;	struct loop_device *lo = inode->i_bdev->bd_disk->private_data;	int err;	switch(cmd) {	case LOOP_SET_STATUS:		mutex_lock(&lo->lo_ctl_mutex);		err = loop_set_status_compat(			lo, (const struct compat_loop_info __user *) arg);		mutex_unlock(&lo->lo_ctl_mutex);		break;	case LOOP_GET_STATUS:		mutex_lock(&lo->lo_ctl_mutex);		err = loop_get_status_compat(			lo, (struct compat_loop_info __user *) arg);		mutex_unlock(&lo->lo_ctl_mutex);		break;	case LOOP_CLR_FD:	case LOOP_GET_STATUS64:	case LOOP_SET_STATUS64:		arg = (unsigned long) compat_ptr(arg);	case LOOP_SET_FD:	case LOOP_CHANGE_FD:		err = lo_ioctl(inode, file, cmd, arg);		break;	default:		err = -ENOIOCTLCMD;		break;	}	return err;}#endifstatic int lo_open(struct inode *inode, struct file *file){	struct loop_device *lo = inode->i_bdev->bd_disk->private_data;	mutex_lock(&lo->lo_ctl_mutex);	lo->lo_refcnt++;	mutex_unlock(&lo->lo_ctl_mutex);	return 0;}static int lo_release(struct inode *inode, struct file *file){	struct loop_device *lo = inode->i_bdev->bd_disk->private_data;	mutex_lock(&lo->lo_ctl_mutex);	--lo->lo_refcnt;	mutex_unlock(&lo->lo_ctl_mutex);	return 0;}static struct block_device_operations lo_fops = {	.owner =	THIS_MODULE,	.open =		lo_open,	.release =	lo_release,	.ioctl =	lo_ioctl,#ifdef CONFIG_COMPAT	.compat_ioctl =	lo_compat_ioctl,#endif};/* * And now the modules code and kernel interface. */static int max_loop;module_param(max_loop, int, 0);MODULE_PARM_DESC(max_loop, "Maximum number of loop devices");MODULE_LICENSE("GPL");MODULE_ALIAS_BLOCKDEV_MAJOR(LOOP_MAJOR);int loop_register_transfer(struct loop_func_table *funcs){	unsigned int n = funcs->number;	if (n >= MAX_LO_CRYPT || xfer_funcs[n])		return -EINVAL;	xfer_funcs[n] = funcs;	return 0;}int loop_unregister_transfer(int number){	unsigned int n = number;	struct loop_device *lo;	struct loop_func_table *xfer;	if (n == 0 || n >= MAX_LO_CRYPT || (xfer = xfer_funcs[n]) == NULL)		return -EINVAL;	xfer_funcs[n] = NULL;	list_for_each_entry(lo, &loop_devices, lo_list) {		mutex_lock(&lo->lo_ctl_mutex);		if (lo->lo_encryption == xfer)			loop_release_xfer(lo);		mutex_unlock(&lo->lo_ctl_mutex);	}	return 0;}EXPORT_SYMBOL(loop_register_transfer);EXPORT_SYMBOL(loop_unregister_transfer);static struct loop_device *loop_alloc(int i){	struct loop_device *lo;	struct gendisk *disk;	lo = kzalloc(sizeof(*lo), GFP_KERNEL);	if (!lo)		goto out;	lo->lo_queue = blk_alloc_queue(GFP_KERNEL);	if (!lo->lo_queue)		goto out_free_dev;	disk = lo->lo_disk = alloc_disk(1);	if (!disk)		goto out_free_queue;	mutex_init(&lo->lo_ctl_mutex);	lo->lo_number		= i;	lo->lo_thread		= NULL;	init_waitqueue_head(&lo->lo_event);	spin_lock_init(&lo->lo_lock);	disk->major		= LOOP_MAJOR;	disk->first_minor	= i;	disk->fops		= &lo_fops;	disk->private_data	= lo;	disk->queue		= lo->lo_queue;	sprintf(disk->disk_name, "loop%d", i);	return lo;out_free_queue:	blk_cleanup_queue(lo->lo_queue);out_free_dev:	kfree(lo);out:	return NULL;}static void loop_free(struct loop_device *lo){	blk_cleanup_queue(lo->lo_queue);	put_disk(lo->lo_disk);	list_del(&lo->lo_list);	kfree(lo);}static struct loop_device *loop_init_one(int i){	struct loop_device *lo;	list_for_each_entry(lo, &loop_devices, lo_list) {		if (lo->lo_number == i)			return lo;	}	lo = loop_alloc(i);	if (lo) {		add_disk(lo->lo_disk);		list_add_tail(&lo->lo_list, &loop_devices);	}	return lo;}static void loop_del_one(struct loop_device *lo){	del_gendisk(lo->lo_disk);	loop_free(lo);}static struct kobject *loop_probe(dev_t dev, int *part, void *data){	struct loop_device *lo;	struct kobject *kobj;	mutex_lock(&loop_devices_mutex);	lo = loop_init_one(dev & MINORMASK);	kobj = lo ? get_disk(lo->lo_disk) : ERR_PTR(-ENOMEM);	mutex_unlock(&loop_devices_mutex);	*part = 0;	return kobj;}static int __init loop_init(void){	int i, nr;	unsigned long range;	struct loop_device *lo, *next;	/*	 * loop module now has a feature to instantiate underlying device	 * structure on-demand, provided that there is an access dev node.	 * However, this will not work well with user space tool that doesn't	 * know about such "feature".  In order to not break any existing	 * tool, we do the following:	 *	 * (1) if max_loop is specified, create that many upfront, and this	 *     also becomes a hard limit.	 * (2) if max_loop is not specified, create 8 loop device on module	 *     load, user can further extend loop device by create dev node	 *     themselves and have kernel automatically instantiate actual	 *     device on-demand.	 */	if (max_loop > 1UL << MINORBITS)		return -EINVAL;	if (max_loop) {		nr = max_loop;		range = max_loop;	} else {		nr = 8;		range = 1UL << MINORBITS;	}	if (register_blkdev(LOOP_MAJOR, "loop"))		return -EIO;	for (i = 0; i < nr; i++) {		lo = loop_alloc(i);		if (!lo)			goto Enomem;		list_add_tail(&lo->lo_list, &loop_devices);	}	/* point of no return */	list_for_each_entry(lo, &loop_devices, lo_list)		add_disk(lo->lo_disk);	blk_register_region(MKDEV(LOOP_MAJOR, 0), range,				  THIS_MODULE, loop_probe, NULL, NULL);	printk(KERN_INFO "loop: module loaded\n");	return 0;Enomem:	printk(KERN_INFO "loop: out of memory\n");	list_for_each_entry_safe(lo, next, &loop_devices, lo_list)		loop_free(lo);	unregister_blkdev(LOOP_MAJOR, "loop");	return -ENOMEM;}static void __exit loop_exit(void){	unsigned long range;	struct loop_device *lo, *next;	range = max_loop ? max_loop :  1UL << MINORBITS;	list_for_each_entry_safe(lo, next, &loop_devices, lo_list)		loop_del_one(lo);	blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range);	unregister_blkdev(LOOP_MAJOR, "loop");}module_init(loop_init);module_exit(loop_exit);#ifndef MODULEstatic int __init max_loop_setup(char *str){	max_loop = simple_strtol(str, NULL, 0);	return 1;}__setup("max_loop=", max_loop_setup);#endif

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?