 99b7623380
			
		
	
	
	99b7623380
	
	
	
		
			
			Setting ->owner as done currently (pde->owner = THIS_MODULE) is racy as correctly noted at bug #12454. Someone can lookup entry with NULL ->owner, thus not pinning enything, and release it later resulting in module refcount underflow. We can keep ->owner and supply it at registration time like ->proc_fops and ->data. But this leaves ->owner as easy-manipulative field (just one C assignment) and somebody will forget to unpin previous/pin current module when switching ->owner. ->proc_fops is declared as "const" which should give some thoughts. ->read_proc/->write_proc were just fixed to not require ->owner for protection. rmmod'ed directories will be empty and return "." and ".." -- no harm. And directories with tricky enough readdir and lookup shouldn't be modular. We definitely don't want such modular code. Removing ->owner will also make PDE smaller. So, let's nuke it. Kudos to Jeff Layton for reminding about this, let's say, oversight. http://bugzilla.kernel.org/show_bug.cgi?id=12454 Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
		
			
				
	
	
		
			201 lines
		
	
	
	
		
			4.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
	
		
			4.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * procfs_example.c: an example proc interface
 | |
|  *
 | |
|  * Copyright (C) 2001, Erik Mouw (mouw@nl.linux.org)
 | |
|  *
 | |
|  * This file accompanies the procfs-guide in the Linux kernel
 | |
|  * source. Its main use is to demonstrate the concepts and
 | |
|  * functions described in the guide.
 | |
|  *
 | |
|  * This software has been developed while working on the LART
 | |
