Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  * access.c -- the files with access control on open
  3  *
  4  * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
  5  * Copyright (C) 2001 O'Reilly & Associates
  6  * Modified for warning-free compilation under Linux 2.6.31 by baker@cs.fsu.edu.
  7  *
  8  * The source code in this file can be freely used, adapted,
  9  * and redistributed in source or binary form, so long as an
 10  * acknowledgment appears in derived source files.  The citation
 11  * should list that the code comes from the book "Linux Device
 12  * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 13  * by O'Reilly & Associates.   No warranty is attached;
 14  * we cannot take responsibility for errors or fitness for use.
 15  *
 16  * $Id: access.c,v 1.2 2010/05/19 20:40:00 baker Exp baker $
 17  */
 18 
 19 /* FIXME: cloned devices as a use for kobjects? */
 20 
 21 /* Uid, etc. moved from struct task_struct to struct cred. */ //tpb
 22  
 23 #include <linux/kernel.h> /* printk() */
 24 #include <linux/module.h>
 25 #include <linux/slab.h>   /* kmalloc() */
 26 #include <linux/fs.h>     /* everything... */
 27 #include <linux/errno.h>  /* error codes */
 28 #include <linux/types.h>  /* size_t */
 29 #include <linux/fcntl.h>
 30 #include <linux/cdev.h>
 31 #include <linux/tty.h>
 32 #include <asm/atomic.h>
 33 #include <linux/list.h>
 34 #include <linux/sched.h>
 35 
 36 #include "scull.h"        /* local definitions */
 37 
 38 static dev_t scull_a_firstdev;  /* Where our range begins */
 39 
 40 /*
 41  * These devices fall back on the main scull operations. They only
 42  * differ in the implementation of open() and close()
 43  */
 44 
 45 
 46 
 47 /************************************************************************
 48  *
 49  * The first device is the single-open one,
 50  *  it has an hw structure and an open count
 51  */
 52 
 53 static struct scull_dev scull_s_device;
 54 static atomic_t scull_s_available = ATOMIC_INIT(1);
 55 
 56 static int scull_s_open(struct inode *inode, struct file *filp)
 57 {
 58         struct scull_dev *dev = &scull_s_device; /* device information */
 59 
 60         if (! atomic_dec_and_test (&scull_s_available)) {
 61                 atomic_inc(&scull_s_available);
 62                 return -EBUSY; /* already open */
 63         }
 64 
 65         /* then, everything else is copied from the bare scull device */
 66         if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
 67                 scull_trim(dev);
 68         filp->private_data = dev;
 69         return 0;          /* success */
 70 }
 71 
 72 static int scull_s_release(struct inode *inode, struct file *filp)
 73 {
 74         atomic_inc(&scull_s_available); /* release the device */
 75         return 0;
 76 }
 77 
 78 
 79 /*
 80  * The other operations for the single-open device come from the bare device
 81  */
 82 struct file_operations scull_sngl_fops = {
 83         .owner =        THIS_MODULE,
 84         .llseek =       scull_llseek,
 85         .read =         scull_read,
 86         .write =        scull_write,
 87         .ioctl =        scull_ioctl,
 88         .open =         scull_s_open,
 89         .release =      scull_s_release,
 90 };
 91 
 92 
 93 /************************************************************************
 94  *
 95  * Next, the "uid" device. It can be opened multiple times by the
 96  * same user, but access is denied to other users if the device is open
 97  */
 98 
 99 static struct scull_dev scull_u_device;
