📄 spull.c
字号:
/* * spull.c -- the Simple Partitionable Utility * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O'Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book "Linux Device * Drivers" by Alessandro Rubini and Jonathan Corbet, published * by O'Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * * $Id: spull.c,v 1.17 2001/07/18 22:28:18 rubini Exp $ */#ifndef __KERNEL__# define __KERNEL__#endif#ifndef MODULE# define MODULE#endif#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h> /* printk() */#include <linux/malloc.h> /* kmalloc() */#include <linux/fs.h> /* everything... */#include <linux/errno.h> /* error codes */#include <linux/timer.h>#include <linux/types.h> /* size_t */#include <linux/fcntl.h> /* O_ACCMODE */#include <linux/hdreg.h> /* HDIO_GETGEO */#include <asm/system.h> /* cli(), *_flags */#define MAJOR_NR spull_major /* force definitions on in blk.h */int spull_major; /* must be declared before including blk.h */#define SPULL_SHIFT 4 /* max 16 partitions */#define SPULL_MAXNRDEV 4 /* max 4 device units */#define DEVICE_NR(device) (MINOR(device)>>SPULL_SHIFT)#define DEVICE_NAME "pd" /* name for messaging */#define DEVICE_INTR spull_intrptr /* pointer to the bottom half */#define DEVICE_NO_RANDOM /* no entropy to contribute */#define DEVICE_REQUEST spull_request#define DEVICE_OFF(d) /* do-nothing */#include <linux/blk.h>#include "spull.h" /* local definitions */#ifdef HAVE_BLKPG_H#include <linux/blkpg.h> /* blk_ioctl() */#endif/* * Non-prefixed symbols are static. They are meant to be assigned at * load time. Prefixed symbols are not static, so they can be used in * debugging. They are hidden anyways by register_symtab() unless * SPULL_DEBUG is defined. */static int major = SPULL_MAJOR;static int devs = SPULL_DEVS;static int rahead = SPULL_RAHEAD;static int size = SPULL_SIZE;static int irq = 0;static int blksize = SPULL_BLKSIZE;MODULE_PARM(major, "i");MODULE_PARM(devs, "i");MODULE_PARM(rahead, "i");MODULE_PARM(size, "i");MODULE_PARM(blksize, "i");MODULE_PARM(irq, "i");MODULE_AUTHOR("Alessandro Rubini");int spull_devs, spull_rahead, spull_size;int spull_blksize, spull_irq;/* The following items are obtained through kmalloc() in spull_init() */Spull_Dev *spull_devices = NULL;int *spull_sizes = NULL;/* Forwards */int spull_revalidate(kdev_t i_rdev);#ifdef LINUX_24struct block_device_operations spull_bdops;#endif/* * Time for our genhd structure. */struct gendisk spull_gendisk = { major: 0, /* Major number assigned later */ major_name: "pd", /* Name of the major device */ minor_shift: SPULL_SHIFT, /* Shift to get device number */ max_p: 1 << SPULL_SHIFT, /* Number of partitions */#ifdef LINUX_24 fops: &spull_bdops, /* Block dev operations */#endif /* everything else is dynamic */};struct hd_struct *spull_partitions = NULL;/* * Flag used in "irq driven" mode to mark when we have operations * outstanding. */volatile int spull_busy = 0;/* * Open and close */int spull_open (struct inode *inode, struct file *filp){ Spull_Dev *dev; /* device information */ int num = DEVICE_NR(inode->i_rdev); if (num >= spull_devs) return -ENODEV; dev = spull_devices + num; /* kill the timer associated to the device: it might be active */ del_timer(&dev->timer); spin_lock(&dev->lock); /* * If no data area is there, allocate it. Clear its head as * well to prevent memory corruption due to bad partition info. */ if (!dev->data) { dev->data = vmalloc(dev->size); memset(dev->data,0,2048); } if (!dev->data) { spin_unlock(&dev->lock); return -ENOMEM; } dev->usage++; MOD_INC_USE_COUNT; spin_unlock(&dev->lock); return 0; /* success */}int spull_release (struct inode *inode, struct file *filp){ Spull_Dev *dev = spull_devices + DEVICE_NR(inode->i_rdev); spin_lock(&dev->lock); dev->usage--; /* * If the device is closed for the last time, start a timer * to release RAM in half a minute. The function and argument * for the timer have been setup in spull_init() */ if (!dev->usage) { dev->timer.expires = jiffies + 60 * HZ; add_timer(&dev->timer); /* but flush it right now */ fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); } MOD_DEC_USE_COUNT; spin_unlock(&dev->lock); return 0;}/* * The timer function. As argument it receives the device */void spull_expires(unsigned long data){ Spull_Dev *dev = (Spull_Dev *)data; spin_lock(&dev->lock); if (dev->usage || !dev->data) { spin_unlock(&dev->lock); printk(KERN_WARNING "spull: timer mismatch for device %i\n", dev - spull_devices); return; } PDEBUG("freeing device %i\n",dev - spull_devices); vfree(dev->data); dev->data=0; spin_unlock(&dev->lock); return;} /* * The ioctl() implementation */int spull_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ int err, size; struct hd_geometry geo; PDEBUG("ioctl 0x%x 0x%lx\n", cmd, arg); switch(cmd) { case BLKGETSIZE: /* Return the device size, expressed in sectors */ err = ! access_ok (VERIFY_WRITE, arg, sizeof(long)); if (err) return -EFAULT; size = spull_gendisk.part[MINOR(inode->i_rdev)].nr_sects; if (copy_to_user((long *) arg, &size, sizeof (long))) return -EFAULT; return 0; case BLKFLSBUF: /* flush */ if (! capable(CAP_SYS_RAWIO)) return -EACCES; /* only root */ fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); return 0; case BLKRAGET: /* return the readahead value */ err = ! access_ok(VERIFY_WRITE, arg, sizeof(long)); if (err) return -EFAULT; PUT_USER(read_ahead[MAJOR(inode->i_rdev)],(long *) arg); return 0; case BLKRASET: /* set the readahead value */ if (!capable(CAP_SYS_RAWIO)) return -EACCES; if (arg > 0xff) return -EINVAL; /* limit it */ read_ahead[MAJOR(inode->i_rdev)] = arg; return 0; case BLKRRPART: /* re-read partition table */ return spull_revalidate(inode->i_rdev); case HDIO_GETGEO: /* * get geometry: we have to fake one... trim the size to a * multiple of 64 (32k): tell we have 16 sectors, 4 heads, * whatever cylinders. Tell also that data starts at sector. 4. */ err = ! access_ok(VERIFY_WRITE, arg, sizeof(geo)); if (err) return -EFAULT; size = spull_size * blksize / SPULL_HARDSECT; geo.cylinders = (size & ~0x3f) >> 6; geo.heads = 4; geo.sectors = 16; geo.start = 4; if (copy_to_user((void *) arg, &geo, sizeof(geo))) return -EFAULT; return 0; default: /* * For ioctls we don't understand, let the block layer handle them. */ return blk_ioctl(inode->i_rdev, cmd, arg); } return -ENOTTY; /* unknown command */}/* * Support for removable devices */int spull_check_change(kdev_t i_rdev){ int minor = DEVICE_NR(i_rdev); Spull_Dev *dev = spull_devices + minor; if (minor >= spull_devs) /* paranoid */ return 0; PDEBUG("check_change for dev %i\n",minor); if (dev->data) return 0; /* still valid */ return 1; /* expired */}/* * The file operations */#ifdef LINUX_24struct block_device_operations spull_bdops = { open: spull_open, release: spull_release, ioctl: spull_ioctl, revalidate: spull_revalidate, check_media_change: spull_check_change,};#else#ifdef LINUX_20void spull_release_20 (struct inode *inode, struct file *filp){ (void) spull_release (inode, filp);}#define spull_release spull_release_20#endifstruct file_operations spull_bdops = { read: block_read, write: block_write, ioctl: spull_ioctl, open: spull_open, release: spull_release, fsync: block_fsync, check_media_change: spull_check_change, revalidate: spull_revalidate};# endif /* LINUX_24 *//* * Note no locks taken out here. In a worst case scenario, we could drop * a chunk of system memory. But that should never happen, since validation * happens at open or mount time, when locks are held. */int spull_revalidate(kdev_t i_rdev){ /* first partition, # of partitions */ int part1 = (DEVICE_NR(i_rdev) << SPULL_SHIFT) + 1; int npart = (1 << SPULL_SHIFT) -1; /* first clear old partition information */ memset(spull_gendisk.sizes+part1, 0, npart*sizeof(int)); memset(spull_gendisk.part +part1, 0, npart*sizeof(struct hd_struct)); spull_gendisk.part[DEVICE_NR(i_rdev) << SPULL_SHIFT].nr_sects = spull_size << 1; /* then fill new info */ printk(KERN_INFO "Spull partition check: (%d) ", DEVICE_NR(i_rdev)); register_disk(&spull_gendisk, i_rdev, SPULL_MAXNRDEV, &spull_bdops, spull_size << 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -