%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%									%
%	Copyright (C) 1992, 1993 Michael K. Johnson,			%
%	johnsonm@sunsite.unc.edu					%
%									%
%	This file is freely copyable, but you must preserve this	%
%	copyright notice on all copies, it must only be distributed	%
%	as part of the Linux Kernel Hackers' Guide, and its use is	%
%	is subject to the conditions expressed in the copyright for	%
%	the whole guide, in the file prelim/copyright.tex		%
%									%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\chapter{The {\tt /proc} filesystem}\label{chap-procfs}

The proc filesystem is an interface to several kernel data structures
which behaves remarkably like a filesystem.  Instead of having to read
/dev/kmem and have some way of knowing where things
are,\footnote{Usually a file called a namelist file, often
/etc/psdatabase.} all an application has to do is read files and
directories in /proc.  This way, all the addresses of the kernel data
structures are compiled into the proc filesystem at kernel compile
time, and programs which use the /proc interface need not be
recompiled or updated when the kernel is recompiled.  It is possible
to mount the proc filesystem somewhere other than /proc, but that
destroys the nice predictablity of the proc filesystem, so we will
conveniently ignore that option.

\section{{\tt /proc} Directories and Files}\label{sec-intr-procfs}

{\bf [This section should be severly cut, and the full version put in
the LPG when that is available.  In the mean time, better here than
nowhere.]}

In /proc, there is a subdirectory for every running process, named by
the number of the process's {\tt pid}.  These directories will be
explained below.  There are also several other files and directories.
These are:
\begin{dispitems}
\item [{\tt self}] This refers to the process accessing the proc
filesystem, and is identical to the directory named by the process id
of the process doing the look-up.
\item [{\tt kmsg}] This file can be used instead of the {\tt syslog()}
system call to log kernel messages.  A process must have superuser
priviledges to read this file, and only one process should read this
file.  This file should {\bf not} be read if a syslog process is
running which uses the {\tt syslog()} system call facility to log
kernel messages.

\item [{\tt loadavg}] This file gives an output like this:
\begin{screen}{\tt 0.13 0.14 0.05}\end{screen}
These numbers are the numbers normally given by {\tt uptime} and other
commands as the load average.

\item [{\tt meminfo}] This file is a condensed version of the output
from the {\tt free} program.  Its output looks like this:
\begin{screen}\begin{verbatim}
        total:   used:    free:   shared:  buffers:
Mem:   7528448  7344128   184320  2637824  1949696
Swap:  8024064  1474560  6549504
\end{verbatim}\end{screen}
Notice that the numbers are in bytes, not KB.  Linus wrote a version
of free which reads this file and can return either bytes ({\tt -b})
or KB ({\tt -k}, the default).  This is included with the {\tt procps}
package at {\tt tsx-11.mit.edu} and other places.  Also notice that
there is {\bf not} a seperate entry for each swap file:  the {\tt
Swap:} line sumarizes all the swap space available to the kernel.

\item [{\tt uptime}] This file contains two things: the time that the
system has been up, and the amount of time it has spent in the idle
process.  Both numbers are given as decimal quantities, in seconds and
hundreths of a second.  The two decimal digits of precision are not
guaranteed on all architectures, but are currently accurate on all
working implementations of \linux, due to the convenient 100 Hz clock.
This file looks like this:
\begin{screen}\begin{verbatim}
604.33 205.45
\end{verbatim}\end{screen}
In this case, the system has been running for 604.33 seconds, and of
that time, 205.45 seconds have been spent in the idle task.

\item [{\tt version}] This file contains a string identifying the
version of \linux\ that is currently running.  An example is:
\begin{screen}\begin{verbatim}
Linux version 0.99.pl4-2 (johnsonm@roland) 01/26/93 18:39:44
\end{verbatim}\end{screen}

\item [{\tt net}] This is a directory containing three files, all of
which give the status of some part of the \linux\ networking layer.
These files contain binary structures, and are therefore not readable
with cat.  However, the standard netstat suite uses these files.  The
binary structures read from these files are defined in {\tt <linux/if*.h>}
The files are:
\begin{dispitems}
\item [{\tt unix}] {\bf [I do not yet have details on the {\tt unix}
interface.  These details will be added later.]}
\item [{\tt arp}] {\bf [I do not yet have details on the {\tt arp}
interface.  These details will be added later.]}
\item [{\tt route}] {\bf [I do not yet have details on the {\tt route}
interface.  These details will be added later.]}
\item [{\tt dev}]{\bf [I do not yet have details on the {\tt dev}
interface.  These details will be added later.]}
\item [{\tt raw}]{\bf [I do not yet have details on the {\tt raw}
interface.  These details will be added later.]}
\item [{\tt tcp}]{\bf [I do not yet have details on the {\tt tcp}
interface.  These details will be added later.]}
\item [{\tt udp}]{\bf [I do not yet have details on the {\tt udp}
interface.  These details will be added later.]}
\end{dispitems}
\end{dispitems}

