学习ldd3--访问控制(第六章)
来源:互联网 发布:linux重命名文件的命令 编辑:程序博客网 时间:2024/05/19 06:50
作者:张伟AreS
/*******************************************************************************/
代码:D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\访问控制
代码: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;
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;
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;
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);
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);
}
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
scullsingle、sculluid、scullwuid、sullpriv
/***************************************/
独享设备
代码:D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\访问控制\独享设备scullsingle
独享设备
代码:D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\访问控制\独享设备scullsingle
这种访问控制一次只允许一个进程访问设备,最好避免使用这种技术,因为它限制了用户的灵活性。scullsingle设备实现了独享设备的策略,
其主要代码如下
static struct scull_dev scull_s_device;
static atomic_t scull_s_available = ATOMIC_INIT(1);
其主要代码如下
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 */
}
{
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 */
}
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;
}
/**************************************/
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;
}
这种访问策略允许一个用户的多个进程同时访问设备,但是不允许多个用户同时访问设备。与独享设备的策略相比,这种方法更加灵活。
此时需要增加两个数据项,一个打开计数器和一个设备属主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,其主要代码如下:
上面两种访问控制方法当设备不能访问时,都是返回-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 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);
}
{
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 */
{
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);
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 */
}
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;
{
int temp;
spin_lock(&scull_w_lock);
scull_w_count--;
temp = scull_w_count;
spin_unlock(&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;
}
wake_up_interruptible_sync(&scull_w_wait); /* awake other uid's */
return 0;
}
/**************************************/
打开时clone设备
另一个实现访问控制的方法是,在进程打开设备时clone一个设备给进程使用。使用这种控制策略实现的设备是scullpriv,
它使用当前进程控制终端的设备号作为访问虚拟设备的键值,也可以使用任意整数值做为键值,但是不同的键值将导致不同的访问策略。
例如,使用uid作为键值,则会给每个用户clone一个设备。使用pid作为键值,则会给每个进程clone一个设备。所以,对于scullpriv,
不同终端上的进程会有不同的clone设备。
它使用当前进程控制终端的设备号作为访问虚拟设备的键值,也可以使用任意整数值做为键值,但是不同的键值将导致不同的访问策略。
例如,使用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;
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;
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;
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;
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);
}
if (lptr->key == key)
return &(lptr->device);
}
/* not found */
lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL);
if (!lptr)
return NULL;
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));
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);
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;
{
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);
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);
spin_lock(&scull_c_lock);
dev = scull_c_lookfor_device(key);
spin_unlock(&scull_c_lock);
if (!dev)
return -ENOMEM;
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 */
}
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;
}
{
/*
* Nothing to do, because the device is persistent.
* A `real' cloned device should be freed on last close
*/
return 0;
}
- 学习ldd3--访问控制(第六章)
- 学习ldd3--ioctl(第六章)
- 学习ldd3--poll(第六章)
- 学习ldd3--llseek(第六章)
- 【学习笔记】第六章 访问权限控制
- 学习ldd3--简单休眠(第六章)
- 学习ldd3--异步通知(第六章)
- 学习ldd3--阻塞型 IO(第六章)
- Squid第六章 访问控制
- 第六章:访问权限控制
- 第六章 访问权限控制
- 第六章 访问控制权限
- 第六章 访问权限控制
- 第六章 访问权限控制
- 第六章 访问权限控制
- 第六章 访问控制权限
- 第六章 访问权限控制
- 第六章 访问权限控制
- 民办院校招生乱象调查
- visitor模式
- 杭州烟花爆炸事故无人重伤-游客衣服包裹头逃生-杭州-烟花爆炸-烧伤
- 学习ldd3--llseek(第六章)
- 大师们对软件开发作的经典总结
- 学习ldd3--访问控制(第六章)
- 关于jsp中OnSubmit="return check()"感觉像无法进入的问题
- Bash Shell PS1: 10 Examples to Make Your Linux Prompt like Angelina Jolie
- 学习ldd3--proc文件系统(第七章与第四章)
- 基于LEACH的无线传感器网络路由算法概述
- 学习ldd3--tasklet(第七章)
- 台湾-海洋过程分析研究室--相關研究資源連結
- 【枚举】教主的花园
- 学习ldd3--工作队列(第七章)