loop.c

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

C
1,565
字号
	spin_lock_irq(&lo->lo_lock);	if (lo->lo_state != Lo_bound)		goto out;	if (unlikely(rw == WRITE && (lo->lo_flags & LO_FLAGS_READ_ONLY)))		goto out;	loop_add_bio(lo, old_bio);	wake_up(&lo->lo_event);	spin_unlock_irq(&lo->lo_lock);	return 0;out:	spin_unlock_irq(&lo->lo_lock);	bio_io_error(old_bio);	return 0;}/* * kick off io on the underlying address space */static void loop_unplug(struct request_queue *q){	struct loop_device *lo = q->queuedata;	clear_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags);	blk_run_address_space(lo->lo_backing_file->f_mapping);}struct switch_request {	struct file *file;	struct completion wait;};static void do_loop_switch(struct loop_device *, struct switch_request *);static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio){	if (unlikely(!bio->bi_bdev)) {		do_loop_switch(lo, bio->bi_private);		bio_put(bio);	} else {		int ret = do_bio_filebacked(lo, bio);		bio_endio(bio, ret);	}}/* * worker thread that handles reads/writes to file backed loop devices, * to avoid blocking in our make_request_fn. it also does loop decrypting * on reads for block backed loop, as that is too heavy to do from * b_end_io context where irqs may be disabled. * * Loop explanation:  loop_clr_fd() sets lo_state to Lo_rundown before * calling kthread_stop().  Therefore once kthread_should_stop() is * true, make_request will not place any more requests.  Therefore * once kthread_should_stop() is true and lo_bio is NULL, we are * done with the loop. */static int loop_thread(void *data){	struct loop_device *lo = data;	struct bio *bio;	set_user_nice(current, -20);	while (!kthread_should_stop() || lo->lo_bio) {		wait_event_interruptible(lo->lo_event,				lo->lo_bio || kthread_should_stop());		if (!lo->lo_bio)			continue;		spin_lock_irq(&lo->lo_lock);		bio = loop_get_bio(lo);		spin_unlock_irq(&lo->lo_lock);		BUG_ON(!bio);		loop_handle_bio(lo, bio);	}	return 0;}/* * loop_switch performs the hard work of switching a backing store. * First it needs to flush existing IO, it does this by sending a magic * BIO down the pipe. The completion of this BIO does the actual switch. */static int loop_switch(struct loop_device *lo, struct file *file){	struct switch_request w;	struct bio *bio = bio_alloc(GFP_KERNEL, 0);	if (!bio)		return -ENOMEM;	init_completion(&w.wait);	w.file = file;	bio->bi_private = &w;	bio->bi_bdev = NULL;	loop_make_request(lo->lo_queue, bio);	wait_for_completion(&w.wait);	return 0;}/* * Do the actual switch; called from the BIO completion routine */static void do_loop_switch(struct loop_device *lo, struct switch_request *p){	struct file *file = p->file;	struct file *old_file = lo->lo_backing_file;	struct address_space *mapping = file->f_mapping;	mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);	lo->lo_backing_file = file;	lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ?		mapping->host->i_bdev->bd_block_size : PAGE_SIZE;	lo->old_gfp_mask = mapping_gfp_mask(mapping);	mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));	complete(&p->wait);}/* * loop_change_fd switched the backing store of a loopback device to * a new file. This is useful for operating system installers to free up * the original file and in High Availability environments to switch to * an alternative location for the content in case of server meltdown. * This can only work if the loop device is used read-only, and if the * new backing store is the same size and type as the old backing store. */static int loop_change_fd(struct loop_device *lo, struct file *lo_file,		       struct block_device *bdev, unsigned int arg){	struct file	*file, *old_file;	struct inode	*inode;	int		error;	error = -ENXIO;	if (lo->lo_state != Lo_bound)		goto out;	/* the loop device has to be read-only */	error = -EINVAL;	if (!(lo->lo_flags & LO_FLAGS_READ_ONLY))		goto out;	error = -EBADF;	file = fget(arg);	if (!file)		goto out;	inode = file->f_mapping->host;	old_file = lo->lo_backing_file;	error = -EINVAL;	if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))		goto out_putf;	/* new backing store needs to support loop (eg splice_read) */	if (!inode->i_fop->splice_read)		goto out_putf;	/* size of the new backing store needs to be the same */	if (get_loop_size(lo, file) != get_loop_size(lo, old_file))		goto out_putf;	/* and ... switch */	error = loop_switch(lo, file);	if (error)		goto out_putf;	fput(old_file);	return 0; out_putf:	fput(file); out:	return error;}static inline int is_loop_device(struct file *file){	struct inode *i = file->f_mapping->host;	return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR;}static int loop_set_fd(struct loop_device *lo, struct file *lo_file,		       struct block_device *bdev, unsigned int arg){	struct file	*file, *f;	struct inode	*inode;	struct address_space *mapping;	unsigned lo_blocksize;	int		lo_flags = 0;	int		error;	loff_t		size;	/* This is safe, since we have a reference from open(). */	__module_get(THIS_MODULE);	error = -EBADF;	file = fget(arg);	if (!file)		goto out;	error = -EBUSY;	if (lo->lo_state != Lo_unbound)		goto out_putf;	/* Avoid recursion */	f = file;	while (is_loop_device(f)) {		struct loop_device *l;		if (f->f_mapping->host->i_rdev == lo_file->f_mapping->host->i_rdev)			goto out_putf;		l = f->f_mapping->host->i_bdev->bd_disk->private_data;		if (l->lo_state == Lo_unbound) {			error = -EINVAL;			goto out_putf;		}		f = l->lo_backing_file;	}	mapping = file->f_mapping;	inode = mapping->host;	if (!(file->f_mode & FMODE_WRITE))		lo_flags |= LO_FLAGS_READ_ONLY;	error = -EINVAL;	if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) {		const struct address_space_operations *aops = mapping->a_ops;		/*		 * If we can't read - sorry. If we only can't write - well,		 * it's going to be read-only.		 */		if (!file->f_op->splice_read)			goto out_putf;		if (aops->prepare_write || aops->write_begin)			lo_flags |= LO_FLAGS_USE_AOPS;		if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)			lo_flags |= LO_FLAGS_READ_ONLY;		lo_blocksize = S_ISBLK(inode->i_mode) ?			inode->i_bdev->bd_block_size : PAGE_SIZE;		error = 0;	} else {		goto out_putf;	}	size = get_loop_size(lo, file);	if ((loff_t)(sector_t)size != size) {		error = -EFBIG;		goto out_putf;	}	if (!(lo_file->f_mode & FMODE_WRITE))		lo_flags |= LO_FLAGS_READ_ONLY;	set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0);	lo->lo_blocksize = lo_blocksize;	lo->lo_device = bdev;	lo->lo_flags = lo_flags;	lo->lo_backing_file = file;	lo->transfer = transfer_none;	lo->ioctl = NULL;	lo->lo_sizelimit = 0;	lo->old_gfp_mask = mapping_gfp_mask(mapping);	mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));	lo->lo_bio = lo->lo_biotail = NULL;	/*	 * set queue make_request_fn, and add limits based on lower level	 * device	 */	blk_queue_make_request(lo->lo_queue, loop_make_request);	lo->lo_queue->queuedata = lo;	lo->lo_queue->unplug_fn = loop_unplug;	set_capacity(lo->lo_disk, size);	bd_set_size(bdev, size << 9);	set_blocksize(bdev, lo_blocksize);	lo->lo_thread = kthread_create(loop_thread, lo, "loop%d",						lo->lo_number);	if (IS_ERR(lo->lo_thread)) {		error = PTR_ERR(lo->lo_thread);		goto out_clr;	}	lo->lo_state = Lo_bound;	wake_up_process(lo->lo_thread);	return 0;out_clr:	lo->lo_thread = NULL;	lo->lo_device = NULL;	lo->lo_backing_file = NULL;	lo->lo_flags = 0;	set_capacity(lo->lo_disk, 0);	invalidate_bdev(bdev);	bd_set_size(bdev, 0);	mapping_set_gfp_mask(mapping, lo->old_gfp_mask);	lo->lo_state = Lo_unbound; out_putf:	fput(file); out:	/* This is safe: open() is still holding a reference. */	module_put(THIS_MODULE);	return error;}static intloop_release_xfer(struct loop_device *lo){	int err = 0;	struct loop_func_table *xfer = lo->lo_encryption;	if (xfer) {		if (xfer->release)			err = xfer->release(lo);		lo->transfer = NULL;		lo->lo_encryption = NULL;		module_put(xfer->owner);	}	return err;}static intloop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer,	       const struct loop_info64 *i){	int err = 0;	if (xfer) {		struct module *owner = xfer->owner;		if (!try_module_get(owner))			return -EINVAL;		if (xfer->init)			err = xfer->init(lo, i);		if (err)			module_put(owner);		else			lo->lo_encryption = xfer;	}	return err;}static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev){	struct file *filp = lo->lo_backing_file;	gfp_t gfp = lo->old_gfp_mask;	if (lo->lo_state != Lo_bound)		return -ENXIO;	if (lo->lo_refcnt > 1)	/* we needed one fd for the ioctl */		return -EBUSY;	if (filp == NULL)		return -EINVAL;	spin_lock_irq(&lo->lo_lock);	lo->lo_state = Lo_rundown;	spin_unlock_irq(&lo->lo_lock);	kthread_stop(lo->lo_thread);	lo->lo_backing_file = NULL;	loop_release_xfer(lo);	lo->transfer = NULL;	lo->ioctl = NULL;	lo->lo_device = NULL;	lo->lo_encryption = NULL;	lo->lo_offset = 0;	lo->lo_sizelimit = 0;	lo->lo_encrypt_key_size = 0;	lo->lo_flags = 0;	lo->lo_thread = NULL;	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);	memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);	memset(lo->lo_file_name, 0, LO_NAME_SIZE);	invalidate_bdev(bdev);	set_capacity(lo->lo_disk, 0);	bd_set_size(bdev, 0);	mapping_set_gfp_mask(filp->f_mapping, gfp);	lo->lo_state = Lo_unbound;	fput(filp);	/* This is safe: open() is still holding a reference. */	module_put(THIS_MODULE);	return 0;}static intloop_set_status(struct loop_device *lo, const struct loop_info64 *info){	int err;	struct loop_func_table *xfer;	if (lo->lo_encrypt_key_size && lo->lo_key_owner != current->uid &&	    !capable(CAP_SYS_ADMIN))		return -EPERM;	if (lo->lo_state != Lo_bound)		return -ENXIO;	if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE)		return -EINVAL;	err = loop_release_xfer(lo);	if (err)		return err;	if (info->lo_encrypt_type) {		unsigned int type = info->lo_encrypt_type;		if (type >= MAX_LO_CRYPT)			return -EINVAL;		xfer = xfer_funcs[type];		if (xfer == NULL)			return -EINVAL;	} else		xfer = NULL;	err = loop_init_xfer(lo, xfer, info);	if (err)		return err;	if (lo->lo_offset != info->lo_offset ||	    lo->lo_sizelimit != info->lo_sizelimit) {		lo->lo_offset = info->lo_offset;		lo->lo_sizelimit = info->lo_sizelimit;		if (figure_loop_size(lo))			return -EFBIG;	}	memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE);	memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE);	lo->lo_file_name[LO_NAME_SIZE-1] = 0;	lo->lo_crypt_name[LO_NAME_SIZE-1] = 0;	if (!xfer)		xfer = &none_funcs;	lo->transfer = xfer->transfer;	lo->ioctl = xfer->ioctl;	lo->lo_encrypt_key_size = info->lo_encrypt_key_size;	lo->lo_init[0] = info->lo_init[0];	lo->lo_init[1] = info->lo_init[1];	if (info->lo_encrypt_key_size) {		memcpy(lo->lo_encrypt_key, info->lo_encrypt_key,		       info->lo_encrypt_key_size);		lo->lo_key_owner = current->uid;	}		return 0;}static intloop_get_status(struct loop_device *lo, struct loop_info64 *info){	struct file *file = lo->lo_backing_file;	struct kstat stat;	int error;	if (lo->lo_state != Lo_bound)		return -ENXIO;	error = vfs_getattr(file->f_path.mnt, file->f_path.dentry, &stat);	if (error)		return error;	memset(info, 0, sizeof(*info));	info->lo_number = lo->lo_number;	info->lo_device = huge_encode_dev(stat.dev);	info->lo_inode = stat.ino;	info->lo_rdevice = huge_encode_dev(lo->lo_device ? stat.rdev : stat.dev);	info->lo_offset = lo->lo_offset;	info->lo_sizelimit = lo->lo_sizelimit;	info->lo_flags = lo->lo_flags;	memcpy(info->lo_file_name, lo->lo_file_name, LO_NAME_SIZE);	memcpy(info->lo_crypt_name, lo->lo_crypt_name, LO_NAME_SIZE);	info->lo_encrypt_type =		lo->lo_encryption ? lo->lo_encryption->number : 0;	if (lo->lo_encrypt_key_size && capable(CAP_SYS_ADMIN)) {		info->lo_encrypt_key_size = lo->lo_encrypt_key_size;		memcpy(info->lo_encrypt_key, lo->lo_encrypt_key,		       lo->lo_encrypt_key_size);	}	return 0;}static voidloop_info64_from_old(const struct loop_info *info, struct loop_info64 *info64){	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);}static intloop_info64_to_old(const struct loop_info64 *info64, struct loop_info *info){

⌨️ 快捷键说明

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