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 + -
显示快捷键?