+/*
+ * linux/drivers/block/vroot.c
+ *
+ * written by Herbert Pötzl, 9/11/2002
+ * ported to 2.6.10 by Herbert Pötzl, 30/12/2004
+ *
+ * based on the loop.c code by Theodore Ts'o.
+ *
+ * Copyright (C) 2002-2005 by Herbert Pötzl.
+ * Redistribution of this file is permitted under the
+ * GNU General Public License.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/file.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <linux/vroot.h>
+
+
+static int max_vroot = 8;
+
+static struct vroot_device *vroot_dev;
+static struct gendisk **disks;
+
+
+static int vroot_set_dev(
+ struct vroot_device *vr,
+ struct file *vr_file,
+ struct block_device *bdev,
+ unsigned int arg)
+{
+ struct block_device *real_bdev;
+ struct file *file;
+ struct inode *inode;
+ int error;
+
+ error = -EBUSY;
+ if (vr->vr_state != Vr_unbound)
+ goto out;
+
+ error = -EBADF;
+ file = fget(arg);
+ if (!file)
+ goto out;
+
+ error = -EINVAL;
+ inode = file->f_dentry->d_inode;
+
+
+ if (S_ISBLK(inode->i_mode)) {
+ real_bdev = inode->i_bdev;
+ vr->vr_device = real_bdev;
+ __iget(real_bdev->bd_inode);
+ } else
+ goto out_fput;
+
+ printk(KERN_INFO "vroot[%d]_set_dev: dev=%p[%d,%d]\n",
+ vr->vr_number, real_bdev,
+ imajor(real_bdev->bd_inode), iminor(real_bdev->bd_inode));
+
+ vr->vr_state = Vr_bound;
+ error = 0;
+
+ out_fput:
+ fput(file);
+ out:
+ return error;
+}
+
+static int vroot_clr_dev(
+ struct vroot_device *vr,
+ struct file *vr_file,
+ struct block_device *bdev)
+{
+ struct block_device *real_bdev;
+
+ if (vr->vr_state != Vr_bound)
+ return -ENXIO;
+ if (vr->vr_refcnt > 1) /* we needed one fd for the ioctl */
+ return -EBUSY;
+
+ real_bdev = vr->vr_device;
+
+ printk(KERN_INFO "vroot[%d]_clr_dev: dev=%p[%d,%d]\n",
+ vr->vr_number, real_bdev,
+ imajor(real_bdev->bd_inode), iminor(real_bdev->bd_inode));
+
+ bdput(real_bdev);
+ vr->vr_state = Vr_unbound;
+ vr->vr_device = NULL;
+ return 0;
+}
+
+
+static int vr_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vroot_device *vr = inode->i_bdev->bd_disk->private_data;
+ int err;
+
+ down(&vr->vr_ctl_mutex);
+ switch (cmd) {
+ case VROOT_SET_DEV:
+ err = vroot_set_dev(vr, file, inode->i_bdev, arg);
+ break;
+ case VROOT_CLR_DEV:
+ err = vroot_clr_dev(vr, file, inode->i_bdev);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ up(&vr->vr_ctl_mutex);
+ return err;
+}
+
+static int vr_open(struct inode *inode, struct file *file)
+{
+ struct vroot_device *vr = inode->i_bdev->bd_disk->private_data;
+
+ down(&vr->vr_ctl_mutex);
+ vr->vr_refcnt++;
+ up(&vr->vr_ctl_mutex);
+ return 0;
+}
+
+static int vr_release(struct inode *inode, struct file *file)
+{
+ struct vroot_device *vr = inode->i_bdev->bd_disk->private_data;
+
+ down(&vr->vr_ctl_mutex);
+ --vr->vr_refcnt;
+ up(&vr->vr_ctl_mutex);
+ return 0;
+}
+
+static struct block_device_operations vr_fops = {
+ .owner = THIS_MODULE,
+ .open = vr_open,
+ .release = vr_release,
+ .ioctl = vr_ioctl,
+};
+
+struct block_device *vroot_get_real_bdev(struct block_device *bdev)
+{
+ struct inode *inode = bdev->bd_inode;
+ struct vroot_device *vr;
+ struct block_device *real_bdev;
+ int minor = iminor(inode);
+
+ vr = &vroot_dev[minor];
+ real_bdev = vr->vr_device;
+
+ printk(KERN_INFO "vroot[%d]_get_real_bdev: dev=%p[%p,%d,%d]\n",
+ vr->vr_number, real_bdev, real_bdev->bd_inode,
+ imajor(real_bdev->bd_inode), iminor(real_bdev->bd_inode));
+
+ if (vr->vr_state != Vr_bound)
+ return ERR_PTR(-ENXIO);
+
+ __iget(real_bdev->bd_inode);
+ return real_bdev;
+}
+
+/*
+ * And now the modules code and kernel interface.
+ */
+
+module_param(max_vroot, int, 0);
+
+MODULE_PARM_DESC(max_vroot, "Maximum number of vroot devices (1-256)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_BLOCKDEV_MAJOR(VROOT_MAJOR);
+
+MODULE_AUTHOR ("Herbert Pötzl");
+MODULE_DESCRIPTION ("Virtual Root Device Mapper");
+
+
+int __init vroot_init(void)
+{
+ int i;
+
+ if (max_vroot < 1 || max_vroot > 256) {
+ max_vroot = MAX_VROOT_DEFAULT;
+ printk(KERN_WARNING "vroot: invalid max_vroot "
+ "(must be between 1 and 256), "
+ "using default (%d)\n", max_vroot);
+ }
+
+ if (register_blkdev(VROOT_MAJOR, "vroot"))
+ return -EIO;
+
+ vroot_dev = kmalloc(max_vroot * sizeof(struct vroot_device), GFP_KERNEL);
+ if (!vroot_dev)
+ goto out_mem1;
+ memset(vroot_dev, 0, max_vroot * sizeof(struct vroot_device));
+
+ disks = kmalloc(max_vroot * sizeof(struct gendisk *), GFP_KERNEL);
+ if (!disks)
+ goto out_mem2;
+
+ for (i = 0; i < max_vroot; i++) {
+ disks[i] = alloc_disk(1);
+ if (!disks[i])
+ goto out_mem3;
+ }
+
+ devfs_mk_dir("vroot");
+
+ for (i = 0; i < max_vroot; i++) {
+ struct vroot_device *vr = &vroot_dev[i];
+ struct gendisk *disk = disks[i];
+
+ memset(vr, 0, sizeof(*vr));
+ init_MUTEX(&vr->vr_ctl_mutex);
+ vr->vr_number = i;
+ disk->major = VROOT_MAJOR;
+ disk->first_minor = i;
+ disk->fops = &vr_fops;
+ sprintf(disk->disk_name, "vroot%d", i);
+ sprintf(disk->devfs_name, "vroot/%d", i);
+ disk->private_data = vr;
+ }
+
+ for (i = 0; i < max_vroot; i++)
+ add_disk(disks[i]);
+ printk(KERN_INFO "vroot: loaded (max %d devices)\n", max_vroot);
+ return 0;
+
+out_mem3:
+ while (i--)
+ put_disk(disks[i]);
+ kfree(disks);
+out_mem2:
+ kfree(vroot_dev);
+out_mem1:
+ unregister_blkdev(VROOT_MAJOR, "vroot");
+ printk(KERN_ERR "vroot: ran out of memory\n");
+ return -ENOMEM;
+}
+
+void vroot_exit(void)
+{
+ int i;
+
+ for (i = 0; i < max_vroot; i++) {
+ del_gendisk(disks[i]);
+ put_disk(disks[i]);
+ }
+ devfs_remove("vroot");
+ if (unregister_blkdev(VROOT_MAJOR, "vroot"))
+ printk(KERN_WARNING "vroot: cannot unregister blkdev\n");
+
+ kfree(disks);
+ kfree(vroot_dev);
+}
+
+module_init(vroot_init);
+module_exit(vroot_exit);
+