Creating and Registering Entries

New entries are added to the proc filesystem in two steps. First, a new instance of proc_dir_entry is created together with all information needed to describe the entry. This instance is then registered in the data structures of proc so that it is visible to the outside. Because the two steps are never carried out independently of each other, the kernel makes auxiliary functions available to combine both actions so that new entries can be generated quickly and easily.

The most frequently used function is called create_proc_entry and requires three arguments: <proc_fs.h>

extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);

□ name specifies the filename.

□ mode specifies the access mode in the conventional Unix scheme (user/group/others).

□ parent is a pointer to the proc_dir_entry instance of the directory where the file is to be inserted.

Caution: The function fills only the essential elements of the proc_dir_entry structure. It is therefore necessary to make a few brief "manual" corrections to the structure generated.

This is illustrated by the following sample code, which generates the proc/net/hyperCard entry to supply information on a (unbelievably good) network card:

struct proc_dir_entry *entry = NULL;

entry = create_proc_entry("hyperCard", S_IFREG|S_IRUGO|S_IWUSR,

printk(KERN_ERR "unable to create /proc/net/hyperCard\n"); return -EIO;

entry->read_proc = hypercard_proc_read;

entry->write_proc = hypercard_proc_write;

Once the entry has been created, it is registered with the proc filesystem using proc_register in fs/proc/generic.c. The task is divided into three steps:

1. A unique proc-internal number is generated to give the entry its own identity. get_inode_number is used to return an unused number for dynamically generated entries.

2. The next and parent elements of the proc_dir_entry instance must be set appropriately to incorporate the new entry into the hierarchy.

3. Depending on the file type, the pointers must be set appropriately to file and inode operations if the corresponding elements of proc_dir_entry, proc_iops and proc_fops previously contained a null pointer. Otherwise, the value held there is retained.

Which file and inode operations are used for proc files? The corresponding pointers are set as follows:

fs/proc/generic.c static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) {

dp->proc_fops = &proc_dir_operations; dp->proc_iops = &proc_dir_inode_operations;

dir->nlink++; } else if (S_ISLNK(dp->mode)) {

dp->proc_iops = &proc_link_inode_operations; } else if (S_ISREG(dp->mode)) {

dp->proc_fops = &proc_file_operations; if (dp->proc_iops == NULL)

dp->proc_iops = &proc_file_inode_operations;

For regular files, the kernel uses proc_file_operations and proc_file_inode_operations to define the file and inode operation methods:

fs/proc/generic.c static struct inode_operations proc_file_inode_operations = { .setattr = proc_notify_change,

fs/proc/generic.c static struct file_operations proc_file_operations = { .llseek = proc_file_lseek,

.read = proc_file_read,

.write = proc_file_write,

Directories use the following structures:

fs/proc/generic.c static struct file_operations proc_dir_operations = { .read = generic_read_dir,

.readdir = proc_readdir,

fs/proc/generic.c

/* proc directories can do almost nothing... */

static struct inode_operations proc_dir_inode_operations = { .lookup = proc_lookup,

.getattr = proc_getattr,

.setattr = proc_notify_change,

Symbolic links require inode operations but not file operations:

fs/proc/generic.c static struct inode_operations proc_link_inode_operations = { .readlink = generic_readlink,

.follow_link = proc_follow_link,

Later in this section, I take a closer look at the implementation of some of the routines in the above data structures.

In addition to create_proc_entry, the kernel provides two further auxiliary functions for creating new proc entries. All three are short wrapper routines for create_proc_entry and are defined with the following parameter list:

static inline struct proc_dir_entry *create_proc_read_entry(const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void * data) { ... }

static inline struct proc_dir_entry *create_proc_info_entry(const char *name, mode_t mode, struct proc_dir_entry *base, get_info_t *get_info) { ... }

create_proc_read_entry and create_proc_info_entry are used to create a new read entry. Because this can be done in two different ways (as discussed in Section 10.1.2), there must also be two routines. Whereas create_proc_info_entry requires a procedure pointer of type get_info_t that is added to the get_info element of proc_dir_entry, create_proc_info_entry expects not only a procedure pointer of type read_proc_t, but also a data pointer that enables the same function to be used as a read routine for various proc entries distinguished by reference to their data argument.

Although we are not interested in their implementation, I include below a list of other auxiliary functions used to manage proc entries:

□ proc_mkdir creates a new directory.

□ proc_mkdir_mode creates a new directory whose access mode can be explicitely specified.

□ proc_symlink generates a symbolic link.

□ remove_proc_entry deletes a dynamically generated entry from the proc directory.

The kernel sources include a sample file in Documentation/DocBook/procfs_example.c. This demonstrates the options described here and can be used as a template for writing proc routines. Section 10.1.6 includes some sample kernel source routines that are responsible for interaction between the read/write routines of the proc filesystem and the kernel subsystems.

Continue reading here: Finding Entries

Was this article helpful?

0 0