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