学习ldd3--访问控制(第六章)

来源:互联网 发布:linux重命名文件的命令 编辑:程序博客网 时间:2024/05/19 06:50
作者:张伟AreS
/*******************************************************************************/
代码:D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\访问控制
作为分析的起点,我们还是要看一下main.c文件中的scull_init_module函数,在该函数中,有如下语句:
   dev = MKDEV(scull_major, scull_minor + scull_nr_devs);/*scull_major和scull_minor的默认值都是0,scull_nr_devs的默认值是4*/
   dev += scull_p_init(dev);//调用了pipe.c文件中的scull_p_init函数,它创建了四个设备,对应的主设备号是系统动态分配值,
                            //次设备号为4,5,6,7,scull_p_init函数的返回值是4
   dev += scull_access_init(dev);
调用scull_access_init函数,这个函数就是我们今天要分析的起点,在access.c文件中定义:
  int scull_access_init(dev_t firstdev)
  {
 int result, i;
 /* Get our number space */
 result = register_chrdev_region (firstdev, SCULL_N_ADEVS, "sculla");
 if (result < 0) {
  printk(KERN_WARNING "sculla: device number registration failed\n");
  return 0;
 }
 scull_a_firstdev = firstdev;
 /* Set up each device. */
 for (i = 0; i < SCULL_N_ADEVS; i++)
  scull_access_setup (firstdev + i, scull_access_devs + i);
 return SCULL_N_ADEVS;
  }
调用了scull_access_setup函数,循环初始化4个访问控制相关设备。注意传递给scull_access_setup函数的第二个参数是scull_access_devs + i,
先看一下scull_access_devs的定义:
static struct scull_adev_info {
 char *name;
 struct scull_dev *sculldev;
 struct file_operations *fops;
} scull_access_devs[] = {
 { "scullsingle", &scull_s_device, &scull_sngl_fops },
 { "sculluid", &scull_u_device, &scull_user_fops },
 { "scullwuid", &scull_w_device, &scull_wusr_fops },
 { "sullpriv", &scull_c_device, &scull_priv_fops }
};
 
scull_access_setup函数:
/*
 * Set up a single device.
 */
