e9e55a21f6
with the latest changes to shr(3), we can use ORCLOSE on the control file to get the mount in the share automatically removed when the server exits or something goes wrong during postsharesrv(). do not expose postfd() and sharefd() functions. they where undocumented and leak the control file descriptors.
831 lines
17 KiB
Text
831 lines
17 KiB
Text
.TH 9P 2
|
|
.SH NAME
|
|
Srv,
|
|
chatty9p,
|
|
dirread9p,
|
|
emalloc9p,
|
|
erealloc9p,
|
|
estrdup9p,
|
|
listensrv,
|
|
postmountsrv,
|
|
postsharesrv,
|
|
readbuf,
|
|
readstr,
|
|
respond,
|
|
responderror,
|
|
srvacquire,
|
|
srvrelease,
|
|
threadlistensrv,
|
|
threadpostmountsrv,
|
|
threadpostsharesrv,
|
|
srv \- 9P file service
|
|
.SH SYNOPSIS
|
|
.ft L
|
|
.nf
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include <fcall.h>
|
|
#include <thread.h>
|
|
#include <9p.h>
|
|
.fi
|
|
.PP
|
|
.ft L
|
|
.nf
|
|
.ta \w'\fL1234'u +\w'\fLTree* 'u
|
|
typedef struct Srv {
|
|
Tree* tree;
|
|
|
|
void (*attach)(Req *r);
|
|
void (*auth)(Req *r);
|
|
void (*open)(Req *r);
|
|
void (*create)(Req *r);
|
|
void (*read)(Req *r);
|
|
void (*write)(Req *r);
|
|
void (*remove)(Req *r);
|
|
void (*flush)(Req *r);
|
|
void (*stat)(Req *r);
|
|
void (*wstat)(Req *r);
|
|
void (*walk)(Req *r);
|
|
|
|
char* (*walk1)(Fid *fid, char *name, Qid *qid);
|
|
char* (*clone)(Fid *oldfid, Fid *newfid);
|
|
|
|
void (*destroyfid)(Fid *fid);
|
|
void (*destroyreq)(Req *r);
|
|
void (*start)(Srv *s);
|
|
void (*end)(Srv *s);
|
|
void* aux;
|
|
|
|
int infd;
|
|
int outfd;
|
|
int srvfd;
|
|
} Srv;
|
|
.fi
|
|
.PP
|
|
.nf
|
|
.ft L
|
|
.ta \w'\fLvoid* 'u
|
|
void srv(Srv *s)
|
|
void postmountsrv(Srv *s, char *name, char *mtpt, int flag)
|
|
void postsharesrv(Srv *s, char *name, char *mtpt, char *desc)
|
|
void threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag)
|
|
void threadpostsharesrv(Srv *s, char *name, char *mtpt, char *desc)
|
|
void listensrv(Srv *s, char *addr)
|
|
void threadlistensrv(Srv *s, char *addr)
|
|
void respond(Req *r, char *error)
|
|
void responderror(Req*)
|
|
void readstr(Req *r, char *src)
|
|
void readbuf(Req *r, void *src, long nsrc)
|
|
typedef int Dirgen(int n, Dir *dir, void *aux)
|
|
void dirread9p(Req *r, Dirgen *gen, void *aux)
|
|
void walkandclone(Req *r, char *(*walk1)(Fid *old, char *name, void *v),
|
|
char *(*clone)(Fid *old, Fid *new, void *v), void *v)
|
|
.fi
|
|
.PP
|
|
.nf
|
|
.ft L
|
|
.ta \w'\fLvoid* 'u
|
|
void srvrelease(Srv *s)
|
|
void srvacquire(Srv *s)
|
|
.fi
|
|
.PP
|
|
.fi
|
|
.PP
|
|
.nf
|
|
.ft L
|
|
.ta \w'\fLvoid* 'u
|
|
void* emalloc9p(ulong n)
|
|
void* erealloc9p(void *v, ulong n)
|
|
char* estrdup9p(char *s)
|
|
.fi
|
|
.PP
|
|
.nf
|
|
.ft L
|
|
extern int chatty9p;
|
|
.fi
|
|
.SH DESCRIPTION
|
|
The function
|
|
.I srv
|
|
serves a 9P session by reading requests from
|
|
.BR s->infd ,
|
|
dispatching them to the function pointers kept in
|
|
.BR Srv ,
|
|
and
|
|
writing the responses to
|
|
.BR s->outfd .
|
|
(Typically,
|
|
.I postmountsrv
|
|
or
|
|
.I threadpostmountsrv
|
|
initializes the
|
|
.B infd
|
|
and
|
|
.B outfd
|
|
structure members. See the description below.)
|
|
.PP
|
|
.B Req
|
|
and
|
|
.B Fid
|
|
structures are allocated one-to-one with uncompleted
|
|
requests and active fids, and are described in
|
|
.IR 9pfid (2).
|
|
.PP
|
|
The behavior of
|
|
.I srv
|
|
depends on whether there is a file tree
|
|
(see
|
|
.IR 9pfile (2))
|
|
associated with the server, that is,
|
|
whether the
|
|
.B tree
|
|
element is nonzero.
|
|
The differences are made explicit in the
|
|
discussion of the service loop below.
|
|
The
|
|
.B aux
|
|
element is the client's, to do with as it pleases.
|
|
.PP
|
|
.I Srv
|
|
does not return until the 9P conversation is finished.
|
|
Since it is usually run in a separate process so that
|
|
the caller can exit, the service loop has little chance
|
|
to return gracefully on out of memory errors.
|
|
It calls
|
|
.IR emalloc9p ,
|
|
.IR erealloc9p ,
|
|
and
|
|
.I estrdup9p
|
|
to obtain its memory.
|
|
The default implementations of these functions
|
|
act as
|
|
.IR malloc ,
|
|
.IR realloc ,
|
|
and
|
|
.I strdup
|
|
but abort the program if they run out of memory.
|
|
If alternate behavior is desired, clients can link against
|
|
alternate implementations of these functions.
|
|
.PP
|
|
.I Postmountsrv
|
|
and
|
|
.I threadpostmountsrv
|
|
are wrappers that create a separate process in which to run
|
|
.IR srv .
|
|
They do the following:
|
|
.IP
|
|
Initialize
|
|
.IB s -> infd
|
|
and
|
|
.IB s -> outfd
|
|
to be one end of a freshly allocated pipe,
|
|
with
|
|
.IB s -> srvfd
|
|
initialized as the other end.
|
|
.IP
|
|
If
|
|
.B name
|
|
is non-nil, post the file descriptor
|
|
.IB s -> srvfd
|
|
under the name
|
|
.BI /srv/ name .
|
|
.IP
|
|
Fork a child process via
|
|
.I rfork
|
|
(see
|
|
.IR fork (2))
|
|
or
|
|
.I procrfork
|
|
(see
|
|
.IR thread (2)),
|
|
using the
|
|
.BR RFPROC ,
|
|
.BR RFNOWAIT ,
|
|
.BR RFNAMEG ,
|
|
.B RFNOTEG
|
|
and
|
|
.B RFMEM
|
|
flags. This isolates the service loop from the callers
|
|
namespace and from notes posted to the callers note group
|
|
but shares data and bss segments.
|
|
.IP
|
|
The child process then waits for the parent to copy its
|
|
file descripor table via
|
|
.I rfork
|
|
using
|
|
.B RFFDG
|
|
flag. This way, the service loop will share the original
|
|
file descriptor table with previously created child
|
|
processes of the caller.
|
|
.IP
|
|
The child process then calls
|
|
.IB close( s -> srvfd )
|
|
and then
|
|
.IB srv( s ) \fR;
|
|
it will exit once
|
|
.I srv
|
|
returns.
|
|
.IP
|
|
If
|
|
.I mtpt
|
|
is non-nil,
|
|
call
|
|
.BI amount( s -> srvfd,
|
|
.IB mtpt ,
|
|
.IB flag ,
|
|
\fB"")\fR;
|
|
otherwise, close
|
|
.IB s -> srvfd \fR.
|
|
.IP
|
|
The parent returns to the caller.
|
|
.LP
|
|
If any error occurs during
|
|
this process, the entire process is terminated by calling
|
|
.I sysfatal
|
|
(see
|
|
.IR perror (2)).
|
|
.PP
|
|
.I Postsharesrv
|
|
is similar to
|
|
.I Postmountsrv
|
|
but instead of mounting the service on a directory, it is
|
|
put in a share (see
|
|
.IR shr (3))
|
|
where
|
|
.IB mtpt
|
|
is the name of the share and
|
|
.IB desc
|
|
is the name of the service channel.
|
|
.PP
|
|
.I Listensrv
|
|
and
|
|
.I threadlistensrv
|
|
create a separate process to announce as
|
|
.IR addr .
|
|
The process listens for incoming connections,
|
|
creating a new process to serve each.
|
|
Using these functions results in
|
|
.I srv
|
|
and the service functions
|
|
being run in multiple processes simultaneously.
|
|
The library locks its own data structures as necessary;
|
|
the client may need to lock data it shares between
|
|
the multiple connections.
|
|
.SS Service functions
|
|
The functions in a
|
|
.B Srv
|
|
structure named after 9P transactions
|
|
are called to satisfy requests as they arrive.
|
|
If a function is provided, it
|
|
.I must
|
|
arrange for
|
|
.I respond
|
|
to be called when the request is satisfied.
|
|
The only parameter of each service function
|
|
is a
|
|
.B Req*
|
|
parameter (say
|
|
.IR r ).
|
|
The incoming request parameters are stored in
|
|
.IB r -> ifcall \fR;
|
|
.IB r -> fid
|
|
and
|
|
.IB r -> newfid
|
|
are pointers to
|
|
.B Fid
|
|
structures corresponding to the
|
|
numeric fids in
|
|
.IB r -> ifcall \fR;
|
|
similarly,
|
|
.IB r -> oldreq
|
|
is the
|
|
.B Req
|
|
structure corresponding to
|
|
.IB r -> ifcall.oldtag \fR.
|
|
The outgoing response data should be stored in
|
|
.IB r -> ofcall \fR.
|
|
The one exception to this rule is that
|
|
.I stat
|
|
should fill in
|
|
.IB r -> d
|
|
rather than
|
|
.IB r -> ofcall.stat \fR:
|
|
the library will convert the structure into the machine-independent
|
|
wire representation.
|
|
Similarly,
|
|
.I wstat
|
|
may consult
|
|
.IB r -> d
|
|
rather than decoding
|
|
.IB r -> ifcall . stat
|
|
itself.
|
|
When a request has been handled,
|
|
.I respond
|
|
should be called with
|
|
.I r
|
|
and an error string.
|
|
If the request was satisfied successfully, the error
|
|
string should be a nil pointer.
|
|
Note that it is permissible for a function to return
|
|
without itself calling
|
|
.IR respond ,
|
|
as long as it has arranged for
|
|
.I respond
|
|
to be called at some point in the future
|
|
by another proc sharing its address space,
|
|
but see the discussion of
|
|
.I flush
|
|
below.
|
|
Once
|
|
.I respond
|
|
has been called, the
|
|
.B Req*
|
|
as well as any pointers it once contained must
|
|
be considered freed and not referenced.
|
|
.PP
|
|
.I Responderror
|
|
calls
|
|
.I respond
|
|
with the system error string
|
|
(see
|
|
.IR errstr (2)).
|
|
.PP
|
|
If the service loop detects an error in a request
|
|
(e.g., an attempt to reuse an extant fid, an open of
|
|
an already open fid, a read from a fid opened for write, etc.)
|
|
it will reply with an error without consulting
|
|
the service functions.
|
|
.PP
|
|
The service loop provided by
|
|
.I srv
|
|
(and indirectly by
|
|
.I postmountsrv
|
|
and
|
|
.IR threadpostmountsrv )
|
|
is single-threaded.
|
|
If it is expected that some requests might
|
|
block, arranging for alternate processes
|
|
to handle them is suggested (see
|
|
.IR 9pqueue (2)).
|
|
.PP
|
|
.I Srvrelease
|
|
temporarily releases the calling process from the server
|
|
loop and if neccesary spawns a new process to handle 9p
|
|
requests. When released, the process can do blocking work
|
|
that would otherwise halt processing of 9p requests.
|
|
.I Srvacquire
|
|
rejoins the calling process with the server loop after
|
|
a srvrelease.
|
|
.PP
|
|
The constraints on the service functions are as follows.
|
|
These constraints are checked while the server executes.
|
|
If a service function fails to do something it ought to have,
|
|
.I srv
|
|
will call
|
|
.I endsrv
|
|
and then abort.
|
|
.TP
|
|
.I Auth
|
|
If authentication is desired,
|
|
the
|
|
.I auth
|
|
function should record that
|
|
.IB r -> afid
|
|
is the new authentication fid and
|
|
set
|
|
.IB r -> afid -> qid
|
|
and
|
|
.IR ofcall.qid .
|
|
.I Auth
|
|
may be nil, in which case it will be treated as having
|
|
responded with the error
|
|
.RI `` "argv0: authentication not required" ,''
|
|
where
|
|
.I argv0
|
|
is the program name variable as set by
|
|
.I ARGBEGIN
|
|
(see
|
|
.IR arg (2)).
|
|
.TP
|
|
.I Attach
|
|
The
|
|
.I attach
|
|
function should check the authentication state of
|
|
.I afid
|
|
if desired,
|
|
and set
|
|
.IB r -> fid -> qid
|
|
and
|
|
.I ofcall.qid
|
|
to the qid of the file system root.
|
|
.I Attach
|
|
may be nil only if file trees are in use;
|
|
in this case, the qid will be filled from the root
|
|
of the tree, and no authentication will be done.
|
|
.TP
|
|
.I Walk
|
|
If file trees are in use,
|
|
.I walk
|
|
is handled internally, and
|
|
.IB srv -> walk
|
|
is never called.
|
|
.IP
|
|
If file trees are not in use,
|
|
.I walk
|
|
should consult
|
|
.IB r -> ifcall . wname
|
|
and
|
|
.IB r -> ifcall . nwname \fR,
|
|
filling in
|
|
.IB ofcall . qid
|
|
and
|
|
.IB ofcall . nqid \fR,
|
|
and also copying any necessary
|
|
.I aux
|
|
state from
|
|
.IB r -> fid
|
|
to
|
|
.IB r -> newfid
|
|
when the two are different.
|
|
As long as
|
|
.I walk
|
|
sets
|
|
.IB ofcall . nqid
|
|
appropriately, it can
|
|
.I respond
|
|
with a nil error string even when 9P
|
|
demands an error
|
|
.RI ( e.g. ,
|
|
in the case of a short walk);
|
|
the library detects error conditions and handles them appropriately.
|
|
.IP
|
|
Because implementing the full walk message is intricate and
|
|
prone to error, the helper routine
|
|
.I walkandclone
|
|
will handle the request given pointers to two functions
|
|
.I walk1
|
|
and (optionally)
|
|
.I clone .
|
|
.IR Clone ,
|
|
if non-nil, is called to signal the creation of
|
|
.I newfid
|
|
from
|
|
.IR oldfid .
|
|
Typically a
|
|
.I clone
|
|
routine will copy or increment a reference count in
|
|
.IR oldfid 's
|
|
.I aux
|
|
element.
|
|
.I Walk1
|
|
should walk
|
|
.I fid
|
|
to
|
|
.IR name ,
|
|
initializing
|
|
.IB fid -> qid
|
|
to the new path's qid.
|
|
Both should return nil
|
|
on success or an error message on error.
|
|
.I Walkandclone
|
|
will call
|
|
.I respond
|
|
after handling the request.
|
|
.TP
|
|
.I Walk1\fR, \fPClone
|
|
If the client provides functions
|
|
.IB srv -> walk1
|
|
and (optionally)
|
|
.IB srv -> clone \fR,
|
|
the 9P service loop will call
|
|
.I walkandclone
|
|
with these functions to handle the request.
|
|
Unlike the
|
|
.I walk1
|
|
above,
|
|
.IB srv -> walk1
|
|
must fill in both
|
|
.IB fid -> qid
|
|
and
|
|
.BI * qid
|
|
with the new qid on a successful walk.
|
|
.TP
|
|
.I Open
|
|
If file trees are in use, the file
|
|
metadata will be consulted on open, create, remove, and wstat
|
|
to see if the requester has the appropriate permissions.
|
|
If not, an error will be sent back without consulting a service function.
|
|
.IP
|
|
If not using file trees or the user has the appropriate permissions,
|
|
.I open
|
|
is called with
|
|
.IB r -> ofcall . qid
|
|
already initialized to the one stored in the
|
|
.B Fid
|
|
structure (that is, the one returned in the previous walk).
|
|
If the qid changes, both should be updated.
|
|
.TP
|
|
.I Create
|
|
The
|
|
.I create
|
|
function must fill in
|
|
both
|
|
.IB r -> fid -> qid
|
|
and
|
|
.IB r -> ofcall . qid
|
|
on success.
|
|
When using file trees,
|
|
.I create
|
|
should allocate a new
|
|
.B File
|
|
with
|
|
.IR createfile ;
|
|
note that
|
|
.I createfile
|
|
may return nil (because, say, the file already exists).
|
|
If the
|
|
.I create
|
|
function is nil,
|
|
.I srv
|
|
behaves as though it were a function that always responded
|
|
with the error ``create prohibited''.
|
|
.TP
|
|
.I Remove
|
|
.I Remove
|
|
should mark the file as removed, whether
|
|
by calling
|
|
.I removefile
|
|
when using file trees, or by updating an internal data structure.
|
|
In general it is not a good idea to clean up the
|
|
.I aux
|
|
information associated with the corresponding
|
|
.B File
|
|
at this time, to avoid memory errors if other
|
|
fids have references to that file.
|
|
Instead, it is suggested that
|
|
.I remove
|
|
simply mark the file as removed (so that further
|
|
operations on it know to fail) and wait until the
|
|
file tree's destroy function is called to reclaim the
|
|
.I aux
|
|
pointer.
|
|
If not using file trees, it is prudent to take the
|
|
analogous measures.
|
|
If
|
|
.I remove
|
|
is not provided, all remove requests will draw
|
|
``remove prohibited'' errors.
|
|
.TP
|
|
.I Read
|
|
The
|
|
.I read
|
|
function must be provided; it fills
|
|
.IB r -> ofcall . data
|
|
with at most
|
|
.IB r -> ifcall . count
|
|
bytes of data from offset
|
|
.IB r -> ifcall . offset
|
|
of the file.
|
|
It also sets
|
|
.IB r -> ofcall . count
|
|
to the number of bytes being returned.
|
|
If using file trees,
|
|
.I srv
|
|
will handle reads of directories internally, only
|
|
calling
|
|
.I read
|
|
for requests on files.
|
|
.I Readstr
|
|
and
|
|
.I readbuf
|
|
are useful for satisfying read requests on a string or buffer.
|
|
Consulting the request in
|
|
.IB r -> ifcall \fR,
|
|
they fill
|
|
.IB r -> ofcall . data
|
|
and set
|
|
.IB r -> ofcall . count \fR;
|
|
they do not call
|
|
.IB respond .
|
|
Similarly,
|
|
.I dirread9p
|
|
can be used to handle directory reads in servers
|
|
not using file trees.
|
|
The passed
|
|
.I gen
|
|
function will be called as necessary to
|
|
fill
|
|
.I dir
|
|
with information for the
|
|
.IR n th
|
|
entry in the directory.
|
|
The string pointers placed in
|
|
.I dir
|
|
should be fresh copies
|
|
made with
|
|
.IR estrdup9p ;
|
|
they will be freed by
|
|
.I dirread9p
|
|
after each successful call to
|
|
.IR gen .
|
|
.I Gen
|
|
should return zero if it successfully filled
|
|
.IR dir ,
|
|
minus one on end of directory.
|
|
.TP
|
|
.I Write
|
|
The
|
|
.I write
|
|
function is similar but need not be provided.
|
|
If it is not, all writes will draw
|
|
``write prohibited'' errors.
|
|
Otherwise,
|
|
.I write
|
|
should attempt to write the
|
|
.IB r -> ifcall . count
|
|
bytes of
|
|
.IB r -> ifcall . data
|
|
to offset
|
|
.IB r -> ifcall . offset
|
|
of the file, setting
|
|
.IB r -> ofcall . count
|
|
to the number of bytes actually written.
|
|
Most programs consider it an error to
|
|
write less than the requested amount.
|
|
.TP
|
|
.I Stat
|
|
.I Stat
|
|
should fill
|
|
.IB r -> d
|
|
with the stat information for
|
|
.IB r -> fid \fR.
|
|
If using file trees,
|
|
.IB r -> d
|
|
will have been initialized with the stat info from
|
|
the tree, and
|
|
.I stat
|
|
itself may be nil.
|
|
.TP
|
|
.I Wstat
|
|
The
|
|
.I wstat
|
|
function consults
|
|
.IB r -> d
|
|
in changing the metadata for
|
|
.IB r -> fid
|
|
as described in
|
|
.IR stat (5).
|
|
When using file trees,
|
|
.I srv
|
|
will take care to check that the request satisfies
|
|
the permissions outlined in
|
|
.IR stat (5).
|
|
Otherwise
|
|
.I wstat
|
|
should take care to enforce permissions
|
|
where appropriate.
|
|
.TP
|
|
.I Flush
|
|
Servers that always call
|
|
.I respond
|
|
before returning from the service functions
|
|
need not provide a
|
|
.I flush
|
|
implementation:
|
|
.I flush
|
|
is only necessary in programs
|
|
that arrange for
|
|
.I respond
|
|
to be called asynchronously.
|
|
.I Flush
|
|
should cause the request
|
|
.IB r -> oldreq
|
|
to be cancelled or hurried along.
|
|
If
|
|
.I oldreq
|
|
is cancelled, this should be signalled by calling
|
|
.I respond
|
|
on
|
|
.I oldreq
|
|
with error string
|
|
.RB ` interrupted '.
|
|
.I Flush
|
|
must respond to
|
|
.I r
|
|
with a nil error string.
|
|
.I Flush
|
|
may respond to
|
|
.I r
|
|
before forcing a response to
|
|
.IB r -> oldreq \fR.
|
|
In this case, the library will delay sending
|
|
the
|
|
.I Rflush
|
|
message until the response to
|
|
.IB r -> oldreq
|
|
has been sent.
|
|
.PD
|
|
.PP
|
|
.IR Destroyfid ,
|
|
.IR destroyreq ,
|
|
.IR start ,
|
|
and
|
|
.I end
|
|
are auxiliary functions, not called in direct response to 9P requests.
|
|
.TP
|
|
.I Destroyfid
|
|
When a
|
|
.BR Fid 's
|
|
reference count drops to zero
|
|
.RI ( i.e.,
|
|
it has been clunked and there are no outstanding
|
|
requests referring to it),
|
|
.I destroyfid
|
|
is called to allow the program to dispose
|
|
of the
|
|
.IB fid -> aux
|
|
pointer.
|
|
.TP
|
|
.I Destroyreq
|
|
Similarly, when a
|
|
.BR Req 's
|
|
reference count drops to zero
|
|
.RI ( i.e. ,
|
|
it has been handled via
|
|
.I respond
|
|
and other outstanding pointers to it have been closed),
|
|
.I destroyreq
|
|
is called to allow the program to dispose of the
|
|
.IB r -> aux
|
|
pointer.
|
|
.TP
|
|
.I Start
|
|
This gets called (from the forked service process)
|
|
prior entering the 9P service loop.
|
|
.TP
|
|
.I End
|
|
Once the 9P service loop has finished
|
|
(end of file been reached on the service pipe
|
|
or a bad message has been read),
|
|
.I end
|
|
is called (if provided) to allow any final cleanup.
|
|
For example, it was used by the Palm Pilot synchronization
|
|
file system (never finished) to gracefully terminate the serial conversation once
|
|
the file system had been unmounted.
|
|
After calling
|
|
.IR end ,
|
|
the service loop (which runs in a separate process
|
|
from its caller) terminates using
|
|
.I _exits
|
|
(see
|
|
.IR exits (2)).
|
|
.PD
|
|
.PP
|
|
If the
|
|
.B chatty9p
|
|
flag is at least one,
|
|
a transcript of the 9P session is printed
|
|
on standard error.
|
|
If the
|
|
.B chatty9p
|
|
flag is greater than one,
|
|
additional unspecified debugging output is generated.
|
|
By convention, servers written using this library
|
|
accept the
|
|
.B -D
|
|
option to increment
|
|
.BR chatty9p .
|
|
.SH EXAMPLES
|
|
.IR Archfs (4),
|
|
.IR cdfs (4),
|
|
.IR nntpfs (4),
|
|
.IR snap (4),
|
|
and
|
|
.B /sys/src/lib9p/ramfs.c
|
|
are good examples of simple single-threaded file servers.
|
|
.PP
|
|
In general, the
|
|
.B File
|
|
interface is appropriate for maintaining arbitrary file trees (as in
|
|
.IR ramfs ).
|
|
The
|
|
.B File
|
|
interface is best avoided when the
|
|
tree structure is easily generated as necessary;
|
|
this is true when the tree is highly structured (as in
|
|
.I cdfs
|
|
and
|
|
.IR nntpfs )
|
|
or is maintained elsewhere.
|
|
.SH SOURCE
|
|
.B /sys/src/lib9p
|
|
.SH SEE ALSO
|
|
.IR 9pfid (2),
|
|
.IR 9pfile (2),
|
|
.IR 9pqueue (2),
|
|
.IR srv (3),
|
|
.IR shr (3),
|
|
.IR intro (5)
|
|
.SH BUGS
|
|
The switch to 9P2000 was taken as an opportunity to tidy
|
|
much of the interface; we promise to avoid such gratuitous change
|
|
in the future.
|