Commits

David du Colombier committed 2206c7b

fossil: import from plan 9

R=rsc
https://codereview.appspot.com/7988047

Comments (0)

Files changed (50)

man/man4/fossil.4

+.TH FOSSIL 4
+.SH NAME
+fossil, flchk, flfmt \- archival file server
+.SH SYNOPSIS
+.B fossil/fossil
+[
+.B -Dt
+]
+[
+.B -c
+.I cmd
+]...
+[
+.B -f
+.I file
+]
+[
+.B -m
+.I free-memory-percent
+]
+.PP
+.B fossil/flchk
+[
+.B -f
+]
+[
+.B -c
+.I ncache
+]
+[
+.B -h
+.I host
+]
+.I file
+.PP
+.B fossil/flfmt
+[
+.B -y
+]
+[
+.B -b
+.I blocksize
+]
+[
+.B -h
+.I host
+]
+[
+.B -l
+.I label
+]
+[
+.B -v
+.I score
+]
+.I file
+.PP
+.B fossil/conf
+[
+.B -w
+]
+.I file
+[
+.I config
+]
+.PP
+.B fossil/last
+.I file
+.SH DESCRIPTION
+.I Fossil
+is the main file system for Plan 9.
+Unlike the Plan 9 file servers of old,
+.I fossil
+is a collection of user-space programs that run on a standard Plan 9 kernel.
+The name of the main fossil file server at Murray Hill is
+.BR pie .
+The Plan 9 distribution file server,
+.BR sources ,
+is also a fossil server.
+.PP
+.I Fossil
+is structured as a magnetic disk write buffer
+optionally backed by a Venti server for archival storage.
+It serves the Plan 9 protocol via TCP.
+A
+.I fossil
+file server conventionally presents
+three trees in the root directory of each file system:
+.BR active ,
+.BR archive ,
+and
+.BR snapshot .
+.B /active
+is the root of a conventional file system
+whose blocks are stored in a disk file.
+In a typical configuration, the file server periodically
+marks the entire file system copy-on-write, effectively
+taking a snapshot of the file system at that moment.
+This snapshot is made available in a name
+created from the date and time of the snapshot:
+.BI /snapshot/ yyyy / mmdd / hhmm \fR,
+where
+.I yyyy
+is the full year,
+.I mm
+is the month number,
+.I dd
+is the day number,
+.I hh
+is the hour,
+and
+.I mm
+is the minute.
+The snapshots in
+.B /snapshot
+are ephemeral: eventually they are deleted
+to reclaim the disk space they occupy.
+Long-lasting snapshots stored on a Venti server
+are kept in 
+.B /archive
+and also named from the date (though not the time) of the snapshot:
+.BI /archive/ yyyy / mmdds \fR,
+where
+.IR yyyy ,
+.IR mm ,
+and
+.I dd
+are year, month, and day as before,
+and
+.I s
+is a sequence number if more than one
+archival snapshot is done in a day.
+For the first snapshot,
+.I s
+is null.
+For the subsequent snapshots,
+.I s
+is
+.BR .1 ,
+.BR .2 ,
+.BR .3 ,
+etc.
+The root of the main file system that is frozen
+for the first archival snapshot of December 15, 2002
+will be named
+.BR /archive/2002/1215/ .
+.PP
+The attach name used in
+.I mount
+(see
+.IR bind (1),
+.IR bind (2)
+and
+.IR attach (5))
+selects a file system to be served
+and optionally a subtree,
+in the format
+.IB fs \fR[\fB/ dir \fR].
+An empty attach name selects
+.BR main/active .
+.PP
+.I Fossil
+normally requires all users except
+.L none
+to provide authentication tickets on each
+.IR attach (5).
+To keep just anyone from connecting,
+.L none
+is only allowed to attach after another user
+has successfully attached on the same
+connection.
+The other user effectively acts as a chaperone
+for
+.LR none .
+Authentication can be disabled using the
+.B -A
+flag to
+.B open
+or
+.B srv
+(see
+.IR fossilcons (8)).
+.PP
+The groups called
+.B noworld
+and
+.B write
+are special on the file server.
+Any user belonging to
+.B noworld
+has attenuated access privileges.
+Specifically, when checking such a user's access to files,
+the file's permission bits are first ANDed
+with 0770 for normal files and 0771 for directories.
+The effect is to deny world access permissions to
+.B noworld
+users, except when walking into directories.
+If the
+.B write
+group exists, then the file system appears read-only
+to users not in the group.
+This is used to make the Plan 9 distribution file server
+.RI ( sources.cs.bell-labs.com )
+readable by the world but writable only to the developers.
+.PP
+.I Fossil
+starts a new instance of the fossil file server.
+It is configured mainly through console commands,
+documented in
+.IR fossilcons (8).
+.PP
+The options are:
+.TF "-c\fI cmd
+.PD
+.TP
+.B -D
+Toggle the debugging flag, which is initially off.
+When the flag is set, information about authentication
+and all protocol messages are written to standard error.
+.TP
+.B -t
+Start a file server console on
+.BR /dev/cons .
+If this option is given,
+.I fossil
+does not fork itself into the background.
+.TP
+.BI -c " cmd
+Execute the console command
+.IR cmd .
+This option may be repeated to give multiple
+commands.
+Typically the only commands given on the
+command line are
+.RB `` ".\fI file" ,''
+which executes a file containing commands,
+and
+.RB `` "srv -p" \fIcons \fR,''
+which starts a file server console on
+.BI /srv/ cons \fR.
+See
+.IR fossilcons (8)
+for more information.
+.TP
+.BI -f " file
+Read and execute console commands stored in the Fossil disk 
+.IR file .
+.I Conf
+.RI ( q.v. )
+reads and writes the command set stored in the disk.
+.TP
+.B -m
+Allocate
+.I free-memory-percent
+percent of the available free RAM for buffers.
+This overrides all other memory sizing parameters,
+notably the
+.B -c
+option to
+.BR open .
+30% is a reasonable choice.
+.PD
+.PP
+.I Flchk
+checks the fossil file system stored in
+.I file
+for inconsistencies.
+.I Flchk
+is deprecated in favor of the console
+.B check
+command (see
+.IR fossilcons (8)).
+.I Flchk
+prints
+.I fossil
+console commands that may be
+executed to take care of
+bad pointers
+.RB ( clrp ),
+bad entries
+.RB ( clre ),
+bad directory entries
+.RB ( clri ),
+unreachable blocks
+.RB ( bfree ).
+Console commands are interspersed with
+more detailed commentary on the file system.
+The commands are distinguished by being prefixed with
+sharp signs.
+Note that all proposed fixes are rather drastic: offending
+pieces of file system are simply chopped off.
+.PP
+.I Flchk
+does
+.I not
+modify the file system, so it is safe to
+run concurrently with
+.IR fossil ,
+though in this case
+the list of unreachable
+blocks and any inconsistencies involving the active file system
+should be taken with a grain of salt.
+.PP
+The options are:
+.TF "-h\fI host
+.PD
+.TP
+.B -f
+Fast mode.
+By default,
+.I flchk
+checks the entire file system image for consistency,
+which includes all the archives to Venti
+and can take a very long time.
+In fast mode,
+.I flchk
+avoids walking in Venti blocks
+whenever possible.
+.TP
+.BI -c " ncache
+Keep a cache of
+.I ncache
+(by default, 1000)
+file system blocks in memory during the check.
+.TP
+.BI -h " host
+Use
+.I host
+as the Venti server.
+.PD
+.PP
+.I Flfmt
+prepares
+.I file
+as a new fossil file system.
+The file system is initialized with three empty directories
+.BR active ,
+.BR archive ,
+and
+.BR snapshot ,
+as described above.
+The options are:
+.TF "-b\fI blocksize
+.PD
+.TP
+.B -y
+Yes mode.
+By default,
+.I flfmt
+will prompt for confirmation before formatting
+a file that already contains a fossil file system,
+and before formatting a file that is not served
+directly by a kernel device.
+If the
+.B -y
+flag is given, no such checks are made.
+.TP
+.BI -b " blocksize
+Set the file system block size (by default, 8192).
+.TP
+.BI -h " host
+Use
+.I host
+as the Venti server.
+.TP
+.BI -l " label
+Set the textual label on the file system to
+.IR label .
+The label is only a comment.
+.TP
+.BI -v " score
+Initialize the file system using the vac file
+system stored on Venti at
+.IR score .
+The score should have been generated by
+.I fossil
+rather than by
+.IR vac (1),
+so that the appropriate snapshot metadata is present.
+.PD
+.PP
+.I Conf
+reads or writes the configuration branded on the Fossil disk
+.IR file .
+By default, it reads the configuration from the disk and prints it to
+standard output.
+If the
+.B -w
+flag is given,
+.I conf
+reads a new configuration from 
+.I config
+(or else from standard input)
+and writes it to the disk.
+Inside the configuration file, the argument
+.L *
+may be used to stand in for the name of the disk holding the configuration.
+The Plan 9 kernel boot process runs
+.RB `` fossil
+.B -f
+.IR disk ''
+to start a Fossil file server.
+The disk is just a convenient place to store configuration
+information.
+.PP
+.I Last
+prints the vac score that resulted after the most recent archival snapshot 
+of the fossil in
+.I file.
+.SH EXAMPLES
+.PP
+Place the root of the archive file system on
+.B /n/dump
+and show the modified times of the MIPS C compiler
+over all dumps in December 2002:
+.IP
+.EX
+9fs dump
+ls -l /n/dump/2002/12*/mips/bin/vc
+.EE
+.PP
+To get only one line of output for each version of the compiler:
+.IP
+.EX
+ls -lp /n/dump/2002/12*/mips/bin/vc | uniq
+.EE
+.ne 14
+.PP
+Initialize a new file system, start the server with permission
+checking turned off, create a users file, and mount the server:
+.IP
+.EX
+fossil/flfmt /dev/sdC0/fossil
+fossil/conf -w /dev/sdC0/fossil <<EOF
+fsys main config
+fsys main open -AWP
+fsys main
+create /active/adm adm sys d775
+create /active/adm/users adm sys 664
+users -w
+srv -p fscons
+srv fossil
+EOF
+fossil/fossil -f /dev/sdC0/fossil
+mount /srv/fossil /n/fossil
+.EE
+.LP
+See the discussion of the
+.B users
+and
+.B uname
+commands in
+.IR fossilcons (8)
+for more about the user table.
+.ne 3
+.PP
+Perhaps because the disk has been corrupted or replaced,
+format a new file system using the last archive score printed
+on the console:
+.IP
+.EX
+fossil/flfmt -v b9b3...5559 /dev/sdC0/fossil
+.EE
+.LP
+Note that while
+.B /snapshot
+will be lost,
+.B /active
+and
+.B /archive
+will be restored to their contents at the time of the
+last archival snapshot.
+.ne 3
+.PP
+Blindly accept the changes prescribed by
+.I flchk
+(not recommended):
+.IP
+.EX
+fossil/flchk /dev/sdC0/fossil | sed -n 's/^# //p' >>/srv/fscons
+.EE
+.LP
+A better strategy is to vet the output,
+filter out any suggestions you're not comfortable with,
+and then use the
+.I sed
+command to prepare the script.
+.SH SOURCE
+.B /sys/src/cmd/fossil
+.SH SEE ALSO
+.IR yesterday (1),
+.IR fs (3),
+.IR fs (4),
+.IR srv (4),
+.IR fossilcons (8),
+.IR loadfossil (8),
+.IR venti (8)
+.SH BUGS
+It is possible that the disk format (but not the Venti format)
+will change in the future, to make the disk a full cache
+rather than just a write buffer.
+Changing to the new format will require reformatting
+the disk as in the example above,
+but note that this will preserve most of the file system
+(all but
+.BR /snapshot )
+with little effort.
+.PP
+The
+.B -m
+option currently assumes a block size of 8K bytes,
+and a single file system per
+.I fossil
+instance.

man/man8/fossilcons.8

+.TH FOSSILCONS 8
+.SH NAME
+fossilcons \- fossil console commands
+.SH SYNOPSIS
+.B
+con /srv/fscons
+.PP
+.PD 0.1
+.B .
+.I file
+.PP
+.B 9p
+.I T-message
+...
+.PP
+.B bind
+[
+.B -b|-a|-c|-bc|-ac
+]
+.I new
+.I old
+.PP
+.B dflag
+.PP
+.B echo
+[
+.B -n
+]
+[
+.I arg
+...
+]
+.PP
+.B listen
+[
+.B -INd
+]
+[
+.I address
+]
+.PP
+.B msg
+[
+.B -m
+.I nmsg
+]
+[
+.B -p
+.I nproc
+]
+.PP
+.B printconfig
+.PP
+.B srv
+[
+.B -APWdp
+]
+.I name
+.PP
+.B uname
+.I name
+[
+.I id
+|
+.BI : id
+|
+.BI % newname
+|
+.BI = leader
+|
+.BI + member
+|
+.BI - member
+]
+.PP
+.B users
+[
+.B -d
+|
+.B -r
+.I file
+]
+[
+.B -w
+]
+.PP
+.B who
+.sp
+.PP
+.B fsys
+.I name
+.PP
+.B fsys
+.I name
+.B config
+[
+.I device
+]
+.PP
+.B fsys
+.I name
+.B venti
+[
+.I host
+]
+.PP
+.B fsys
+.I name
+.B open
+[
+.B -APVWar
+]
+[
+.B -c
+.I ncache
+]
+.PP
+[
+.B fsys
+.I name
+]
+.B close
+.PP
+.B fsys
+.I name
+.B unconfig
+.sp
+.PP
+[
+.B fsys
+.I name
+]
+.B bfree
+.I addr
+.PP
+[
+.B fsys
+.I name
+]
+.B block
+.I addr
+.I offset
+[
+.I count
+[
+.I data
+]]
+.PP
+.in +1i
+.ti -1i
+[
+.B fsys
+.I name
+]
+.B check
+[
+.B pblock
+] [
+.B pdir
+] [
+.B pfile
+] [
+.B bclose
+] [
+.B clri
+] [
+.B clre
+] [
+.B clrp
+] [
+.B fix
+] [
+.B venti
+] [
+.B snapshot
+]
+.PP
+[
+.B fsys
+.I name
+]
+.B clre
+.I addr
+.I offsets
+\&...
+.PP
+[
+.B fsys
+.I name
+]
+.B clri
+.I files
+\&...
+.PP
+[
+.B fsys
+.I name
+]
+.B clrp
+.I addr
+.I offset
+\&...
+.PP
+[
+.B fsys
+.I name
+]
+.B create
+.I path
+.I uid
+.I gid
+.I perm
+.PP
+[
+.B fsys
+.I name
+]
+.B df
+.PP
+[
+.B fsys
+.I name
+]
+.B epoch
+[[
+.B -ry
+]
+.I n
+]
+.PP
+[
+.B fsys
+.I name
+]
+.B halt
+.PP
+[
+.B fsys
+.I name
+]
+.B label
+.I addr
+[
+.I type
+.I state
+.I epoch
+.I epochclose
+.I tag
+]
+.PP
+[
+.B fsys
+.I name
+]
+.B remove
+.I files
+\&...
+.PP
+[
+.B fsys
+.I name
+]
+.B snap
+[
+.B -a
+]
+[
+.B -s
+.I src
+]
+[
+.B -d
+.I dst
+]
+.PP
+[
+.B fsys
+.I name
+]
+.B snapclean
+[
+.I timeout
+]
+.PP
+[
+.B fsys
+.I name
+]
+.B snaptime
+[
+.B -a
+.I hhmm
+]
+[
+.B -s
+.I interval
+]
+[
+.B -t
+.I timeout
+]
+.PP
+[
+.B fsys
+.I name
+]
+.B stat
+.IR files ...
+.PP
+[
+.B fsys
+.I name
+]
+.B sync
+.PP
+[
+.B fsys
+.I name
+]
+.B unhalt
+.PP
+[
+.B fsys
+.I name
+]
+.B vac
+.I dir
+.PP
+[
+.B fsys
+.I name
+]
+.B wstat
+.I file
+.I elem
+.I uid
+.I gid
+.I perm
+.I length
+.SH DESCRIPTION
+These are configuration and maintenance commands
+executed at the console of a 
+.IR fossil (4)
+file server.
+The commands are split into three groups above:
+file server configuration,
+file system configuration,
+and file system maintenance.
+This manual page is split in the same way.
+.SS File server configuration
+.PP
+The
+dot
+.RI ( . )
+command
+reads
+.IR file ,
+treating each line as a command to be executed.
+Blank lines and lines beginning with a 
+.L #
+character are ignored.
+Errors during execution are printed but do not stop the script.
+Note that
+.I file
+is a file in the name space in which
+.I fossil
+was started,
+.I not
+a file in any file system served by
+.IR fossil .
+.PP
+.I 9p
+executes a 9P transaction; the arguments
+are in the same format used by
+.IR 9pcon (8).
+.PP
+.I Bind
+behaves similarly to
+.IR bind (1).
+It is useful when fossil
+is started without devices it needs configured
+into its namespace.
+.PP
+.I Dflag
+toggles the debug flag and prints the new setting.
+When the debug flag is set, all protocol messages
+and information about authentication is printed to
+standard error.
+.PP
+.I Echo
+behaves identically to
+.IR echo (1),
+writing to the console.
+.PP
+.I Listen
+manages the network addresses at which
+fossil is listening.
+With no arguments,
+.I listen
+prints the current list of addresses and their network directories.
+With one argument, listen
+.I address
+starts a new listener at
+.IR address ;
+the
+.B -d
+flag causes 
+.I listen
+to remove the listener
+at the given address.
+By default, the user
+.I none
+is only allowed to attach on a connection after
+at least one other user has successfully attached.
+The
+.B -N
+flag allows connections from
+.I none
+at any time.
+The
+.B -I
+flag causes
+.I fossil
+to check the IP address of incoming connections
+against
+.BR /mnt/ipok ,
+rejecting attaches from disallowed addresses.
+This mechanism is not intended for general use.
+The server
+.I sources.cs.bell-labs.com
+uses it to comply with U.S. crytography
+export regulations.
+.PP
+.I Msg
+prints the maximum internal 9P message queue size
+and the maximum number of 9P processes to
+allocate for serving the queue.
+The
+.B -m
+and
+.B -p
+options set the two variables.
+.PP
+.I Printconfig
+prints the
+.B config
+line for each configured file system
+and prints the
+.B venti
+line, if any, used to configure this file server.
+.PP
+.I Srv
+behaves like listen but uses
+.BI /srv/ name
+rather than a network address.
+With the
+.B -p
+flag, 
+.I srv 
+edits a list of console services rather than 9P services.
+With no arguments,
+.I srv
+prints the current list of services.
+With one argument, srv
+.I name
+starts a new service at
+.IR /srv/name ;
+the
+.B -d
+flag causes 
+.I srv
+to remove the named service.
+See the
+.I [fsys] open
+command below for a description of the
+.B -APW
+options.
+.PP
+.I Uname
+manipulates entries in the user table.
+There is no distinction between users and groups:
+a user is a group with one member.
+For each user, the user table records:
+.TF \fImembers
+.PD
+.TP
+.I id
+the string used to represent this user in the on-disk structures
+.TP
+.I name
+the string used to represent this user in the 9P protocol
+.TP
+.I leader
+the group's leader (see
+.IR stat (5)
+for a description of the special privileges held by a group leader)
+.TP
+.I members
+a comma-separated list of members in this group
+.PP
+The
+.I id
+and
+.I name
+are usually the same string, but need not be.
+Once an
+.I id
+is used in file system structures archived to Venti,
+it is impossible to change those disk structures,
+and thus impossible to rename the
+.IR id .
+The translation from
+.I name
+to
+.I id
+allows the appearance of renaming the user even
+though the on-disk structures still record the old name.
+(In a conventional Unix file system, the
+.I id
+is stored as a small integer rather than a string.)
+.I Leader
+and
+.I members
+are names, not ids.
+.PP
+The first argument to
+.I uname
+is the
+.I name
+of a user.
+The second argument is a verb, one of:
+.TF \fI%newname
+.PD
+.TP
+.I id
+create a user with name
+.RI ` name '
+and id
+.RI ` id ;'
+also create a home directory
+.BI /active/usr/ uname \fR
+.TP
+.BI : id
+create a user with name
+.RI ` name '
+and id
+.RI ` id ,'
+but do not create a home directory
+.TP
+.BI % newname
+rename user
+.RI ` name '
+to
+.RI ` newname ,'
+throughout the user table
+.TP
+.BI = leader
+set
+.IR name 's
+group leader
+to
+.IR leader .
+.TP
+.BI =
+remove
+.IR name 's
+group leader; then all members will be
+considered leaders
+.TP
+.BI + member
+add
+.I member
+to
+.IR name 's
+list of members
+.TP
+.BI - member
+remove
+.I member
+from
+.IR name 's
+list of members
+.LP
+If the verb is omitted, the entire entry for
+.I name
+is printed, in the form
+`\fIid\fL:\fIname\fL:\fIleader\fL:\fImembers\fR.'
+.LP
+The end of this manual page gives examples.
+.PP
+.I Users
+manipulates the user table.
+The user table is a list of lines in the form printed
+by the
+.I uname
+command.
+The
+.B -d
+flag resets the user table with the default:
+.IP
+.EX
+adm:adm:adm:sys
+none:none::
+noworld:noworld::
+sys:sys::
+glenda:glenda:glenda:
+.EE
+.PP
+Except
+.BR glenda ,
+these users are mandatory: they must appear in all user
+files and cannot be renamed.
+.PP
+The
+.B -r
+flag reads a user table from the named
+.I file
+in file system
+.BR main .
+The
+.B -w
+flag writes the table to
+.B /active/adm/users
+on the file system
+.BR main .
+.B /active/adm
+and
+.B /active/adm/users
+will be created if they do not exist.
+.PP
+.I Users
+.B -r
+.B /active/adm/users
+is automatically executed when the file system
+.B main
+is opened.
+.PP
+.I Users
+.B -w
+is automatically executed after each change to the user
+table by the
+.I uname
+command.
+.PP
+.I Who
+prints a list of users attached to each active connection.
+.SS File system configuration
+.I Fsys
+sets the current file system to
+.IR name ,
+which must be configured and open (q.v.).
+The current file system name is
+displayed as the file server prompt.
+The special name
+.B all
+stands for all file systems;
+commands applied to
+.B all
+are applied to each file system in turn.
+The commands
+.BR config ,
+.BR open ,
+.BR venti ,
+and
+.B close
+cannot be applied to
+.BR all .
+.PP
+.I Fsys
+takes as an optional argument
+(after
+.BR name )
+a command to execute on the named file system.
+Most commands require that the named file system
+be configured and open; these commands can be invoked
+without the
+.BI fsys " name
+prefix, in which case the current file system is used.
+A few commands
+.RB ( config ,
+.BR open ,
+and
+.BR unconfig )
+operate on unopened file systems; they require the prefix.
+.PP
+.I Config
+creates a new file system named
+.I name
+using disk file
+.IR device .
+This just adds an entry to fossil's internal table.
+If
+.I device
+is missing,
+the
+.I file
+argument to
+.IR fossil 's
+.B -f
+option will be used instead;
+this allows the
+.I fossil
+configuration file to avoid naming the partition that it is embedded in,
+making it more portable.
+.PP
+.I Venti
+establishes a connection to the Venti server
+.I host
+(by default, the environment variable
+.B $venti
+or the network variable
+.BR $venti )
+for use by the named file system.
+If no
+.I venti
+command is issued before
+.IR open ,
+the default Venti server will be used.
+If the file system is open,
+and was not opened with the
+.B -V
+flag,
+the command redials the Venti server.
+This can be used to reestablish broken connections.
+It is not a good idea to use the command to switch
+between Venti servers, since Fossil does not keep track
+of which blocks are stored on which servers.
+.PP
+.I Open
+opens the file system, reading the
+root and super blocks and allocating an in-memory
+cache for disk and Venti blocks.
+The options are:
+.TF "-c\fI ncache
+.PD
+.TP
+.B -A
+run with no authentication
+.TP
+.B -P
+run with no permission checking
+.TP
+.B -V
+do not attempt to connect to a Venti server
+.TP
+.B -W
+allow wstat to make arbitrary changes to the user and group fields
+.TP
+.B -a
+do not update file access times;
+primarily to avoid wear on flash memories
+.TP
+.B -r
+open the file system read-only
+.TP
+.BI -c " ncache
+allocate an in-memory cache of 
+.I ncache
+(by default, 1000)
+blocks
+.PP
+The
+.I -APW
+settings can be overridden on a per-connection basis
+by the
+.I srv
+command above.
+.PP
+.I Close
+flushes all dirty file system blocks to disk
+and then closes the device file.
+.PP
+.I Unconfig
+removes the named file system (which must be closed)
+from fossil's internal table.
+.br
+.ne 3
+.SS File system maintenance
+.I Bfree
+marks the block at disk address
+.I addr
+as available for allocation.
+Before doing so, it prints a
+.I label
+command (q.v.)
+that can be used to restore the block to its previous state.
+.PP
+.I Block
+displays (in hexadecimal)
+the contents of the block at disk address
+.IR addr ,
+starting at
+.I offset
+and continuing for
+.I count
+bytes or until the end of the block.
+If 
+.I data
+(also hexadecimal)
+is given, the contents in that range are
+replaced with data.
+When writing to a block,
+.I block
+prints the old and new contents,
+so that the change is easily undone.
+Editing blocks is discouraged.
+.PP
+.I Clre
+zeros an entry from a disk block.
+Before doing so, it prints a
+.I block
+command that can be used 
+to restore the entry.
+.PP
+.I Clri
+removes the internal directory entry
+and abandons storage associated with
+.IR files .
+It ignores the usual rules for sanity, such as checking against
+removing a non-empty directory.
+A subsequent
+.I flchk
+(see
+.IR fossil (4))
+will identify the abandoned storage so it can be reclaimed with
+.I bfree
+commands.
+.PP
+.I Clrp
+zeros a pointer in a disk block.
+Before doing so, it prints a 
+.I block
+command that can be used to restore the entry.
+.PP
+.I Check
+checks the file system for various inconsistencies.
+If the file system is not already halted, it is halted for
+the duration of the check.
+If the archiver is currently sending a snapshot to Venti,
+the check will refuse to run; the only recourse is to wait
+for the archiver to finish.
+.PP
+A list of keyword options control the check.
+The
+.BR pblock ,
+.BR pdir ,
+and
+.B pfile
+options cause 
+.I check
+to print the name of each block, directory, or file encountered.
+.PP
+By default,
+.I check
+reports errors but does not fix them.
+The
+.BR bclose ,
+.BR clri ,
+.BR clre ,
+and
+.B clrp
+options specify correcting actions that may be taken:
+closing leaked blocks, clearing bad file directory entries,
+clearing bad pointers, and clearing bad entries.
+The
+.B fix
+option enables all of these; it is equivalent to
+.B bclose
+.B clri
+.B clre
+.BR clrp .
+.PP
+By default,
+.I check
+scans the portion of the active file system held in the write buffer,
+avoiding blocks stored on Venti or used only in snapshots.
+The
+.B venti
+option causes
+.I check
+to scan the portion of the file system stored on Venti,
+and the
+.B snapshot
+option causes
+.I check
+to scan old snapshots.
+Specifying
+.B snapshot
+causes
+.I check
+to take a long time;
+specifying
+.B venti
+or
+(worse)
+.B venti
+.B snapshot
+causes
+.I check
+to take a very long time.
+.PP
+.I Create
+creates a file on the current file system.
+.I Uid
+and
+.I gid
+are uids
+.RI ( not
+unames;
+see the discussion above, in the description
+of the 
+.I uname
+command).
+.I Perm
+is the low 9 bits of the permission mode of the file,
+in octal.
+The 
+.BR a ,
+.BR d ,
+and
+.B l
+mode prefixes
+set the append-only, directory, and lock bits.
+The
+.I perm
+is formatted as described in the
+.I stat
+command;
+creating files or directories with the
+.BR snapshot (s)
+bit set is not allowed.
+.PP
+.I Df
+prints the amount of used disk space in the write buffer.
+.PP
+.I Epoch
+sets the low file system epoch.
+Snapshots in the file system are given increasing epoch numbers.
+The file system maintains a low and a high epoch number,
+and only allows access to snapshots in that range.
+The low epoch number can be moved forward to discard old snapshots
+and reclaim the disk space they occupy.
+(The high epoch number is always the epoch of the currently
+active file system.)
+.PP
+With no argument
+.I epoch
+reports the current low and high epoch numbers.
+The command
+``\fLepoch\fI n''\fR
+is used to propose changing the low epoch to
+.IR n .
+In response, 
+.I fossil
+scans
+.B /archive
+and
+.B /snapshot
+for snapshots that would be discarded, printing their
+epoch numbers and the
+.I clri
+commands necessary to remove them.
+The epoch is changed only if no such paths are found.
+The usual sequence of commands is (1) run epoch to
+print the snapshots and their epochs, (2) clri some snapshots,
+(3) run epoch again.
+If the file system is completely full (there are no free blocks),
+.I clri
+may fail because it needs to allocate blocks.
+For this situation,
+the
+.B -y
+flag to epoch forces the epoch change even when
+it means discarding currently accessible snapshots.
+Note that when there are still snapshots in
+.BR /archive ,
+the archiver should take care
+of those snapshots (moving the blocks from disk to Venti)
+if you give it more time.
+.PP
+The
+.B -r
+flag to epoch causes it to remove any now-inaccessible
+snapshot directories once it has changed the epoch.
+This flag only makes sense in conjunction with the
+.B -y
+flag.
+.PP
+.I Epoch
+is a very low-level way to retire snapshots.
+The preferred way is by setting an automatic timer
+with
+.IR snaptime .
+.PP
+.I Halt
+suspends all file system activity;
+.I unhalt
+resumes activity.
+.PP
+.I Label
+displays and edits the label associated with a block.
+When editing, a parameter of
+.B -
+means leave that field unchanged.
+Editing labels is discouraged.
+.PP
+.I Remove
+removes
+.IR files .
+.PP
+.I Snap
+takes a temporary snapshot of the current file system,
+recording it in 
+.BI /snapshot/ yyyy / mmdd / hhmm \fR,
+as described in 
+.IR fossil (4).
+The
+.B -a
+flag causes 
+.I snap
+to take an archival snapshot, recording it in
+.BI /archive/ yyyy / mmdd \fR,
+also described in
+.IR fossil (4).
+By default the snapshot is taken of
+.BR /active ,
+the root of the active file system.
+The 
+.B -s
+flag specifies a different source path.
+The
+.B -d
+flag specifies a different destination path.
+These  two flags are useful together for moving snapshots into
+the archive tree.
+.PP
+.I Snapclean
+immediately discards all snapshots that are more than
+.I timeout
+minutes old.
+The default timeout is the one set by the
+.I snaptime
+command.
+The discarding is a one-time event rather than
+a recurring event as in
+.IR snaptime .
+.PP
+.I Snaptime
+displays and edits the times at which snapshots are automatically
+taken.
+An archival snapshot is taken once a day, at
+.IR hhmm ,
+while temporary snapshots are taken at multiples of
+.I interval
+minutes.
+Temporary snapshots are discarded after they are
+.I timeout
+minutes old.
+The snapshot cleanup runs every
+.I timeout
+minutes or once a day, whichever is more frequent,
+so snapshots may grow to an age of almost twice the timeout
+before actually being discarded.
+With no arguments,
+.I snaptime
+prints the current snapshot times.
+The
+.B -a
+and
+.B -s
+options set the archive and snapshot times.
+An
+.I hhmm
+or
+.I interval
+of
+.L none
+can be used to disable that kind of automatic snapshot.
+The
+.B -t
+option sets the snapshot timeout.
+If
+.I timeout
+is
+.LR none ,
+temporary snapshots are not automatically discarded.
+By default, all three times are set to
+.LR none .
+.PP
+.I Stat
+displays metadata for each of the named
+.IR files ,
+in the form:
+.IP
+.EX
+stat \fIfile elem uid gid perm length
+.EE
+.LP
+(Replacing
+.B stat
+with
+.B wstat
+yields a valid command.)
+The
+.I perm
+is an octal number less than or equal to 777,
+prefixed with any of the following letters
+to indicate additional bits.
+.IP
+.EX
+.ta +4n
+a	\fRappend only
+d	\fRdirectory
+l	\fRexclusive use
+s	\fRis the root of a snapshot
+t	\fRtemporary bit
+A	\fRMS-DOS archive bit
+G	\fRsetgid
+H	\fRMS-DOS hidden bit
+L	\fRsymbolic link
+S	\fRMS-DOS system bit
+U	\fRsetuid
+Y	\fRsticky
+.EE
+.PP
+The bits denoted by capital letters are included
+to support non-Plan 9 systems.
+They are not made visible by the 9P protocol.
+.PP
+.I Sync
+writes dirty blocks in memory to the disk.
+.PP
+.I Vac
+prints the Venti score for a
+.IR vac (1)
+archive containing the tree rooted
+at
+.IR dir ,
+which must already be archived to Venti
+(typically
+.IR dir
+is a directory in the
+.B /archive
+tree).
+.PP
+.I Wstat
+changes the metadata of the named
+.IR file .
+Specifying
+.B -
+for any of the fields means ``don't change.''
+Attempts to change the
+.B d
+or
+.B s
+bits in the
+.I perm
+are silently ignored.
+.SH EXAMPLES
+.IR Sources ,
+the Plan 9 distribution file server,
+uses the following configuration file:
+.IP
+.EX
+srv -p fscons.sources
+srv -p fscons.sources.adduserd
+srv sources
+fsys main config /dev/sdC0/fossil.outside
+fsys main open -c 25600
+fsys main
+users /active/adm/users
+listen tcp!*!564
+msg -m 40 -p 10
+snaptime -a 0000 -s 15
+.EE
+.LP
+The second console is used by the daemon
+that creates new accounts.
+.PP
+To add a new user with
+.I name
+and
+.I id
+.B rob
+and create his home directory:
+.IP
+.EX
+uname rob rob
+.EE
+.PP
+To create a new group
+.B sys
+(with no home directory)
+and add
+.B rob
+to it:
+.IP
+.EX
+uname sys :sys
+uname sys +rob
+.EE
+.PP
+To save an old (but not yet discarded) snapshot into the archive tree:
+.IP
+.EX
+snap -a -s /snapshot/2003/1220/0700 -d /archive/2003/1220
+.EE

src/cmd/fossil/9.h

+#include <auth.h>
+#include <fcall.h>
+
+enum {
+	NFidHash	= 503,
+};
+
+typedef struct Con Con;
+typedef struct DirBuf DirBuf;
+typedef struct Excl Excl;
+typedef struct Fid Fid;
+typedef struct Fsys Fsys;
+typedef struct Msg Msg;
+
+#pragma incomplete DirBuf
+#pragma incomplete Excl
+#pragma incomplete Fsys
+
+struct Msg {
+	uchar*	data;
+	u32int	msize;			/* actual size of data */
+	Fcall	t;
+	Fcall	r;
+	Con*	con;
+
+	Msg*	anext;			/* allocation free list */
+
+	Msg*	mnext;			/* all active messsages on this Con */
+	Msg* 	mprev;
+
+	int	state;			/* */
+
+	Msg*	flush;			/* flushes waiting for this Msg */
+
+	Msg*	rwnext;			/* read/write queue */
+	int	nowq;			/* do not place on write queue */
+};
+
+enum {
+	MsgN		= 0,
+	MsgR		= 1,
+	Msg9		= 2,
+	MsgW		= 3,
+	MsgF		= 4,
+};
+
+enum {
+	ConNoneAllow	= 1<<0,
+	ConNoAuthCheck	= 1<<1,
+	ConNoPermCheck	= 1<<2,
+	ConWstatAllow	= 1<<3,
+	ConIPCheck	= 1<<4,
+};
+struct Con {
+	char*	name;
+	uchar*	data;			/* max, not negotiated */
+	int	isconsole;		/* immutable */
+	int	flags;			/* immutable */
+	char	remote[128];		/* immutable */
+	VtLock*	lock;
+	int	state;
+	int	fd;
+	Msg*	version;
+	u32int	msize;			/* negotiated with Tversion */
+	VtRendez* rendez;
+
+	Con*	anext;			/* alloc */
+	Con*	cnext;			/* in use */
+	Con*	cprev;
+
+	VtLock*	alock;
+	int	aok;			/* authentication done */
+
+	VtLock*	mlock;
+	Msg*	mhead;			/* all Msgs on this connection */
+	Msg*	mtail;
+	VtRendez* mrendez;
+
+	VtLock*	wlock;
+	Msg*	whead;			/* write queue */
+	Msg*	wtail;
+	VtRendez* wrendez;
+
+	VtLock*	fidlock;		/* */
+	Fid*	fidhash[NFidHash];
+	Fid*	fhead;
+	Fid*	ftail;
+	int	nfid;
+};
+
+enum {
+	ConDead		= 0,
+	ConNew		= 1,
+	ConDown		= 2,
+	ConInit		= 3,
+	ConUp		= 4,
+	ConMoribund	= 5,
+};
+
+struct Fid {
+	VtLock*	lock;
+	Con*	con;
+	u32int	fidno;
+	int	ref;			/* inc/dec under Con.fidlock */
+	int	flags;
+
+	int	open;
+	Fsys*	fsys;
+	File*	file;
+	Qid	qid;
+	char*	uid;
+	char*	uname;
+	DirBuf*	db;
+	Excl*	excl;
+
+	VtLock*	alock;			/* Tauth/Tattach */
+	AuthRpc* rpc;
+	char*	cuname;
+
+	Fid*	sort;			/* sorted by uname in cmdWho */
+	Fid*	hash;			/* lookup by fidno */
+	Fid*	next;			/* clunk session with Tversion */
+	Fid*	prev;
+};
+
+enum {					/* Fid.flags and fidGet(..., flags) */
+	FidFCreate	= 0x01,
+	FidFWlock	= 0x02,
+};
+
+enum {					/* Fid.open */
+	FidOCreate	= 0x01,
+	FidORead	= 0x02,
+	FidOWrite	= 0x04,
+	FidORclose	= 0x08,
+};
+
+/*
+ * 9p.c
+ */
+extern int (*rFcall[Tmax])(Msg*);
+extern int validFileName(char*);
+
+/*
+ * 9auth.c
+ */
+extern int authCheck(Fcall*, Fid*, Fsys*);
+extern int authRead(Fid*, void*, int);
+extern int authWrite(Fid*, void*, int);
+
+/*
+ * 9dir.c
+ */
+extern void dirBufFree(DirBuf*);
+extern int dirDe2M(DirEntry*, uchar*, int);
+extern int dirRead(Fid*, uchar*, int, vlong);
+
+/*
+ * 9excl.c
+ */
+extern int exclAlloc(Fid*);
+extern void exclFree(Fid*);
+extern void exclInit(void);
+extern int exclUpdate(Fid*);
+
+/*
+ * 9fid.c
+ */
+extern void fidClunk(Fid*);
+extern void fidClunkAll(Con*);
+extern Fid* fidGet(Con*, u32int, int);
+extern void fidInit(void);
+extern void fidPut(Fid*);
+
+/*
+ * 9fsys.c
+ */
+extern void fsysFsRlock(Fsys*);
+extern void fsysFsRUnlock(Fsys*);
+extern Fs* fsysGetFs(Fsys*);
+extern Fsys* fsysGet(char*);
+extern char* fsysGetName(Fsys*);
+extern File* fsysGetRoot(Fsys*, char*);
+extern Fsys* fsysIncRef(Fsys*);
+extern int fsysInit(void);
+extern int fsysNoAuthCheck(Fsys*);
+extern int fsysNoPermCheck(Fsys*);
+extern void fsysPut(Fsys*);
+extern int fsysWstatAllow(Fsys*);
+
+/*
+ * 9lstn.c
+ */
+extern int lstnInit(void);
+
+/*
+ * 9proc.c
+ */
+extern Con* conAlloc(int, char*, int);
+extern void conInit(void);
+extern void msgFlush(Msg*);
+extern void msgInit(void);
+
+/*
+ * 9srv.c
+ */
+extern int srvInit(void);
+
+/*
+ * 9user.c
+ */
+extern int groupLeader(char*, char*);
+extern int groupMember(char*, char*);
+extern int groupWriteMember(char*);
+extern char* unameByUid(char*);
+extern char* uidByUname(char*);
+extern int usersInit(void);
+extern int usersFileRead(char*);
+extern int validUserName(char*);
+
+extern char* uidadm;
+extern char* unamenone;
+extern char* uidnoworld;
+
+/*
+ * Ccli.c
+ */
+extern int cliAddCmd(char*, int (*)(int, char*[]));
+extern int cliError(char*, ...);
+extern int cliInit(void);
+extern int cliExec(char*);
+#pragma	varargck	argpos	cliError	1
+
+/*
+ * Ccmd.c
+ */
+extern int cmdInit(void);
+
+/*
+ * Ccons.c
+ */
+extern int consPrompt(char*);
+extern int consInit(void);
+extern int consOpen(int, int, int);
+extern int consTTY(void);
+extern int consWrite(char*, int);
+
+/*
+ * Clog.c
+ */
+extern int consPrint(char*, ...);
+extern int consVPrint(char*, va_list);
+#pragma	varargck	argpos	consPrint	1
+
+/*
+ * fossil.c
+ */
+extern int Dflag;

src/cmd/fossil/9auth.c

+#include "stdinc.h"
+#include "9.h"
+
+int
+authRead(Fid* afid, void* data, int count)
+{
+	AuthInfo *ai;
+	AuthRpc *rpc;
+
+	if((rpc = afid->rpc) == nil){
+		vtSetError("not an auth fid");
+		return -1;
+	}
+
+	switch(auth_rpc(rpc, "read", nil, 0)){
+	default:
+		vtSetError("fossil authRead: auth protocol not finished");
+		return -1;
+	case ARdone:
+		if((ai = auth_getinfo(rpc)) == nil){
+			vtSetError("%r");
+			break;
+		}
+		if(ai->cuid == nil || *ai->cuid == '\0'){
+			vtSetError("auth with no cuid");
+			auth_freeAI(ai);
+			break;
+		}
+		assert(afid->cuname == nil);
+		afid->cuname = vtStrDup(ai->cuid);
+		auth_freeAI(ai);
+		if(Dflag)
+			fprint(2, "authRead cuname %s\n", afid->cuname);
+		assert(afid->uid == nil);
+		if((afid->uid = uidByUname(afid->cuname)) == nil){
+			vtSetError("unknown user %#q", afid->cuname);
+			break;
+		}
+		return 0;
+	case ARok:
+		if(count < rpc->narg){
+			vtSetError("not enough data in auth read");
+			break;
+		}
+		memmove(data, rpc->arg, rpc->narg);
+		return rpc->narg;
+	case ARphase:
+		vtSetError("%r");
+		break;
+	}
+	return -1;
+}
+
+int
+authWrite(Fid* afid, void* data, int count)
+{
+	assert(afid->rpc != nil);
+	if(auth_rpc(afid->rpc, "write", data, count) != ARok)
+		return -1;
+	return count;
+}
+
+int
+authCheck(Fcall* t, Fid* fid, Fsys* fsys)
+{
+	Con *con;
+	Fid *afid;
+	uchar buf[1];
+
+	/*
+	 * Can't lookup with FidWlock here as there may be
+	 * protocol to do. Use a separate lock to protect altering
+	 * the auth information inside afid.
+	 */
+	con = fid->con;
+	if(t->afid == NOFID){
+		/*
+		 * If no authentication is asked for, allow
+		 * "none" provided the connection has already
+		 * been authenticatated.
+		 *
+		 * The console is allowed to attach without
+		 * authentication.
+		 */
+		vtRLock(con->alock);
+		if(con->isconsole){
+			/* anything goes */
+		}else if((con->flags&ConNoneAllow) || con->aok){
+			static int noneprint;
+
+			if(noneprint++ < 10)
+				consPrint("attach %s as %s: allowing as none\n",
+					fsysGetName(fsys), fid->uname);
+			vtMemFree(fid->uname);
+			fid->uname = vtStrDup(unamenone);
+		}else{
+			vtRUnlock(con->alock);
+			consPrint("attach %s as %s: connection not authenticated, not console\n",
+				fsysGetName(fsys), fid->uname);
+			vtSetError("cannot attach as none before authentication");
+			return 0;
+		}
+		vtRUnlock(con->alock);
+
+		if((fid->uid = uidByUname(fid->uname)) == nil){
+			consPrint("attach %s as %s: unknown uname\n",
+				fsysGetName(fsys), fid->uname);
+			vtSetError("unknown user");
+			return 0;
+		}
+		return 1;
+	}
+
+	if((afid = fidGet(con, t->afid, 0)) == nil){
+		consPrint("attach %s as %s: bad afid\n",
+			fsysGetName(fsys), fid->uname);
+		vtSetError("bad authentication fid");
+		return 0;
+	}
+
+	/*
+	 * Check valid afid;
+	 * check uname and aname match.
+	 */
+	if(!(afid->qid.type & QTAUTH)){
+		consPrint("attach %s as %s: afid not an auth file\n",
+			fsysGetName(fsys), fid->uname);
+		fidPut(afid);
+		vtSetError("bad authentication fid");
+		return 0;
+	}
+	if(strcmp(afid->uname, fid->uname) != 0 || afid->fsys != fsys){
+		consPrint("attach %s as %s: afid is for %s as %s\n",
+			fsysGetName(fsys), fid->uname,
+			fsysGetName(afid->fsys), afid->uname);
+		fidPut(afid);
+		vtSetError("attach/auth mismatch");
+		return 0;
+	}
+
+	vtLock(afid->alock);
+	if(afid->cuname == nil){
+		if(authRead(afid, buf, 0) != 0 || afid->cuname == nil){
+			vtUnlock(afid->alock);
+			consPrint("attach %s as %s: %R\n",
+				fsysGetName(fsys), fid->uname);
+			fidPut(afid);
+			vtSetError("fossil authCheck: auth protocol not finished");
+			return 0;
+		}
+	}
+	vtUnlock(afid->alock);
+
+	assert(fid->uid == nil);
+	if((fid->uid = uidByUname(afid->cuname)) == nil){
+		consPrint("attach %s as %s: unknown cuname %s\n",
+			fsysGetName(fsys), fid->uname, afid->cuname);
+		fidPut(afid);
+		vtSetError("unknown user");
+		return 0;
+	}
+
+	vtMemFree(fid->uname);
+	fid->uname = vtStrDup(afid->cuname);
+	fidPut(afid);
+
+	/*
+	 * Allow "none" once the connection has been authenticated.
+	 */
+	vtLock(con->alock);
+	con->aok = 1;
+	vtUnlock(con->alock);
+
+	return 1;
+}

src/cmd/fossil/9dir.c

+#include "stdinc.h"
+
+#include "9.h"
+
+/* one entry buffer for reading directories */
+struct DirBuf {
+	DirEntryEnum*	dee;
+	int		valid;
+	DirEntry	de;
+};
+
+static DirBuf*
+dirBufAlloc(File* file)
+{
+	DirBuf *db;
+
+	db = vtMemAllocZ(sizeof(DirBuf));
+	db->dee = deeOpen(file);
+	if(db->dee == nil){
+		/* can happen if dir is removed from under us */
+		vtMemFree(db);
+		return nil;
+	}
+	return db;
+}
+
+void
+dirBufFree(DirBuf* db)
+{
+	if(db == nil)
+		return;
+
+	if(db->valid)
+		deCleanup(&db->de);
+	deeClose(db->dee);
+	vtMemFree(db);
+}
+
+int
+dirDe2M(DirEntry* de, uchar* p, int np)
+{
+	int n;
+	Dir dir;
+
+	memset(&dir, 0, sizeof(Dir));
+
+	dir.qid.path = de->qid;
+	dir.qid.vers = de->mcount;
+	dir.mode = de->mode & 0777;
+	if(de->mode & ModeAppend){
+		dir.qid.type |= QTAPPEND;
+		dir.mode |= DMAPPEND;
+	}
+	if(de->mode & ModeExclusive){
+		dir.qid.type |= QTEXCL;
+		dir.mode |= DMEXCL;
+	}
+	if(de->mode & ModeDir){
+		dir.qid.type |= QTDIR;
+		dir.mode |= DMDIR;
+	}
+	if(de->mode & ModeSnapshot){
+		dir.qid.type |= QTMOUNT;	/* just for debugging */
+		dir.mode |= DMMOUNT;
+	}
+	if(de->mode & ModeTemporary){
+		dir.qid.type |= QTTMP;
+		dir.mode |= DMTMP;
+	}
+
+	dir.atime = de->atime;
+	dir.mtime = de->mtime;
+	dir.length = de->size;
+
+	dir.name = de->elem;
+	if((dir.uid = unameByUid(de->uid)) == nil)
+		dir.uid = smprint("(%s)", de->uid);
+	if((dir.gid = unameByUid(de->gid)) == nil)
+		dir.gid = smprint("(%s)", de->gid);
+	if((dir.muid = unameByUid(de->mid)) == nil)
+		dir.muid = smprint("(%s)", de->mid);
+
+	n = convD2M(&dir, p, np);
+
+	vtMemFree(dir.muid);
+	vtMemFree(dir.gid);
+	vtMemFree(dir.uid);
+
+	return n;
+}
+
+int
+dirRead(Fid* fid, uchar* p, int count, vlong offset)
+{
+	int n, nb;
+	DirBuf *db;
+
+	/*
+	 * special case of rewinding a directory
+	 * otherwise ignore the offset
+	 */
+	if(offset == 0 && fid->db){
+		dirBufFree(fid->db);
+		fid->db = nil;
+	}
+
+	if(fid->db == nil){
+		fid->db = dirBufAlloc(fid->file);
+		if(fid->db == nil)
+			return -1;
+	}
+
+	db = fid->db;
+
+	for(nb = 0; nb < count; nb += n){
+		if(!db->valid){
+			n = deeRead(db->dee, &db->de);
+			if(n < 0)
+				return -1;
+			if(n == 0)
+				break;
+			db->valid = 1;
+		}
+		n = dirDe2M(&db->de, p+nb, count-nb);
+		if(n <= BIT16SZ)