|  * computing board (http://www.lartmaker.nl), which was sponsored
 | |
|  * by the Delt University of Technology projects Mobile Multi-media
 | |
|  * Communications and Ubiquitous Communications.
 | |
|  *
 | |
|  * This program is free software; you can redistribute
 | |
|  * it and/or modify it under the terms of the GNU General
 | |
|  * Public License as published by the Free Software
 | |
|  * Foundation; either version 2 of the License, or (at your
 | |
|  * option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be
 | |
|  * useful, but WITHOUT ANY WARRANTY; without even the implied
 | |
|  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE.  See the GNU General Public License for more
 | |
|  * details.
 | |
|  * 
 | |
|  * You should have received a copy of the GNU General Public
 | |
|  * License along with this program; if not, write to the
 | |
|  * Free Software Foundation, Inc., 59 Temple Place,
 | |
|  * Suite 330, Boston, MA  02111-1307  USA
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/proc_fs.h>
 | |
| #include <linux/jiffies.h>
 | |
| #include <asm/uaccess.h>
 | |
| 
 | |
| 
 | |
| #define MODULE_VERS "1.0"
 | |
| #define MODULE_NAME "procfs_example"
 | |
| 
 | |
| #define FOOBAR_LEN 8
 | |
| 
 | |
| struct fb_data_t {
 | |
| 	char name[FOOBAR_LEN + 1];
 | |
| 	char value[FOOBAR_LEN + 1];
 | |
| };
 | |
| 
 | |
| 
 | |
| static struct proc_dir_entry *example_dir, *foo_file,
 | |
| 	*bar_file, *jiffies_file, *symlink;
 | |
| 
 | |
| 
 | |
| struct fb_data_t foo_data, bar_data;
 | |
| 
 | |
| 
 | |
| static int proc_read_jiffies(char *page, char **start,
 | |
| 			     off_t off, int count,
 | |
| 			     int *eof, void *data)
 | |
| {
 | |
| 	int len;
 | |
| 
 | |
| 	len = sprintf(page, "jiffies = %ld\n",
 | |
|                       jiffies);
 | |
| 
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int proc_read_foobar(char *page, char **start,
 | |
| 			    off_t off, int count, 
 | |
| 			    int *eof, void *data)
 | |
| {
 | |
| 	int len;
 | |
| 	struct fb_data_t *fb_data = (struct fb_data_t *)data;
 | |
| 
 | |
| 	/* DON'T DO THAT - buffer overruns are bad */
 | |
| 	len = sprintf(page, "%s = '%s'\n", 
 | |
| 		      fb_data->name, fb_data->value);
 | |
| 
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int proc_write_foobar(struct file *file,
 | |
| 			     const char *buffer,
 | |
| 			     unsigned long count, 
 | |
| 			     void *data)
 | |
| {
 | |
| 	int len;
 | |
| 	struct fb_data_t *fb_data = (struct fb_data_t *)data;
 | |
| 
 | |
| 	if(count > FOOBAR_LEN)
 | |
| 		len = FOOBAR_LEN;
 | |
| 	else
 | |
| 		len = count;
 | |
| 
 | |
| 	if(copy_from_user(fb_data->value, buffer, len))
 | |
| 		return -EFAULT;
 | |
| 
 | |
| 	fb_data->value[len] = '\0';
 | |
| 
 | |
| 	return len;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int __init init_procfs_example(void)
 | |
| {
 | |
| 	int rv = 0;
 | |
| 
 | |
| 	/* create directory */
 | |
| 	example_dir = proc_mkdir(MODULE_NAME, NULL);
 | |
| 	if(example_dir == NULL) {
 | |
| 		rv = -ENOMEM;
 | |
| 		goto out;
 | |
| 	}
 | |
| 	/* create jiffies using convenience function */
 | |
| 	jiffies_file = create_proc_read_entry("jiffies", 
 | |
| 					      0444, example_dir, 
 | |
| 					      proc_read_jiffies,
 | |
| 					      NULL);
 | |
| 	if(jiffies_file == NULL) {
 | |
| 		rv  = -ENOMEM;
 | |
| 		goto no_jiffies;
 | |
| 	}
 | |
| 
 | |
| 	/* create foo and bar files using same callback
 | |
| 	 * functions 
 | |
| 	 */
 | |
| 	foo_file = create_proc_entry("foo", 0644, example_dir);
 | |
| 	if(foo_file == NULL) {
 | |
| 		rv = -ENOMEM;
 | |
| 		goto no_foo;
 | |
| 	}
 | |
| 
 | |
| 	strcpy(foo_data.name, "foo");
 | |
| 	strcpy(foo_data.value, "foo");
 | |
| 	foo_file->data = &foo_data;
 | |
| 	foo_file->read_proc = proc_read_foobar;
 | |
| 	foo_file->write_proc = proc_write_foobar;
 | |
| 		
 | |
| 	bar_file = create_proc_entry("bar", 0644, example_dir);
 | |
| 	if(bar_file == NULL) {
 | |
| 		rv = -ENOMEM;
 | |
| 		goto no_bar;
 | |
| 	}
 | |
| 
 | |
| 	strcpy(bar_data.name, "bar");
 | |
| 	strcpy(bar_data.value, "bar");
 | |
| 	bar_file->data = &bar_data;
 | |
| 	bar_file->read_proc = proc_read_foobar;
 | |
| 	bar_file->write_proc = proc_write_foobar;
 | |
| 		
 | |
| 	/* create symlink */
 | |
| 	symlink = proc_symlink("jiffies_too", example_dir, 
 | |
| 			       "jiffies");
 | |
| 	if(symlink == NULL) {
 | |
| 		rv = -ENOMEM;
 | |
| 		goto no_symlink;
 | |
| 	}
 | |
| 
 | |
| 	/* everything OK */
 | |
| 	printk(KERN_INFO "%s %s initialised\n",
 | |
| 	       MODULE_NAME, MODULE_VERS);
 | |
| 	return 0;
 | |
| 
 | |
| no_symlink:
 | |
| 	remove_proc_entry("bar", example_dir);
 | |
| no_bar:
 | |
| 	remove_proc_entry("foo", example_dir);
 | |
| no_foo:
 | |
| 	remove_proc_entry("jiffies", example_dir);
 | |
| no_jiffies:			      
 | |
| 	remove_proc_entry(MODULE_NAME, NULL);
 | |
| out:
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void __exit cleanup_procfs_example(void)
 | |
| {
 | |
| 	remove_proc_entry("jiffies_too", example_dir);
 | |
| 	remove_proc_entry("bar", example_dir);
 | |
| 	remove_proc_entry("foo", example_dir);
 | |
| 	remove_proc_entry("jiffies", example_dir);
 | |
| 	remove_proc_entry(MODULE_NAME, NULL);
 | |
| 
 | |
| 	printk(KERN_INFO "%s %s removed\n",
 | |
| 	       MODULE_NAME, MODULE_VERS);
 | |
| }
 | |
| 
 | |
| 
 | |
| module_init(init_procfs_example);
 | |
| module_exit(cleanup_procfs_example);
 | |
| 
 | |
| MODULE_AUTHOR("Erik Mouw");
 | |
| MODULE_DESCRIPTION("procfs examples");
 | |
| MODULE_LICENSE("GPL");
 |