\medskip
\noindent Each of the process subdirectories (those with numerical
names and the self directory) have several files and subdirectories, as
well.  The files are:
\begin{dispitems}
\item [{\tt cmdline}] This holds the complete command line for the
process, {\bf unless the whole process has been swapped out,} or
unless the process is a zombie.  In either of these later cases, there
is nothing in this file: i.e. a read on this file will return as
having read 0 characters.  This file is null-terminated, but {\bf not}
newline-terminated. 

\item [{\tt cwd}] A link to the current working directory of that
process.  To find out the cwd of process 20, say, you can do this:
\begin{screen}\begin{verbatim}
(cd /proc/20/cwd; pwd)
\end{verbatim}\end{screen}

\item [{\tt environ}] This file contains the environment for the
process.  There are no newlines in this file: the entries are
seperated by null characters, and there is a null character at the
end.  Thus, to print out the environment of process 10, you would do:
\begin{screen}\begin{verbatim}
cat /proc/10/environ | tr "\000" "\n"
\end{verbatim}\end{screen}
This file is also null-terminated and not newline terminated..

\item [{\tt exe}] This is a link to the executable.  You can type
\begin{screen}\begin{verbatim}
/proc/10/exe
\end{verbatim}\end{screen}
to run another copy of whatever process 10 is.

