Copyright (c) 1991, 1992 by Frank J. Edwards
See the file COPYRIGHT in this distribution for copyright details.

The fs generates debugging information through the STREAMS logger
facility during most operations.  At strategic points throughout the
mount/umount process, and in the most of the routines, log messages
are generated.  Catch the trace logs with strace(1m).  Typically I
run something like this from the console display:

    # strace | tee /dev/par Results/strace

which pipes the output of strace(1m) into tee(1) so that the trace
entries are printed on the parallel printer (/dev/par), recorded in
a file (Results/strace), and displayed on the screen (stdout).  Since
the VFS sometimes crashes badly, I changed the console I/O routines to
always put out characters to the serial port at 9600 baud.  This means
that any console messages also go to my Wyse50 terminal.  If the VFS
goes into an infinite loop or something, when I do the Ctrl-Amiga-Amiga
the last page of data will still be on the terminal.  Then my "getpanic"
program will do a screen dump of the terminal (when the machine reboots)
to grab an image of the data and store it in a file.  An unfortunate
side-effect is that any modem work gets blown out of the water, so I
only use the console routines sparingly (I usually use the STREAMS
facilities).  Of course, I tend to turn the modem off when I'm doing
this work anyway...

[The above has changed slightly.  There is now a variable which
contains a bitmask of how the debugging information should be
generated:  0x01 = strlog(), 0x02 = serial port, 0x04 = parallel port,
0x08 = console printf().  That variable is "ados_debug" and can be
modified in the running kernel by using adb:  "adb -w /unix /dev/kmem".
Ignore the warning message.  "ados_debug/X" will display the value,
and to change it to something else, use "ados_debug/W 5", for
instance, to get strlog() and parallel port output.  The parallel port
is not timed-out, so putting the printer offline will cause the kernel
to hang waiting for the printer.  Output to the serial port is always
at 4800 baud (I needed a speed that my A500 could keep up with -- it's
a long story).]

Here's a chart that shows which fields are zero/non-zero for the
different key block types.  These are longwords which are checked
by the sanity routines when they try to read a block of the
corresponding type.  Only applies to the AmigaDOS VFS.

			      Zero   Non-Zero
ROOT block:
    rd_rsvp1[0..1]		x
    rd_rsvp2[0]			x
    rd_rsvp3[0..2]		x
    rd_hashsz				x	((BlockSz / 4) - 56)

			      Zero   Non-Zero
FILE block
    uf_rsvp1[0..1]		x
    uf_hkey				x
    uf_hiseq				x	# blocks in file
    uf_hashsz				x	# entries in uf_data[]
    uf_block0				x	first entry of uf_data[]
    uf_hashchn				x	Other files with same Hash?
    uf_parent				x
    uf_exten				x	File > 36K?

			      Zero   Non-Zero
DIR block
    ud_rsvp1[0..2]		x
    ud_rsvp2[0..1]		x
    ud_rsvp3[0]			x
    ud_rsvp4[0..6]		x
    ud_hkey				x
    ud_parent				x

Most of the filesystem code does what would be expected of each of the
routines, ie. vfs_mount() mounts the filesystem.  Some are not quite so
obvious; ados_inactive() is called when a vnode goes inactive and is
usually to free the memory for the vnode as well as putting the anode
back onto the anode freelist, flushing pages to disk, etc.  A lot of
these routines need major work since my fs is read-only currently, and
doesn't implement a lot of fancy caching routines (see TODO also).
--
11/08/92

Some key points about certain routines:

ados_inactive
    is called after the OS finishes with an anode.  The VN_RELE
    macro calls vn_rele() which causes VOP_INACTIVE() to be called.  This
    is also done during the purging of the DNLC (directory name lookup
    cache).  Ados_inactive() should free the anode and put it back on the
    free list.  I don't free the memory associated with it 'cuz there's too
    much startup overhead in reading it back.  The filesystem has a perf-
    ormance problem in any case, partly because I don't use the hashing
    routine to find a filename, I just search the directory sequentially.
    It was easier to implement, but I may pay for it in the long run
    since I'll have to rewrite that section now!  (But the directory
    handling stuff is a mess anyway and needs to be redone.)  And The
    kernel needs much of the headers read anytime it sees a directory
    (to determine size, nlinks, etc).

ados_aget
    tries to find the anode that goes with the keyblk in one of the
    in-memory lists, aused or afree.  Aused is the list of currently
    active anodes.  Afree is the list of anodes that are free.  Being
    a free anode doesn't mean the data isn't valid.  Whenever the code
    finishes with an anode it goes on the end of the list.  When a
    fresh anode is needed, the first one on the list is used.  This
    implements a simple LRU for anode reuse.  Being on the free list
    doesn't mean that the contents of the anode are invalid.  In fact,
    when an anode is not "in use" it goes onto the free list without
    any data being purged, although it will (in the future) be queued
    for flushing back to disk.  That's why the ados_aget() routine
    checks both the used and free list before allocating a new anode.
    (NB: it should check the free list from the tail forward; FIXME)