static void scull_access_setup (dev_t devno, struct scull_adev_info *devinfo)
{
 struct scull_dev *dev = devinfo->sculldev;
 int err;
 /* Initialize the device structure ,分别初始化了量子数,量子集数,信号量和cdev成员*/
 dev->quantum = scull_quantum;
 dev->qset = scull_qset;
 init_MUTEX(&dev->sem);
 /* Do the cdev stuff. */
 cdev_init(&dev->cdev, devinfo->fops);
 kobject_set_name(&dev->cdev.kobj, devinfo->name);/*注册了sys系统中的名字*/
 dev->cdev.owner = THIS_MODULE;
 err = cdev_add (&dev->cdev, devno, 1);
        /* Fail gracefully if need be */
 if (err) {
  printk(KERN_NOTICE "Error %d adding %s\n", err, devinfo->name);
  kobject_put(&dev->cdev.kobj);
 } else
  printk(KERN_NOTICE "%s registered at %x\n", devinfo->name, devno);
}
scull_access_devs数组定义了4个访问控制设备,这4个设备使用不同的访问控制策略:
scullsingle、sculluid、scullwuid、sullpriv
/***************************************/  
独享设备
代码:D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\访问控制\独享设备scullsingle
这种访问控制一次只允许一个进程访问设备,最好避免使用这种技术,因为它限制了用户的灵活性。scullsingle设备实现了独享设备的策略,
其主要代码如下
static struct scull_dev scull_s_device;
static atomic_t scull_s_available = ATOMIC_INIT(1);
static int scull_s_open(struct inode *inode, struct file *filp)
{
    struct scull_dev *dev = &scull_s_device; /* device information */
    /*如果是第一次打开,scull_s_available=1,1-1=0返回true遇到!非,不执行if语句,顺利open,
     *第二个进程来open,scull_s_available =0,0-1=-1,返回false遇到!是,执行if语句,scull_s_available+1=0后,返回-EBUSY
     *之后第一个进来的进程不释放scull_s_available互斥信号量的话,之后进来的进程都如第二个进程无法open*/
    if (! atomic_dec_and_test (&scull_s_available)) {
        atomic_inc(&scull_s_available);
        return -EBUSY; /* already open */
    }
    /* then, everything else is copied from the bare scull device */
    if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
        scull_trim(dev);
    filp->private_data = dev;
    return 0;          /* success */
}
/*该函数在进程关闭设备文件时调用,其作用是将原子变量scull_s_available的值加1,表示释放设备。*/
static int scull_s_release(struct inode *inode, struct file *filp)
{
    atomic_inc(&scull_s_available); /* release the device */
    return 0;

 
/**************************************/
限制每次只由一个用户访问:
    这种访问策略允许一个用户的多个进程同时访问设备,但是不允许多个用户同时访问设备。与独享设备的策略相比,这种方法更加灵活。
此时需要增加两个数据项,一个打开计数器和一个设备属主UID。同样,这两个数据项最好保存在设备结构体内部,但是为了与scull复用代码,
在实现时我们把这两个变量定义为全局变量
  
使用这种策略实现的设备叫sculluid,其主要代码如下:                       
static struct scull_dev scull_u_device;                                                                                   
static int scull_u_count;   /* initialized to 0 by default */            
static uid_t scull_u_owner; /* initialized to 0 by default */            
static spinlock_t scull_u_lock = SPIN_LOCK_UNLOCKED;                     
                                                                         
static int scull_u_open(struct inode *inode, struct file *filp)          
{                                                                        
    struct scull_dev *dev = &scull_u_device; /* device information */    
                                                                         
    spin_lock(&scull_u_lock); /*下面的判断必须是原子的*/                                         
    if (scull_u_count &&      /*scull_u_count不为0,说明不是第一个进程来访问,那这个进程的uid必须和第一个进程的uid符合*/                            
            (scull_u_owner != current->uid) &&  /* allow user */         
            (scull_u_owner != current->euid) && /* allow whoever did su */
            !capable(CAP_DAC_OVERRIDE)) { /* still allow root */         
        spin_unlock(&scull_u_lock);                                      
        return -EBUSY;   /* -EPERM would confuse the user */             
    }                                                                    
                                                                         
    if (scull_u_count == 0)   /*还没有进程访问过时,第一次有进程访问,就会执行到此语句*/                                           
        scull_u_owner = current->uid; /* 第一次访问就设置好用户id,以后的进程的uid必须符合此id,说明是同一个用户的多个进程才能访问 */                      
                                                                         
    scull_u_count++;                                                     
    spin_unlock(&scull_u_lock);                                          
                                                                         
/* then, everything else is copied from the bare scull device */         
                                                                         
    if ((filp->f_flags & O_ACCMODE) == O_WRONLY)                         
        scull_trim(dev);                                                 
    filp->private_data = dev;                                            
    return 0;          /* success */                                     
}                                                                        
                                                                         
static int scull_u_release(struct inode *inode, struct file *filp)       
{                                                                        
    spin_lock(&scull_u_lock);                                            
    scull_u_count--; /* nothing else */                                  
    spin_unlock(&scull_u_lock);                                          
    return 0;                                                            
}                                                    
/**************************************/
阻塞型open
  上面两种访问控制方法当设备不能访问时,都是返回-EBUSY退出,但是有些情况下,可能需要让进程阻塞等待,这时就需要实现阻塞型open
 
scullwuid设备实现了阻塞型open,其主要代码如下:
static struct scull_dev scull_w_device;
static int scull_w_count;   /* initialized to 0 by default */
static uid_t scull_w_owner; /* initialized to 0 by default */
static DECLARE_WAIT_QUEUE_HEAD(scull_w_wait);
static spinlock_t scull_w_lock = SPIN_LOCK_UNLOCKED;
static inline int scull_w_available(void)
{
    return scull_w_count == 0 ||scull_w_owner == current->uid || scull_w_owner == current->euid ||capable(CAP_DAC_OVERRIDE);
}
static int scull_w_open(struct inode *inode, struct file *filp)
{
    struct scull_dev *dev = &scull_w_device; /* device information */
    spin_lock(&scull_w_lock);
    while (! scull_w_available()) {  如果不能访问设备,阻塞在scull_w_wait上等待,不返回-EBUSY
        spin_unlock(&scull_w_lock);
        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;
        if (wait_event_interruptible (scull_w_wait, scull_w_available()))  /*返回1表示被中断,不是唤醒*/
            return -ERESTARTSYS; /* tell the fs layer to handle it */
        spin_lock(&scull_w_lock);
    }
    if (scull_w_count == 0)
        scull_w_owner = current->uid; /* grab it */
    scull_w_count++;
    spin_unlock(&scull_w_lock);
    /* then, everything else is copied from the bare scull device */
    if ((filp->f_flags & O_ACCMODE) == O_WRONLY)
        scull_trim(dev);
    filp->private_data = dev;
    return 0;          /* success */
}
static int scull_w_release(struct inode *inode, struct file *filp)
{
    int temp;
    spin_lock(&scull_w_lock);
    scull_w_count--;
    temp = scull_w_count;
    spin_unlock(&scull_w_lock);
    if (temp == 0)
        wake_up_interruptible_sync(&scull_w_wait); /* awake other uid's */
    return 0;
}
/**************************************/
打开时clone设备
    另一个实现访问控制的方法是,在进程打开设备时clone一个设备给进程使用。使用这种控制策略实现的设备是scullpriv,
它使用当前进程控制终端的设备号作为访问虚拟设备的键值,也可以使用任意整数值做为键值,但是不同的键值将导致不同的访问策略。
例如,使用uid作为键值,则会给每个用户clone一个设备。使用pid作为键值,则会给每个进程clone一个设备。所以,对于scullpriv,
不同终端上的进程会有不同的clone设备。
下面是scullpriv的主要实现代码:
/* The clone-specific data structure includes a key field */
struct scull_listitem {
    struct scull_dev device;
    dev_t key;
    struct list_head list;
};
/* The list of devices, and a lock to protect it */
static LIST_HEAD(scull_c_list);
static spinlock_t scull_c_lock = SPIN_LOCK_UNLOCKED;
/* A placeholder scull_dev which really just holds the cdev stuff. */
static struct scull_dev scull_c_device;
/* Look for a device or create one if missing */
static struct scull_dev *scull_c_lookfor_device(dev_t key)
{
    struct scull_listitem *lptr;
    list_for_each_entry(lptr, &scull_c_list, list) {
        if (lptr->key == key)
            return &(lptr->device);
    }
    /* not found */
    lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL);
    if (!lptr)
        return NULL;
    /* initialize the device */
    memset(lptr, 0, sizeof(struct scull_listitem));
    lptr->key = key;
    scull_trim(&(lptr->device)); /* initialize it */
    init_MUTEX(&(lptr->device.sem));
    /* place it in the list */
    list_add(&lptr->list, &scull_c_list);
    return &(lptr->device);
}
static int scull_c_open(struct inode *inode, struct file *filp)
{
    struct scull_dev *dev;
    dev_t key;
    if (!current->signal->tty) {
        PDEBUG("Process \"%s\" has no ctl tty\n", current->comm);
        return -EINVAL;
    }
    key = tty_devnum(current->signal->tty);
    /* look for a scullc device in the list */
    spin_lock(&scull_c_lock);
    dev = scull_c_lookfor_device(key);
    spin_unlock(&scull_c_lock);
    if (!dev)
        return -ENOMEM;
    /* then, everything else is copied from the bare scull device */
    if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
        scull_trim(dev);
    filp->private_data = dev;
    return 0;          /* success */
}
static int scull_c_release(struct inode *inode, struct file *filp)
{
    /*
     * Nothing to do, because the device is persistent.
     * A `real' cloned device should be freed on last close
     */
    return 0;
}
                 
原创粉丝点击