\item [{\tt fd}] This is a subdirectory containing one entry for each
file which the process has open, named by its file descripter, and
which is a link to the actual file.  Programs that will take a
filename, but will not take the standard input, and which write to a
file, but will not send their output to standard output, can be
effectively foiled this way, assuming that {\tt -i} is the flag
designating an input file and {\tt -o} is the flag designating an
output file:
\begin{screen}\begin{verbatim}
... | foobar -i /proc/self/fd/0 -o /proc/self/fd/1 | ...
\end{verbatim}\end{screen}
{\em Voil\'{a}.}  Instant filter!  Note that this will not work for
programs that seek on their files, as the files in the {\tt fd}
directory are not seekable.

\item [{\tt lib}] This is a subdirectory containing one entry for each
shared library that the process is using, named by number, starting
from 0, in the order that they were intialized by the process.  These
are links to the library file.

\item [{\tt mem}] This is {\bf not} the same as the mem (1,1) device,
despite the fact that it has the same device numbers.  The
/dev/mem device is the physical memory before any address translation
is done, but the mem file here is the memory of the process that
accesses it.  This cannot be {\tt mmap()}ed currently, and will not be
until a general {\tt mmap()} is added to the kernel.

\item [{\tt root}] This is a pointer to the root directory of the
process.  This is useful for programs that call {\tt chroot()}, such as ftpd.

\item [{\tt stat}] This file contains a lot of status information
about the process.  The fields, in order, with their proper {\tt
scanf()} format specifiers, are:
\begin{dispitems}
\item [pid {\tt \%d}] The process id.
\item [comm {\tt (\%s)}] The filename of the executable, in
parentheses.  This is visible whether or not the executable is swapped
out.
\item [state {\tt \%c}] One character from the string ``RSDZT'' where
R is running, S is sleeping in an interruptable wait, D is sleeping in an
uninterruptable wait or swapping, Z is zombie, and T is traced or
stopped (on a signal).
\item [ppid {\tt \%d}] The pid of the parent.
\item [pgrp {\tt \%d}] The pgrp of the process.
\item [session {\tt \%d}] The session id of the process.
\item [tty {\tt \%d}] The tty the process uses.
\item [tpgid {\tt \%d}] The pgrp of the process which currently owns
the tty that the process is connected to.
\item [flags {\tt \%u}] The flags of the process.  Currently, every
flag has the math bit set, because crt0.s checks for math emulation,
so this is not included in the output.  This is probably a bug, as not {\em
every\/} process is a compiled c program.  The math bit should be a
decimal 4, and the traced bit is decimal 10.
\item [min\_flt {\tt \%u}] The number of minor faults the process has
made, those which have not required loading a memory page from disk.
\item [cmin\_flt {\tt \%u}] The number of minor faults that the
process and its children have made.
\item [maj\_flt {\tt \%u}] The number of major faults the process has
made, those which have required loading a memory page from disk.
\item [cmaj\_flt {\tt \%u}] The number of major faults that the
process and its children have made.
\item [utime {\tt \%d}] The number of jiffies that this process has
been scheduled in user mode.
\item [stime {\tt \%d}] The number of jiffies that this process has
been scheduled in kernel mode.
\item [cutime {\tt \%d}] The number of jiffies that this proces and
its children have been scheduled in user mode.
\item [cstime {\tt \%d}] The number of jiffies that this proces and
its children have been scheduled in kernel mode.
\item [counter {\tt \%d}] The current maximum size in jiffies of the
process's next timeslice, of what is currently left of its current
timeslice, if it is the currently running process.
\item [priority {\tt \%d}] The standard \unix\ nice value, plus
fifteen.  The value is never negative in the kernel.
\item [timeout {\tt \%u}] The time in jiffies of the process's next
timeout.
\item [it\_real\_value {\tt \%u}] Something having to do with interval
timers.  {\bf [Document this.]}
\item [start\_time {\tt \%d}] Time the process started in jiffies
after system boot.
\item [vsize {\tt \%u}] Virtual memory size
\item [rss {\tt \%u}] Resident Set Size: number of pages the process
has in {\bf real} memory, minus 3 for administrative purposes.  This
is just the pages which count towards text, data, or stack space.
This does {\bf not} include pages which have not been demand-loaded
in, or which are swapped out.
\item [rlim {\tt \%u}] Current limit on the size of the process.  {\bf
[Is this correct?.]}  
\item [start\_code {\tt \%u}] The address above which program text can run.
\item [end\_code {\tt \%u}] The address below which program text can run.
\item [start\_stack {\tt \%u}] The address of the start of the stack.
\item [kstk\_esp {\tt \%u}] {\bf [I don't know]}
\item [kstk\_eip {\tt \%u}] {\bf [I don't know]}
\item [signal {\tt \%d}] {\bf [Fill this in later]}
\item [blocked {\tt \%d}] {\bf [Fill this in later]}
\item [sigignore {\tt \%d}] {\bf [Fill this in later]}
\item [sigcatch {\tt \%d}] {\bf [Fill this in later]}
\item [wchan {\tt \%u}] This is the ``channel'' in which the process
is waiting.  This is the address of a system call, and can be looked
up in a namelist if you need a textual name.
\end{dispitems}

\item [{\tt statm}] This file contains special status information that
takes a bit longer to cook than the information in stat, and is needed
rarely enough that it has been relegated to a seperate file.  For each
field in this file, the proc filesystem has to look at each of the
0x300 entries in the page directory, and count what they are doing.
Here is a description of these fields:
\begin{dispitems}
\item [size {\tt \%d}] The total number of pages that the process has
mapped in the virtual memory space, whether they are in physical
memory or not.
\item [resident {\tt \%d}] The total number of pages that the process
has in physical memory.  This should equal the rss field from the stat
file, but is calculated rather than read from the process structure.
\item [shared {\tt \%d}] The total number of pages that the process
has that are shared with at least one other process.
\item [trs {\tt \%d}] Text Resident Size: the total number of text
(code) pages belonging to the process that are present in physical
memory.  Does {\bf not} include shared library pages.
\item [lrs {\tt \%d}] Library Resident Size: the total number of
library pages used by the process that are present in physical memory.
\item [drs {\tt \%d}] Data Resident Size: the total number of data
pages belonging to the process that are present in physical memory.
Include dirty library pages and stack.
\item [dt {\tt \%d}] The number of library pages which have been
accessed (i.e., are dirty).
\end{dispitems}

\end{dispitems}

\section{Structure of the /proc filesystem}\label{sec-struc-procfs}

The proc filesystem is rather interesting, because none of the files
exist in any real directory structure.  Rather, the proper vfs
structures are filled in with functions which do gigantic case
statements, and in the case of reading a file, get a page, fill it in,
and put the result in user memory space.

One of the most interesting parts of the proc filesystem is the way
that the individual process directories are implemented.  Essentially,
every process directory has the inode number of its PID shifted left
16 bits into a 32 bit number greater than {\tt 0x0000ffff}.  Within
the process directories, inode numbers are reused, because the upper
16 bits of the inode number have been masked off after choosing the
right directory.

Another interesting feature is that unlike in a ``real'' filesystem,
where there is one {\tt file\_operations} structure for the whole
filesystem, as file lookup is done, different {\tt file\_operations}
structures are assigned to the {\tt f\_ops} member of the file
structure passed to those functions, dynamically changing which
functions will be called for directory lookup and file reading.

{\bf [Expand on this section later~--- right now it is mostly here to
remind me to finish it\dots]}

\section{Programming the /proc filesystem}\label{sec-prog-procfs}

Unlike in most filesystems, not all inode numbers in the proc
filesystem are unique.  Some files are declared in structures like
\begin{screen}\begin{verbatim}
static struct proc_dir_entry root_dir[] = {
        { 1,1,"." },
        { 1,2,".." },
        { 2,7,"loadavg" },
        { 3,6,"uptime" },
        { 4,7,"meminfo" },
        { 5,4,"kmsg" },
        { 6,7,"version" },
        { 7,4,"self" }  /* will change inode # */
        { 8,4,"net" }
};
\end{verbatim}\end{screen}
and some files are dynamically created as the filesystem is read.  All
the process directories (those with numerical names and {\tt self})
essentially have inode numbers that are the pid
shifted left 16 bits, but the
files within those directories re-use low (1--10 or so) inode numbers,
which are added at runtime to the pid of the process.
This is done in inode.c by careful re-assignment of {\tt inode\_operation}
structures.

Other directories, such as /proc/net/, have their own inode numbers.
For instance, the net directory itself has inode number 8.  The files
within that directory use inode numbers from the range 128--160, and
those are uniquely identified in inode.c and the files given the
proper permissions when looked up and read.

Adding a file is relatively simple, and is left as an exersize for
the reader.  Adding a new directory is a little bit harder.  Assuming
that it is not a dynamically allocated directory like the process
directories, here are the steps:\footnote{Unless you are making a
subdirectory of the replicating, dynamically allocated process
directory, you would have to create a new filesystem type, similar to
the proc filesystem in design.  Subdirectories of the process
directories are supported by the mechanism which dynamically creates
the process directories.  I suggest going through this explanation of
how to add a non-dynamically-allocated directory, understand it, and
then read the code for the process subdirectories, if you wish to add
subdirectories to the process subdirectories.}
\begin{enumerate}
\item Choose a {\bf unique} range of inode numbers, giving yourself a
reasonable amount of room for expansion.  Then, right
before the line
\begin{screen}\begin{verbatim}
     if (!pid) { /* not a process directory but in /proc/ */
\end{verbatim}\end{screen}
add a section that looks like this:
\begin{screen}\begin{verbatim}
     if ((ino >= 128) && (ino <= 160)) { /* files withing /proc/net */
           inode->i_mode = S_IFREG | 0444;
           inode->i_op = &proc_net_inode_operations;
           return;
     }
\end{verbatim}\end{screen}
but modify it to to do what you want.  For instance, perhaps you have
a range of 200--256, and some files, inodes 200, 201, and 202,
and some directories, which are inodes 204 and 205.  You also have a
file that is readable only by root, inode 206.

Your example might look like this:
\begin{screen}\begin{verbatim}
     if ((ino >= 200) && (ino <= 256)) { /* files withing /proc/foo */
           switch (ino) {
             case 204:
             case 205:
                  inode->i_mode = S_IFDIR | 0555;
                  inode->i_op = &proc_foo_inode_operations;
                  break;
             case 206:
                  inode->i_mode = S_IFREG | 0400;
                  inode->i_op = &proc_foo_inode_operations;
                  break;
             default:
                  inode->i_mode = S_IFREG | 0444;
                  inode->i_op = &proc_foo_inode_operations;
                  break;
           }
           return;
     }
\end{verbatim}\end{screen}

\item Find the definition of the files.  If your files will go in a
subdirectory of /proc, for instance, you will look in root.c, and find
the following:
\begin{screen}\begin{verbatim}
static struct proc_dir_entry root_dir[] = {
     { 1,1,"." },
     { 1,2,".." },
     { 2,7,"loadavg" },
     { 3,6,"uptime" },
     { 4,7,"meminfo" },
     { 5,4,"kmsg" },
     { 6,7,"version" },
     { 7,4,"self" },  /* will change inode # */
     { 8,4,"net" }
};
\end{verbatim}\end{screen}
You will then add a new file to this structure, like this, using the
next available inode number:
\begin{screen}\begin{verbatim}
[...]
     { 6,7,"version" },
     { 7,4,"self" },  /* will change inode # */
     { 8,4,"net" },
     { 9,3,"foo" }
};
\end{verbatim}\end{screen}
You will then have to provide for this new directory in inode.c, so:
\begin{screen}\begin{verbatim}
     if (!pid) { /* not a process directory but in /proc/ */
          inode->i_mode = S_IFREG | 0444;
          inode->i_op = &proc_array_inode_operations;
          switch (ino)
            case 5:
                 inode->i_op = &proc_kmsg_inode_operations;
                 break;
            case 8: /* for the net directory */
                 inode->i_mode = S_IFDIR | 0555;
                 inode->i_op = &proc_net_inode_operations;
                 break;
            default:
                 break;
                 return;
     }
\end{verbatim}\end{screen}
becomes
\begin{screen}\begin{verbatim}
     if (!pid) { /* not a process directory but in /proc/ */
          inode->i_mode = S_IFREG | 0444;
          inode->i_op = &proc_array_inode_operations;
          switch (ino)
            case 5:
                 inode->i_op = &proc_kmsg_inode_operations;
                 break;
            case 8: /* for the net directory */
                 inode->i_mode = S_IFDIR | 0555;
                 inode->i_op = &proc_net_inode_operations;
                 break;
            case 9: /* for the foo directory */
                 inode->i_mode = S_IFDIR | 0555;
                 inode->i_op = &proc_foo_inode_operations;
                 break;
            default:
                 break;
                 return;
     }
\end{verbatim}\end{screen}

\item You now have to provide for the contents of the files within the
foo directory.  Make a file called proc/foo.c, following the following
model:\footnote{This file is availabe as file proc/foo.c in the
\kguide\ source mentioned on the copyright page.}  {\bf [The code in
{\tt proc\_lookupfoo()} and {\tt proc\_readfoo()} should be
abstracted, as the functionality is used in more than one place.]}
\begin{screen}\begin{verbatim}
/*
 *  linux/fs/proc/foo.c
 *
 *  Copyright (C) 1993 Linus Torvalds, Michael K. Johnson, and Your N. Here
 *
 *  proc foo directory handling functions
 *
 *  inode numbers 200 - 256 are reserved for this directory
 *  (/proc/foo/ and its subdirectories)
 */

#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>

static int proc_readfoo(struct inode *, struct file *, struct dirent *, int);
static int proc_lookupfoo(struct inode *,const char *,int,struct inode **);
static int proc_read(struct inode * inode, struct file * file,
                     char * buf, int count);

static struct file_operations proc_foo_operations = {
        NULL,                   /* lseek - default */
        proc_read,              /* read */
        NULL,                   /* write - bad */
        proc_readfoo,           /* readdir */
        NULL,                   /* select - default */
        NULL,                   /* ioctl - default */
        NULL,                   /* mmap */
        NULL,                   /* no special open code */
        NULL                    /* no special release code */
};

/*
 * proc directories can do almost nothing..
 */
struct inode_operations proc_foo_inode_operations = {
        &proc_foo_operations,   /* default foo directory file-ops */
        NULL,                   /* create */
        proc_lookupfoo,         /* lookup */
        NULL,                   /* link */
        NULL,                   /* unlink */
        NULL,                   /* symlink */
        NULL,                   /* mkdir */
        NULL,                   /* rmdir */
        NULL,                   /* mknod */
        NULL,                   /* rename */
        NULL,                   /* readlink */
        NULL,                   /* follow_link */
        NULL,                   /* bmap */
        NULL,                   /* truncate */
        NULL                    /* permission */
};

static struct proc_dir_entry foo_dir[] = {
        { 1,2,".." },
        { 9,1,"." },
        { 200,3,"bar" },
        { 201,4,"suds" },
        { 202,5,"xyzzy" },
        { 203,3,"baz" },
        { 204,4,"dir1" },
        { 205,4,"dir2" },
        { 206,8,"rootfile" }
};

#define NR_FOO_DIRENTRY ((sizeof (foo_dir))/(sizeof (foo_dir[0])))

unsigned int get_bar(char * buffer);
unsigned int get_suds(char * buffer);
unsigned int get_xyzzy(char * buffer);
unsigned int get_baz(char * buffer);
unsigned int get_rootfile(char * buffer);


static int proc_read(struct inode * inode, struct file * file,
                     char * buf, int count)
{
        char * page;
        int length;
        int end;
        unsigned int ino;

        if (count < 0)
                return -EINVAL;
        page = (char *) get_free_page(GFP_KERNEL);
        if (!page)
                return -ENOMEM;
        ino = inode->i_ino;
        switch (ino) {
                case 200:
                        length = get_bar(page);
                        break;
                case 201:
                        length = get_suds(page);
                        break;
                case 202:
                        length = get_xyzzy(page);
                        break;
                case 203:
                        length = get_baz(page);
                        break;
                case 206:
                        length = get_rootfile(page);
                        break;
                default:
                        free_page((unsigned long) page);
                        return -EBADF;
        }
        if (file->f_pos >= length) {
                free_page((unsigned long) page);
                return 0;
        }
        if (count + file->f_pos > length)
                count = length - file->f_pos;
        end = count + file->f_pos;
        memcpy_tofs(buf, page + file->f_pos, count);
        free_page((unsigned long) page);
        file->f_pos = end;
        return count;

}

static int proc_lookupfoo(struct inode * dir,const char * name, int len,
        struct inode ** result)
{
        unsigned int pid, ino;
        int i;

        *result = NULL;
        if (!dir)
                return -ENOENT;
        if (!S_ISDIR(dir->i_mode)) {
                iput(dir);
                return -ENOENT;
        }
        ino = dir->i_ino;
        i = NR_FOO_DIRENTRY;
        while (i-- > 0 && !proc_match(len,name,foo_dir+i))
                /* nothing */;
        if (i < 0) {
                iput(dir);
                return -ENOENT;
        }
        if (!(*result = iget(dir->i_sb,ino))) {
                iput(dir);
                return -ENOENT;
        }
        iput(dir);
        return 0;
}

static int proc_readfoo(struct inode * inode, struct file * filp,
        struct dirent * dirent, int count)
{
        struct proc_dir_entry * de;
        unsigned int pid, ino;
        int i,j;

        if (!inode || !S_ISDIR(inode->i_mode))
                return -EBADF;
        ino = inode->i_ino;
        if (((unsigned) filp->f_pos) < NR_FOO_DIRENTRY) {
                de = foo_dir + filp->f_pos;
                filp->f_pos++;
                i = de->namelen;
                ino = de->low_ino;
                put_fs_long(ino, &dirent->d_ino);
                put_fs_word(i,&dirent->d_reclen);
                put_fs_byte(0,i+dirent->d_name);
                j = i;
                while (i--)
                        put_fs_byte(de->name[i], i+dirent->d_name);
                return j;
        }
        return 0;
}


unsigned int get_foo(char * buffer)
{

/* code to find everything goes here */

        return sprintf(buffer, "format string", variables);
}


unsigned int get_suds(char * buffer)
{

/* code to find everything goes here */

        return sprintf(buffer, "format string", variables);
}


unsigned int get_xyzzy(char * buffer)
{

/* code to find everything goes here */

        return sprintf(buffer, "format string", variables);
}


unsigned int get_baz(char * buffer)
{

/* code to find everything goes here */

        return sprintf(buffer, "format string", variables);
}


unsigned int get_rootfile(char * buffer)
{

/* code to find everything goes here */

        return sprintf(buffer, "format string", variables);
}
\end{verbatim}\end{screen}

\item Filling in the directories dir1 and dir2 is left as an
excersize.  In most cases, such directories will not be needed.
However, if they are, the steps presented here may be applied
recursively to add files to a directory at another level.  Notice that
I saved a range of 200--256 for /proc/foo/ and all its
subdirectories, so there are plenty of unused inode numbers in that
range for your files in dir1 and dir2.  I suggest reserving a range
for each directory, in case you need to expand.  Also, I suggest
keeping all the extra data and functions in foo.c, rather than making
yet another file, unless the files in the dir1 and dir2 directories
are significantly different in concept than foo.

\item Make the appropriate changes to fs/proc/Makefile.  This is also
left as an excersize for the reader.

\end{enumerate}

{\bf [Please note: I have made changes similar to these (I wrote the
/proc/net/ support).  However, this has been written from memory, and
may be unintentionally incomplete.  If you notice any inadequacies,
please explain them to me in as complete detail as possible.  My email
address is {\tt johnsonm@sunsite.unc.edu}]}