100 static int scull_u_count;       /* initialized to 0 by default */
101 static uid_t scull_u_owner;     /* initialized to 0 by default */
102 static spinlock_t scull_u_lock = SPIN_LOCK_UNLOCKED;
103 
104 static int scull_u_open(struct inode *inode, struct file *filp)
105 {
106         struct scull_dev *dev = &scull_u_device; /* device information */
107 
108         spin_lock(&scull_u_lock);
109         if (scull_u_count && 
110                         (scull_u_owner != current->cred->uid) &&  /* allow user */
111                         (scull_u_owner != current->cred->euid) && /* allow whoever did su */
112                         !capable(CAP_DAC_OVERRIDE)) { /* still allow root */
113                 spin_unlock(&scull_u_lock);
114                 return -EBUSY;   /* -EPERM would confuse the user */
115         }
116 
117         if (scull_u_count == 0)
118                 scull_u_owner = current->cred->uid; /* grab it */
119 
120         scull_u_count++;
121         spin_unlock(&scull_u_lock);
122 
123 /* then, everything else is copied from the bare scull device */
124 
125         if ((filp->f_flags & O_ACCMODE) == O_WRONLY)
126                 scull_trim(dev);
127         filp->private_data = dev;
128         return 0;          /* success */
129 }
130 
131 static int scull_u_release(struct inode *inode, struct file *filp)
132 {
133         spin_lock(&scull_u_lock);
134         scull_u_count--; /* nothing else */
135         spin_unlock(&scull_u_lock);
136         return 0;
137 }
138 
139 
140 
141 /*
142  * The other operations for the device come from the bare device
143  */
144 struct file_operations scull_user_fops = {
145         .owner =      THIS_MODULE,
146         .llseek =     scull_llseek,
147         .read =       scull_read,
148         .write =      scull_write,
149         .ioctl =      scull_ioctl,
150         .open =       scull_u_open,
151         .release =    scull_u_release,
152 };
153 
154 
155 /************************************************************************
156  *
157  * Next, the device with blocking-open based on uid
158  */
159 
160 static struct scull_dev scull_w_device;
161 static int scull_w_count;       /* initialized to 0 by default */
162 static uid_t scull_w_owner;     /* initialized to 0 by default */
163 static DECLARE_WAIT_QUEUE_HEAD(scull_w_wait);
164 static spinlock_t scull_w_lock = SPIN_LOCK_UNLOCKED;
165 
166 static inline int scull_w_available(void)
167 {
168         return scull_w_count == 0 ||
169                 scull_w_owner == current->cred->uid ||
170                 scull_w_owner == current->cred->euid ||
171                 capable(CAP_DAC_OVERRIDE);
172 }
173 
174 
175 static int scull_w_open(struct inode *inode, struct file *filp)
176 {
177         struct scull_dev *dev = &scull_w_device; /* device information */
178 
179         spin_lock(&scull_w_lock);
180         while (! scull_w_available()) {
181                 spin_unlock(&scull_w_lock);
182                 if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
183                 if (wait_event_interruptible (scull_w_wait, scull_w_available()))
184                         return -ERESTARTSYS; /* tell the fs layer to handle it */
185                 spin_lock(&scull_w_lock);
186         }
187         if (scull_w_count == 0)
188                 scull_w_owner = current->cred->uid; /* grab it */
189         scull_w_count++;
190         spin_unlock(&scull_w_lock);
191 
192         /* then, everything else is copied from the bare scull device */
193         if ((filp->f_flags & O_ACCMODE) == O_WRONLY)
194                 scull_trim(dev);
195         filp->private_data = dev;
196         return 0;          /* success */
197 }
198 
199 static int scull_w_release(struct inode *inode, struct file *filp)
200 {
201         int temp;
202 
203         spin_lock(&scull_w_lock);
204         scull_w_count--;
205         temp = scull_w_count;
206         spin_unlock(&scull_w_lock);
207 
208         if (temp == 0)
209                 wake_up_interruptible_sync(&scull_w_wait); /* awake other uid's */
210         return 0;
211 }
212 
213 
214 /*
215  * The other operations for the device come from the bare device
216  */
217 struct file_operations scull_wusr_fops = {
218         .owner =      THIS_MODULE,
219         .llseek =     scull_llseek,
220         .read =       scull_read,
221         .write =      scull_write,
222         .ioctl =      scull_ioctl,
223         .open =       scull_w_open,
224         .release =    scull_w_release,
225 };
226 
227 /************************************************************************
228  *
229  * Finally the `cloned' private device. This is trickier because it
230  * involves list management, and dynamic allocation.
231  */
232 
233 /* The clone-specific data structure includes a key field */
234 
235 struct scull_listitem {
236         struct scull_dev device;
237         dev_t key;
238         struct list_head list;
239     
240 };
241 
242 /* The list of devices, and a lock to protect it */
243 static LIST_HEAD(scull_c_list);
244 static spinlock_t scull_c_lock = SPIN_LOCK_UNLOCKED;
245 
246 /* A placeholder scull_dev which really just holds the cdev stuff. */
247 static struct scull_dev scull_c_device;   
248 
249 /* Look for a device or create one if missing */
250 static struct scull_dev *scull_c_lookfor_device(dev_t key)
251 {
252         struct scull_listitem *lptr;
253 
254         list_for_each_entry(lptr, &scull_c_list, list) {
255                 if (lptr->key == key)
256                         return &(lptr->device);
257         }
258 
259         /* not found */
260         lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL);
261         if (!lptr)
262                 return NULL;
263 
264         /* initialize the device */
265         memset(lptr, 0, sizeof(struct scull_listitem));
266         lptr->key = key;
267         scull_trim(&(lptr->device)); /* initialize it */
268         init_MUTEX(&(lptr->device.sem));
269 
270         /* place it in the list */
271         list_add(&lptr->list, &scull_c_list);
272 
273         return &(lptr->device);
274 }
275 
276 static int scull_c_open(struct inode *inode, struct file *filp)
277 {
278         struct scull_dev *dev;
279         dev_t key;
280  
281         if (!current->signal->tty) { 
282                 PDEBUG("Process \"%s\" has no ctl tty\n", current->comm);
283                 return -EINVAL;
284         }
285         key = tty_devnum(current->signal->tty);
286 
287         /* look for a scullc device in the list */
288         spin_lock(&scull_c_lock);
289         dev = scull_c_lookfor_device(key);
290         spin_unlock(&scull_c_lock);
291 
292         if (!dev)
293                 return -ENOMEM;
294 
295         /* then, everything else is copied from the bare scull device */
296         if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
297                 scull_trim(dev);
298         filp->private_data = dev;
299         return 0;          /* success */
300 }
301 
302 static int scull_c_release(struct inode *inode, struct file *filp)
303 {
304         /*
305          * Nothing to do, because the device is persistent.
306          * A `real' cloned device should be freed on last close
307          */
308         return 0;
309 }
310 
311 
312 
313 /*
314  * The other operations for the device come from the bare device
315  */
316 struct file_operations scull_priv_fops = {
317         .owner =    THIS_MODULE,
318         .llseek =   scull_llseek,
319         .read =     scull_read,
320         .write =    scull_write,
321         .ioctl =    scull_ioctl,
322         .open =     scull_c_open,
323         .release =  scull_c_release,
324 };
325 
326 /************************************************************************
327  *
328  * And the init and cleanup functions come last
329  */
330 
331 static struct scull_adev_info {
332         char *name;
333         struct scull_dev *sculldev;
334         struct file_operations *fops;
335 } scull_access_devs[] = {
336         { "scullsingle", &scull_s_device, &scull_sngl_fops },
337         { "sculluid", &scull_u_device, &scull_user_fops },
338         { "scullwuid", &scull_w_device, &scull_wusr_fops },
339         { "sullpriv", &scull_c_device, &scull_priv_fops }
340 };
341 #define SCULL_N_ADEVS 4
342 
343 /*
344  * Set up a single device.
345  */
346 static void scull_access_setup (dev_t devno, struct scull_adev_info *devinfo)
347 {
348         struct scull_dev *dev = devinfo->sculldev;
349         int err;
350 
351         /* Initialize the device structure */
352         dev->quantum = scull_quantum;
353         dev->qset = scull_qset;
354         init_MUTEX(&dev->sem);
355 
356         /* Do the cdev stuff. */
357         cdev_init(&dev->cdev, devinfo->fops);
358         kobject_set_name(&dev->cdev.kobj, devinfo->name);
359         dev->cdev.owner = THIS_MODULE;
360         err = cdev_add (&dev->cdev, devno, 1);
361         /* Fail gracefully if need be */
362         if (err) {
363                 printk(KERN_NOTICE "Error %d adding %s\n", err, devinfo->name);
364                 kobject_put(&dev->cdev.kobj);
365         } else
366                 printk(KERN_NOTICE "%s registered at %x\n", devinfo->name, devno);
367 }
368 
369 
370 int scull_access_init(dev_t firstdev)
371 {
372         int result, i;
373 
374         /* Get our number space */
375         result = register_chrdev_region (firstdev, SCULL_N_ADEVS, "sculla");
376         if (result < 0) {
377                 printk(KERN_WARNING "sculla: device number registration failed\n");
378                 return 0;
379         }
380         scull_a_firstdev = firstdev;
381 
382         /* Set up each device. */
383         for (i = 0; i < SCULL_N_ADEVS; i++)
384                 scull_access_setup (firstdev + i, scull_access_devs + i);
385         return SCULL_N_ADEVS;
386 }
387 
388 /*
389  * This is called by cleanup_module or on failure.
390  * It is required to never fail, even if nothing was initialized first
391  */
392 void scull_access_cleanup(void)
393 {
394         struct scull_listitem *lptr, *next;
395         int i;
396 
397         /* Clean up the static devs */
398         for (i = 0; i < SCULL_N_ADEVS; i++) {
399                 struct scull_dev *dev = scull_access_devs[i].sculldev;
400                 cdev_del(&dev->cdev);
401                 scull_trim(scull_access_devs[i].sculldev);
402         }
403 
404         /* And all the cloned devices */
405         list_for_each_entry_safe(lptr, next, &scull_c_list, list) {
406                 list_del(&lptr->list);
407                 scull_trim(&(lptr->device));
408                 kfree(lptr);
409         }
410 
411         /* Free up our number space */
412         unregister_chrdev_region(scull_a_firstdev, SCULL_N_ADEVS);
413         return;
414 }
415 
  This page was automatically generated by the LXR engine.