ados_lookup
    this is the function in vnops.c which is responsible for locating
    a given filename.  It's supposed to parse as much of the pathname
    as it wants, returning whatever pieces aren't consumed in the
    process (this was for efficiency in RFS).  This is the ugliest
    part of this VFS.  Mostly because the layering between the
    routines is trash.  On my list of "things to do" is a cflow chart
    that I'll use to restructure the calls.

dnlc_enter
dnlc_lookup
dnlc_purge1
dnlc_remove
    so far this code only uses the first three of these.  They should
    be pretty much self-explanatory as to what they do.  They take as
    arguments the vnode pointer of the directory and a filename within
    the directory.  The dnlc_remove() would be used when files are
    renamed or removed (so the cache is no longer valid) or when
    selective purging of the cache is desired.  Speaking of purging,
    the dnlc_purge1() routine purges a single entry from the cache.
    This is used in ados_aget() thus:  if there are no free anodes (on
    the free list) then the purge routine is called.  If a vnode is
    purged, a "while" loop causes the free list to be checked again.  If
    there's still not a free anode, the purge routine is called again.
    This is repeated until there's nothing left in the cache, or a
    free anode shows up on the free list (because it was just purged).
    If all cache entries are purged and there are still no free anodes,
    the current implementation will dynamically allocate 4 more
    anodes and add them to the free list, then loop back to the above
    mentioned "while" loop.

    This is kinda' kludgey and forces the cache to be purged
    unnecessarily.  A better method would be to call dnlc_purge_vfs()
    which only purges cache entries of a given vfs type.  But this
    routine isn't discussed much in the SVR4 tutorial paper, so I'm
    shying away from using it until later.  When I have a source license
    (yeah, right).

There are a couple of other ugly coding hacks that will change
eventually.  For instance, (struct llchain) is cached immediately if a
directory is the target of ados_aget() or when reading the raw data
blocks of a directory that isn't already in the cache (the first
should prevent the second, but just in case...)  The ugly part is that
the a_idx field is used to indicate if the llchain list has been read
yet.  It really should just use the a_ll member of (struct anode) and
test for zero/non-zero.  There are other similar strangeness which are
a result of piecing the code together based on pseudo-code provided in
the SVR4 tutorial, some of the system header files, and the Trivial
File System (TFS) sent to me by Ford and Keith.

The directory reading/parsing routines need to be thrown out and the
AmigaDOS hashing routines used instead.  No caching of directories at all
(let the VMM cache the blocks that we read a lot).  We can cache file
extension blocks, though, which means we still need the (struct ll_chain)
and the above paragraph still applies.
--
11/13/92

The sanitychk() routine would complain about a reserved field of a
ROOT block being non-zero, but it didn't indicate which one.  It does
now.
--
11/16/92

Turns out that the directory caching FS uses that field in the directory
blocks (called the "extension" field).  So I added some hacks that tell
the vfs to ignore that field sometimes!  (Like when mounting a DOS3 or
DOS5 filesystem.)  Mail sent to "bugs@cbmvax" for clarification on this.

Also, it occurs to me that other folks won't have the kernel mods for
KPutFmt() and pKPutFmt() for the debugging module.  There are two ways
around this.  (1) Edit the Makefile and remove "debug.o" from the list
of object modules required and then "#define DBG(a,b,c,d) /* nothing */"
in ados_defs.h to prevent those routines from being called.  Or (2)
edit the "debug.c" file and comment out the if stmts at the end of the
file which reference those two functions.

I'm going to see about hacking a #define into the Makefile to fix this
before I ship it out.  Look for "-DNO_..." in the Makefile and
uncomment it if it's in there.  Each one provides for the elimination
of a different debugging output stream.
--
11/19/92

The getblk() and reada() routines in subr2.c were using the smaller
block size when the SMALL_BLOCK bit was set, but they didn't account for
the fact that only reads of a regular file were affected, not
directories.  This was allowing the mount command to work, but nothing
would show up in it since the ados_readdir() and ados_lookup() routines
were getting garbage data back from the low-level read-a-block
routines.
--
11/25/92

The ados_readdir() and ados_lookup() routines need to handle "." and
".." especially for the shell's purposes.  Each of those routines can
simply write a fake entry.  Calling the raw ados_read() routine on a
directory should return the raw data, but ados_readdir() should fake the
first two entries.

I'd like to see an option on the mount command that makes the filenames
case-insensitive like they are under AmigaDOS.  I like the fact that I can
type filenames without hitting shift, but they will sort in the right
locations when displayed by "ls" or "echo *".  Speaking of wildcards, what
would case folding do to the shell?

I need to check the dnlc routines and make sure that filenames are going
into, and coming out of, the cache at the right places.

And another idea for a permission scheme:  the owner and group id of the
original mount point are propogated to all files on the partition.  This
allows the superuser to mount the partition on a user's subdirectory and
that user would have access.  Or mount it in a protected location and
regular permission checks would limit access.
