fossil: import from plan 9
R=rsc https://codereview.appspot.com/7988047
This commit is contained in:
parent
fea86f0639
commit
6f4d00ee45
514
man/man4/fossil.4
Normal file
514
man/man4/fossil.4
Normal file
@ -0,0 +1,514 @@
|
||||
.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.
|
||||
1207
man/man8/fossilcons.8
Normal file
1207
man/man8/fossilcons.8
Normal file
File diff suppressed because it is too large
Load Diff
258
src/cmd/fossil/9.h
Normal file
258
src/cmd/fossil/9.h
Normal file
@ -0,0 +1,258 @@
|
||||
#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;
|
||||
175
src/cmd/fossil/9auth.c
Normal file
175
src/cmd/fossil/9auth.c
Normal file
@ -0,0 +1,175 @@
|
||||
#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;
|
||||
}
|
||||
132
src/cmd/fossil/9dir.c
Normal file
132
src/cmd/fossil/9dir.c
Normal file
@ -0,0 +1,132 @@
|
||||
#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)
|
||||
break;
|
||||
db->valid = 0;
|
||||
deCleanup(&db->de);
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
126
src/cmd/fossil/9excl.c
Normal file
126
src/cmd/fossil/9excl.c
Normal file
@ -0,0 +1,126 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
static struct {
|
||||
VtLock* lock;
|
||||
|
||||
Excl* head;
|
||||
Excl* tail;
|
||||
} ebox;
|
||||
|
||||
struct Excl {
|
||||
Fsys* fsys;
|
||||
uvlong path;
|
||||
ulong time;
|
||||
|
||||
Excl* next;
|
||||
Excl* prev;
|
||||
};
|
||||
|
||||
enum {
|
||||
LifeTime = (5*60),
|
||||
};
|
||||
|
||||
int
|
||||
exclAlloc(Fid* fid)
|
||||
{
|
||||
ulong t;
|
||||
Excl *excl;
|
||||
|
||||
assert(fid->excl == nil);
|
||||
|
||||
t = time(0L);
|
||||
vtLock(ebox.lock);
|
||||
for(excl = ebox.head; excl != nil; excl = excl->next){
|
||||
if(excl->fsys != fid->fsys || excl->path != fid->qid.path)
|
||||
continue;
|
||||
/*
|
||||
* Found it.
|
||||
* Now, check if it's timed out.
|
||||
* If not, return error, it's locked.
|
||||
* If it has timed out, zap the old
|
||||
* one and continue on to allocate a
|
||||
* a new one.
|
||||
*/
|
||||
if(excl->time >= t){
|
||||
vtUnlock(ebox.lock);
|
||||
vtSetError("exclusive lock");
|
||||
return 0;
|
||||
}
|
||||
excl->fsys = nil;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not found or timed-out.
|
||||
* Alloc a new one and initialise.
|
||||
*/
|
||||
excl = vtMemAllocZ(sizeof(Excl));
|
||||
excl->fsys = fid->fsys;
|
||||
excl->path = fid->qid.path;
|
||||
excl->time = t+LifeTime;
|
||||
if(ebox.tail != nil){
|
||||
excl->prev = ebox.tail;
|
||||
ebox.tail->next = excl;
|
||||
}
|
||||
else{
|
||||
ebox.head = excl;
|
||||
excl->prev = nil;
|
||||
}
|
||||
ebox.tail = excl;
|
||||
excl->next = nil;
|
||||
vtUnlock(ebox.lock);
|
||||
|
||||
fid->excl = excl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
exclUpdate(Fid* fid)
|
||||
{
|
||||
ulong t;
|
||||
Excl *excl;
|
||||
|
||||
excl = fid->excl;
|
||||
|
||||
t = time(0L);
|
||||
vtLock(ebox.lock);
|
||||
if(excl->time < t || excl->fsys != fid->fsys){
|
||||
vtUnlock(ebox.lock);
|
||||
vtSetError("exclusive lock broken");
|
||||
return 0;
|
||||
}
|
||||
excl->time = t+LifeTime;
|
||||
vtUnlock(ebox.lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
exclFree(Fid* fid)
|
||||
{
|
||||
Excl *excl;
|
||||
|
||||
if((excl = fid->excl) == nil)
|
||||
return;
|
||||
fid->excl = nil;
|
||||
|
||||
vtLock(ebox.lock);
|
||||
if(excl->prev != nil)
|
||||
excl->prev->next = excl->next;
|
||||
else
|
||||
ebox.head = excl->next;
|
||||
if(excl->next != nil)
|
||||
excl->next->prev = excl->prev;
|
||||
else
|
||||
ebox.tail = excl->prev;
|
||||
vtUnlock(ebox.lock);
|
||||
|
||||
vtMemFree(excl);
|
||||
}
|
||||
|
||||
void
|
||||
exclInit(void)
|
||||
{
|
||||
ebox.lock = vtLockAlloc();
|
||||
}
|
||||
304
src/cmd/fossil/9fid.c
Normal file
304
src/cmd/fossil/9fid.c
Normal file
@ -0,0 +1,304 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
static struct {
|
||||
VtLock* lock;
|
||||
|
||||
Fid* free;
|
||||
int nfree;
|
||||
int inuse;
|
||||
} fbox;
|
||||
|
||||
static void
|
||||
fidLock(Fid* fid, int flags)
|
||||
{
|
||||
if(flags & FidFWlock){
|
||||
vtLock(fid->lock);
|
||||
fid->flags = flags;
|
||||
}
|
||||
else
|
||||
vtRLock(fid->lock);
|
||||
|
||||
/*
|
||||
* Callers of file* routines are expected to lock fsys->fs->elk
|
||||
* before making any calls in order to make sure the epoch doesn't
|
||||
* change underfoot. With the exception of Tversion and Tattach,
|
||||
* that implies all 9P functions need to lock on entry and unlock
|
||||
* on exit. Fortunately, the general case is the 9P functions do
|
||||
* fidGet on entry and fidPut on exit, so this is a convenient place
|
||||
* to do the locking.
|
||||
* No fsys->fs->elk lock is required if the fid is being created
|
||||
* (Tauth, Tattach and Twalk). FidFCreate is always accompanied by
|
||||
* FidFWlock so the setting and testing of FidFCreate here and in
|
||||
* fidUnlock below is always done under fid->lock.
|
||||
* A side effect is that fidFree is called with the fid locked, and
|
||||
* must call fidUnlock only after it has disposed of any File
|
||||
* resources still held.
|
||||
*/
|
||||
if(!(flags & FidFCreate))
|
||||
fsysFsRlock(fid->fsys);
|
||||
}
|
||||
|
||||
static void
|
||||
fidUnlock(Fid* fid)
|
||||
{
|
||||
if(!(fid->flags & FidFCreate))
|
||||
fsysFsRUnlock(fid->fsys);
|
||||
if(fid->flags & FidFWlock){
|
||||
fid->flags = 0;
|
||||
vtUnlock(fid->lock);
|
||||
return;
|
||||
}
|
||||
vtRUnlock(fid->lock);
|
||||
}
|
||||
|
||||
static Fid*
|
||||
fidAlloc(void)
|
||||
{
|
||||
Fid *fid;
|
||||
|
||||
vtLock(fbox.lock);
|
||||
if(fbox.nfree > 0){
|
||||
fid = fbox.free;
|
||||
fbox.free = fid->hash;
|
||||
fbox.nfree--;
|
||||
}
|
||||
else{
|
||||
fid = vtMemAllocZ(sizeof(Fid));
|
||||
fid->lock = vtLockAlloc();
|
||||
fid->alock = vtLockAlloc();
|
||||
}
|
||||
fbox.inuse++;
|
||||
vtUnlock(fbox.lock);
|
||||
|
||||
fid->con = nil;
|
||||
fid->fidno = NOFID;
|
||||
fid->ref = 0;
|
||||
fid->flags = 0;
|
||||
fid->open = FidOCreate;
|
||||
assert(fid->fsys == nil);
|
||||
assert(fid->file == nil);
|
||||
fid->qid = (Qid){0, 0, 0};
|
||||
assert(fid->uid == nil);
|
||||
assert(fid->uname == nil);
|
||||
assert(fid->db == nil);
|
||||
assert(fid->excl == nil);
|
||||
assert(fid->rpc == nil);
|
||||
assert(fid->cuname == nil);
|
||||
fid->hash = fid->next = fid->prev = nil;
|
||||
|
||||
return fid;
|
||||
}
|
||||
|
||||
static void
|
||||
fidFree(Fid* fid)
|
||||
{
|
||||
if(fid->file != nil){
|
||||
fileDecRef(fid->file);
|
||||
fid->file = nil;
|
||||
}
|
||||
if(fid->db != nil){
|
||||
dirBufFree(fid->db);
|
||||
fid->db = nil;
|
||||
}
|
||||
fidUnlock(fid);
|
||||
|
||||
if(fid->uid != nil){
|
||||
vtMemFree(fid->uid);
|
||||
fid->uid = nil;
|
||||
}
|
||||
if(fid->uname != nil){
|
||||
vtMemFree(fid->uname);
|
||||
fid->uname = nil;
|
||||
}
|
||||
if(fid->excl != nil)
|
||||
exclFree(fid);
|
||||
if(fid->rpc != nil){
|
||||
close(fid->rpc->afd);
|
||||
auth_freerpc(fid->rpc);
|
||||
fid->rpc = nil;
|
||||
}
|
||||
if(fid->fsys != nil){
|
||||
fsysPut(fid->fsys);
|
||||
fid->fsys = nil;
|
||||
}
|
||||
if(fid->cuname != nil){
|
||||
vtMemFree(fid->cuname);
|
||||
fid->cuname = nil;
|
||||
}
|
||||
|
||||
vtLock(fbox.lock);
|
||||
fbox.inuse--;
|
||||
if(fbox.nfree < 10){
|
||||
fid->hash = fbox.free;
|
||||
fbox.free = fid;
|
||||
fbox.nfree++;
|
||||
}
|
||||
else{
|
||||
vtLockFree(fid->alock);
|
||||
vtLockFree(fid->lock);
|
||||
vtMemFree(fid);
|
||||
}
|
||||
vtUnlock(fbox.lock);
|
||||
}
|
||||
|
||||
static void
|
||||
fidUnHash(Fid* fid)
|
||||
{
|
||||
Fid *fp, **hash;
|
||||
|
||||
assert(fid->ref == 0);
|
||||
|
||||
hash = &fid->con->fidhash[fid->fidno % NFidHash];
|
||||
for(fp = *hash; fp != nil; fp = fp->hash){
|
||||
if(fp == fid){
|
||||
*hash = fp->hash;
|
||||
break;
|
||||
}
|
||||
hash = &fp->hash;
|
||||
}
|
||||
assert(fp == fid);
|
||||
|
||||
if(fid->prev != nil)
|
||||
fid->prev->next = fid->next;
|
||||
else
|
||||
fid->con->fhead = fid->next;
|
||||
if(fid->next != nil)
|
||||
fid->next->prev = fid->prev;
|
||||
else
|
||||
fid->con->ftail = fid->prev;
|
||||
fid->prev = fid->next = nil;
|
||||
|
||||
fid->con->nfid--;
|
||||
}
|
||||
|
||||
Fid*
|
||||
fidGet(Con* con, u32int fidno, int flags)
|
||||
{
|
||||
Fid *fid, **hash;
|
||||
|
||||
if(fidno == NOFID)
|
||||
return nil;
|
||||
|
||||
hash = &con->fidhash[fidno % NFidHash];
|
||||
vtLock(con->fidlock);
|
||||
for(fid = *hash; fid != nil; fid = fid->hash){
|
||||
if(fid->fidno != fidno)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Already in use is an error
|
||||
* when called from attach, clone or walk.
|
||||
*/
|
||||
if(flags & FidFCreate){
|
||||
vtUnlock(con->fidlock);
|
||||
vtSetError("%s: fid 0x%ud in use", argv0, fidno);
|
||||
return nil;
|
||||
}
|
||||
fid->ref++;
|
||||
vtUnlock(con->fidlock);
|
||||
|
||||
fidLock(fid, flags);
|
||||
if((fid->open & FidOCreate) || fid->fidno == NOFID){
|
||||
fidPut(fid);
|
||||
vtSetError("%s: fid invalid", argv0);
|
||||
return nil;
|
||||
}
|
||||
return fid;
|
||||
}
|
||||
|
||||
if((flags & FidFCreate) && (fid = fidAlloc()) != nil){
|
||||
assert(flags & FidFWlock);
|
||||
fid->con = con;
|
||||
fid->fidno = fidno;
|
||||
fid->ref = 1;
|
||||
|
||||
fid->hash = *hash;
|
||||
*hash = fid;
|
||||
if(con->ftail != nil){
|
||||
fid->prev = con->ftail;
|
||||
con->ftail->next = fid;
|
||||
}
|
||||
else{
|
||||
con->fhead = fid;
|
||||
fid->prev = nil;
|
||||
}
|
||||
con->ftail = fid;
|
||||
fid->next = nil;
|
||||
|
||||
con->nfid++;
|
||||
vtUnlock(con->fidlock);
|
||||
|
||||
/*
|
||||
* The FidOCreate flag is used to prevent any
|
||||
* accidental access to the Fid between unlocking the
|
||||
* hash and acquiring the Fid lock for return.
|
||||
*/
|
||||
fidLock(fid, flags);
|
||||
fid->open &= ~FidOCreate;
|
||||
return fid;
|
||||
}
|
||||
vtUnlock(con->fidlock);
|
||||
|
||||
vtSetError("%s: fid not found", argv0);
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
fidPut(Fid* fid)
|
||||
{
|
||||
vtLock(fid->con->fidlock);
|
||||
assert(fid->ref > 0);
|
||||
fid->ref--;
|
||||
vtUnlock(fid->con->fidlock);
|
||||
|
||||
if(fid->ref == 0 && fid->fidno == NOFID){
|
||||
fidFree(fid);
|
||||
return;
|
||||
}
|
||||
fidUnlock(fid);
|
||||
}
|
||||
|
||||
void
|
||||
fidClunk(Fid* fid)
|
||||
{
|
||||
assert(fid->flags & FidFWlock);
|
||||
|
||||
vtLock(fid->con->fidlock);
|
||||
assert(fid->ref > 0);
|
||||
fid->ref--;
|
||||
fidUnHash(fid);
|
||||
fid->fidno = NOFID;
|
||||
vtUnlock(fid->con->fidlock);
|
||||
|
||||
if(fid->ref > 0){
|
||||
/* not reached - fidUnHash requires ref == 0 */
|
||||
fidUnlock(fid);
|
||||
return;
|
||||
}
|
||||
fidFree(fid);
|
||||
}
|
||||
|
||||
void
|
||||
fidClunkAll(Con* con)
|
||||
{
|
||||
Fid *fid;
|
||||
u32int fidno;
|
||||
|
||||
vtLock(con->fidlock);
|
||||
while(con->fhead != nil){
|
||||
fidno = con->fhead->fidno;
|
||||
vtUnlock(con->fidlock);
|
||||
if((fid = fidGet(con, fidno, FidFWlock)) != nil)
|
||||
fidClunk(fid);
|
||||
vtLock(con->fidlock);
|
||||
}
|
||||
vtUnlock(con->fidlock);
|
||||
}
|
||||
|
||||
void
|
||||
fidInit(void)
|
||||
{
|
||||
fbox.lock = vtLockAlloc();
|
||||
}
|
||||
1896
src/cmd/fossil/9fsys.c
Normal file
1896
src/cmd/fossil/9fsys.c
Normal file
File diff suppressed because it is too large
Load Diff
184
src/cmd/fossil/9lstn.c
Normal file
184
src/cmd/fossil/9lstn.c
Normal file
@ -0,0 +1,184 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
typedef struct Lstn Lstn;
|
||||
struct Lstn {
|
||||
int afd;
|
||||
int flags;
|
||||
char* address;
|
||||
char dir[NETPATHLEN];
|
||||
|
||||
Lstn* next;
|
||||
Lstn* prev;
|
||||
};
|
||||
|
||||
static struct {
|
||||
VtLock* lock;
|
||||
|
||||
Lstn* head;
|
||||
Lstn* tail;
|
||||
} lbox;
|
||||
|
||||
static void
|
||||
lstnFree(Lstn* lstn)
|
||||
{
|
||||
vtLock(lbox.lock);
|
||||
if(lstn->prev != nil)
|
||||
lstn->prev->next = lstn->next;
|
||||
else
|
||||
lbox.head = lstn->next;
|
||||
if(lstn->next != nil)
|
||||
lstn->next->prev = lstn->prev;
|
||||
else
|
||||
lbox.tail = lstn->prev;
|
||||
vtUnlock(lbox.lock);
|
||||
|
||||
if(lstn->afd != -1)
|
||||
close(lstn->afd);
|
||||
vtMemFree(lstn->address);
|
||||
vtMemFree(lstn);
|
||||
}
|
||||
|
||||
static void
|
||||
lstnListen(void* a)
|
||||
{
|
||||
Lstn *lstn;
|
||||
int dfd, lfd;
|
||||
char newdir[NETPATHLEN];
|
||||
|
||||
vtThreadSetName("listen");
|
||||
|
||||
lstn = a;
|
||||
for(;;){
|
||||
if((lfd = listen(lstn->dir, newdir)) < 0){
|
||||
fprint(2, "listen: listen '%s': %r", lstn->dir);
|
||||
break;
|
||||
}
|
||||
if((dfd = accept(lfd, newdir)) >= 0)
|
||||
conAlloc(dfd, newdir, lstn->flags);
|
||||
else
|
||||
fprint(2, "listen: accept %s: %r\n", newdir);
|
||||
close(lfd);
|
||||
}
|
||||
lstnFree(lstn);
|
||||
}
|
||||
|
||||
static Lstn*
|
||||
lstnAlloc(char* address, int flags)
|
||||
{
|
||||
int afd;
|
||||
Lstn *lstn;
|
||||
char dir[NETPATHLEN];
|
||||
|
||||
vtLock(lbox.lock);
|
||||
for(lstn = lbox.head; lstn != nil; lstn = lstn->next){
|
||||
if(strcmp(lstn->address, address) != 0)
|
||||
continue;
|
||||
vtSetError("listen: already serving '%s'", address);
|
||||
vtUnlock(lbox.lock);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if((afd = announce(address, dir)) < 0){
|
||||
vtSetError("listen: announce '%s': %r", address);
|
||||
vtUnlock(lbox.lock);
|
||||
return nil;
|
||||
}
|
||||
|
||||
lstn = vtMemAllocZ(sizeof(Lstn));
|
||||
lstn->afd = afd;
|
||||
lstn->address = vtStrDup(address);
|
||||
lstn->flags = flags;
|
||||
memmove(lstn->dir, dir, NETPATHLEN);
|
||||
|
||||
if(lbox.tail != nil){
|
||||
lstn->prev = lbox.tail;
|
||||
lbox.tail->next = lstn;
|
||||
}
|
||||
else{
|
||||
lbox.head = lstn;
|
||||
lstn->prev = nil;
|
||||
}
|
||||
lbox.tail = lstn;
|
||||
vtUnlock(lbox.lock);
|
||||
|
||||
if(vtThread(lstnListen, lstn) < 0){
|
||||
vtSetError("listen: thread '%s': %r", lstn->address);
|
||||
lstnFree(lstn);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return lstn;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdLstn(int argc, char* argv[])
|
||||
{
|
||||
int dflag, flags;
|
||||
Lstn *lstn;
|
||||
char *usage = "usage: listen [-dIN] [address]";
|
||||
|
||||
dflag = 0;
|
||||
flags = 0;
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 'd':
|
||||
dflag = 1;
|
||||
break;
|
||||
case 'I':
|
||||
flags |= ConIPCheck;
|
||||
break;
|
||||
case 'N':
|
||||
flags |= ConNoneAllow;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
switch(argc){
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 0:
|
||||
vtRLock(lbox.lock);
|
||||
for(lstn = lbox.head; lstn != nil; lstn = lstn->next)
|
||||
consPrint("\t%s\t%s\n", lstn->address, lstn->dir);
|
||||
vtRUnlock(lbox.lock);
|
||||
break;
|
||||
case 1:
|
||||
if(!dflag){
|
||||
if(lstnAlloc(argv[0], flags) == nil)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
vtLock(lbox.lock);
|
||||
for(lstn = lbox.head; lstn != nil; lstn = lstn->next){
|
||||
if(strcmp(lstn->address, argv[0]) != 0)
|
||||
continue;
|
||||
if(lstn->afd != -1){
|
||||
close(lstn->afd);
|
||||
lstn->afd = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
vtUnlock(lbox.lock);
|
||||
|
||||
if(lstn == nil){
|
||||
vtSetError("listen: '%s' not found", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lstnInit(void)
|
||||
{
|
||||
lbox.lock = vtLockAlloc();
|
||||
|
||||
cliAddCmd("listen", cmdLstn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
1181
src/cmd/fossil/9p.c
Normal file
1181
src/cmd/fossil/9p.c
Normal file
File diff suppressed because it is too large
Load Diff
109
src/cmd/fossil/9ping.c
Normal file
109
src/cmd/fossil/9ping.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
typedef uvlong u64int;
|
||||
|
||||
#define TWID64 ((u64int)~(u64int)0)
|
||||
|
||||
|
||||
u64int
|
||||
unittoull(char *s)
|
||||
{
|
||||
char *es;
|
||||
u64int n;
|
||||
|
||||
if(s == nil)
|
||||
return TWID64;
|
||||
n = strtoul(s, &es, 0);
|
||||
if(*es == 'k' || *es == 'K'){
|
||||
n *= 1024;
|
||||
es++;
|
||||
}else if(*es == 'm' || *es == 'M'){
|
||||
n *= 1024*1024;
|
||||
es++;
|
||||
}else if(*es == 'g' || *es == 'G'){
|
||||
n *= 1024*1024*1024;
|
||||
es++;
|
||||
}
|
||||
if(*es != '\0')
|
||||
return TWID64;
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd, i;
|
||||
int n = 1000, m;
|
||||
int s = 1;
|
||||
double *t, t0, t1;
|
||||
uchar *buf;
|
||||
double a, d, max, min;
|
||||
|
||||
m = OREAD;
|
||||
ARGBEGIN{
|
||||
case 'n':
|
||||
n = atoi(ARGF());
|
||||
break;
|
||||
case 's':
|
||||
s = unittoull(ARGF());
|
||||
if(s < 1 || s > 1024*1024)
|
||||
sysfatal("bad size");
|
||||
break;
|
||||
case 'r':
|
||||
m = OREAD;
|
||||
break;
|
||||
case 'w':
|
||||
m = OWRITE;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
fd = 0;
|
||||
if(argc == 1){
|
||||
fd = open(argv[0], m);
|
||||
if(fd < 0)
|
||||
sysfatal("could not open file: %s: %r", argv[0]);
|
||||
}
|
||||
|
||||
buf = malloc(s);
|
||||
t = malloc(n*sizeof(double));
|
||||
|
||||
t0 = nsec();
|
||||
for(i=0; i<n; i++){
|
||||
if(m == OREAD){
|
||||
if(pread(fd, buf, s, 0) < s)
|
||||
sysfatal("bad read: %r");
|
||||
}else{
|
||||
if(pwrite(fd, buf, s, 0) < s)
|
||||
sysfatal("bad write: %r");
|
||||
}
|
||||
t1 = nsec();
|
||||
t[i] = (t1 - t0)*1e-3;
|
||||
t0 = t1;
|
||||
}
|
||||
|
||||
a = 0.;
|
||||
d = 0.;
|
||||
max = 0.;
|
||||
min = 1e12;
|
||||
|
||||
for(i=0; i<n; i++){
|
||||
a += t[i];
|
||||
if(max < t[i])
|
||||
max = t[i];
|
||||
if(min > t[i])
|
||||
min = t[i];
|
||||
}
|
||||
|
||||
a /= n;
|
||||
|
||||
for(i=0; i<n; i++)
|
||||
d += (a - t[i]) * (a - t[i]);
|
||||
d /= n;
|
||||
d = sqrt(d);
|
||||
|
||||
print("avg = %.0fµs min = %.0fµs max = %.0fµs dev = %.0fµs\n", a, min, max, d);
|
||||
|
||||
exits(0);
|
||||
}
|
||||
|
||||
825
src/cmd/fossil/9proc.c
Normal file
825
src/cmd/fossil/9proc.c
Normal file
@ -0,0 +1,825 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
enum {
|
||||
NConInit = 128,
|
||||
NMsgInit = 384,
|
||||
NMsgProcInit = 64,
|
||||
NMsizeInit = 8192+IOHDRSZ,
|
||||
};
|
||||
|
||||
static struct {
|
||||
VtLock* alock; /* alloc */
|
||||
Msg* ahead;
|
||||
VtRendez* arendez;
|
||||
|
||||
int maxmsg;
|
||||
int nmsg;
|
||||
int nmsgstarve;
|
||||
|
||||
VtLock* rlock; /* read */
|
||||
Msg* rhead;
|
||||
Msg* rtail;
|
||||
VtRendez* rrendez;
|
||||
|
||||
int maxproc;
|
||||
int nproc;
|
||||
int nprocstarve;
|
||||
|
||||
u32int msize; /* immutable */
|
||||
} mbox;
|
||||
|
||||
static struct {
|
||||
VtLock* alock; /* alloc */
|
||||
Con* ahead;
|
||||
VtRendez* arendez;
|
||||
|
||||
VtLock* clock;
|
||||
Con* chead;
|
||||
Con* ctail;
|
||||
|
||||
int maxcon;
|
||||
int ncon;
|
||||
int nconstarve;
|
||||
|
||||
u32int msize;
|
||||
} cbox;
|
||||
|
||||
static void
|
||||
conFree(Con* con)
|
||||
{
|
||||
assert(con->version == nil);
|
||||
assert(con->mhead == nil);
|
||||
assert(con->whead == nil);
|
||||
assert(con->nfid == 0);
|
||||
assert(con->state == ConMoribund);
|
||||
|
||||
if(con->fd >= 0){
|
||||
close(con->fd);
|
||||
con->fd = -1;
|
||||
}
|
||||
con->state = ConDead;
|
||||
con->aok = 0;
|
||||
con->flags = 0;
|
||||
con->isconsole = 0;
|
||||
|
||||
vtLock(cbox.alock);
|
||||
if(con->cprev != nil)
|
||||
con->cprev->cnext = con->cnext;
|
||||
else
|
||||
cbox.chead = con->cnext;
|
||||
if(con->cnext != nil)
|
||||
con->cnext->cprev = con->cprev;
|
||||
else
|
||||
cbox.ctail = con->cprev;
|
||||
con->cprev = con->cnext = nil;
|
||||
|
||||
if(cbox.ncon > cbox.maxcon){
|
||||
if(con->name != nil)
|
||||
vtMemFree(con->name);
|
||||
vtLockFree(con->fidlock);
|
||||
vtMemFree(con->data);
|
||||
vtRendezFree(con->wrendez);
|
||||
vtLockFree(con->wlock);
|
||||
vtRendezFree(con->mrendez);
|
||||
vtLockFree(con->mlock);
|
||||
vtRendezFree(con->rendez);
|
||||
vtLockFree(con->lock);
|
||||
vtMemFree(con);
|
||||
cbox.ncon--;
|
||||
vtUnlock(cbox.alock);
|
||||
return;
|
||||
}
|
||||
con->anext = cbox.ahead;
|
||||
cbox.ahead = con;
|
||||
if(con->anext == nil)
|
||||
vtWakeup(cbox.arendez);
|
||||
vtUnlock(cbox.alock);
|
||||
}
|
||||
|
||||
static void
|
||||
msgFree(Msg* m)
|
||||
{
|
||||
assert(m->rwnext == nil);
|
||||
assert(m->flush == nil);
|
||||
|
||||
vtLock(mbox.alock);
|
||||
if(mbox.nmsg > mbox.maxmsg){
|
||||
vtMemFree(m->data);
|
||||
vtMemFree(m);
|
||||
mbox.nmsg--;
|
||||
vtUnlock(mbox.alock);
|
||||
return;
|
||||
}
|
||||
m->anext = mbox.ahead;
|
||||
mbox.ahead = m;
|
||||
if(m->anext == nil)
|
||||
vtWakeup(mbox.arendez);
|
||||
vtUnlock(mbox.alock);
|
||||
}
|
||||
|
||||
static Msg*
|
||||
msgAlloc(Con* con)
|
||||
{
|
||||
Msg *m;
|
||||
|
||||
vtLock(mbox.alock);
|
||||
while(mbox.ahead == nil){
|
||||
if(mbox.nmsg >= mbox.maxmsg){
|
||||
mbox.nmsgstarve++;
|
||||
vtSleep(mbox.arendez);
|
||||
continue;
|
||||
}
|
||||
m = vtMemAllocZ(sizeof(Msg));
|
||||
m->data = vtMemAlloc(mbox.msize);
|
||||
m->msize = mbox.msize;
|
||||
mbox.nmsg++;
|
||||
mbox.ahead = m;
|
||||
break;
|
||||
}
|
||||
m = mbox.ahead;
|
||||
mbox.ahead = m->anext;
|
||||
m->anext = nil;
|
||||
vtUnlock(mbox.alock);
|
||||
|
||||
m->con = con;
|
||||
m->state = MsgR;
|
||||
m->nowq = 0;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
static void
|
||||
msgMunlink(Msg* m)
|
||||
{
|
||||
Con *con;
|
||||
|
||||
con = m->con;
|
||||
|
||||
if(m->mprev != nil)
|
||||
m->mprev->mnext = m->mnext;
|
||||
else
|
||||
con->mhead = m->mnext;
|
||||
if(m->mnext != nil)
|
||||
m->mnext->mprev = m->mprev;
|
||||
else
|
||||
con->mtail = m->mprev;
|
||||
m->mprev = m->mnext = nil;
|
||||
}
|
||||
|
||||
void
|
||||
msgFlush(Msg* m)
|
||||
{
|
||||
Con *con;
|
||||
Msg *flush, *old;
|
||||
|
||||
con = m->con;
|
||||
|
||||
if(Dflag)
|
||||
fprint(2, "msgFlush %F\n", &m->t);
|
||||
|
||||
/*
|
||||
* If this Tflush has been flushed, nothing to do.
|
||||
* Look for the message to be flushed in the
|
||||
* queue of all messages still on this connection.
|
||||
* If it's not found must assume Elvis has already
|
||||
* left the building and reply normally.
|
||||
*/
|
||||
vtLock(con->mlock);
|
||||
if(m->state == MsgF){
|
||||
vtUnlock(con->mlock);
|
||||
return;
|
||||
}
|
||||
for(old = con->mhead; old != nil; old = old->mnext)
|
||||
if(old->t.tag == m->t.oldtag)
|
||||
break;
|
||||
if(old == nil){
|
||||
if(Dflag)
|
||||
fprint(2, "msgFlush: cannot find %d\n", m->t.oldtag);
|
||||
vtUnlock(con->mlock);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Dflag)
|
||||
fprint(2, "\tmsgFlush found %F\n", &old->t);
|
||||
|
||||
/*
|
||||
* Found it.
|
||||
* There are two cases where the old message can be
|
||||
* truly flushed and no reply to the original message given.
|
||||
* The first is when the old message is in MsgR state; no
|
||||
* processing has been done yet and it is still on the read
|
||||
* queue. The second is if old is a Tflush, which doesn't
|
||||
* affect the server state. In both cases, put the old
|
||||
* message into MsgF state and let MsgWrite toss it after
|
||||
* pulling it off the queue.
|
||||
*/
|
||||
if(old->state == MsgR || old->t.type == Tflush){
|
||||
old->state = MsgF;
|
||||
if(Dflag)
|
||||
fprint(2, "msgFlush: change %d from MsgR to MsgF\n",
|
||||
m->t.oldtag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Link this flush message and the old message
|
||||
* so multiple flushes can be coalesced (if there are
|
||||
* multiple Tflush messages for a particular pending
|
||||
* request, it is only necessary to respond to the last
|
||||
* one, so any previous can be removed) and to be
|
||||
* sure flushes wait for their corresponding old
|
||||
* message to go out first.
|
||||
* Waiting flush messages do not go on the write queue,
|
||||
* they are processed after the old message is dealt
|
||||
* with. There's no real need to protect the setting of
|
||||
* Msg.nowq, the only code to check it runs in this
|
||||
* process after this routine returns.
|
||||
*/
|
||||
if((flush = old->flush) != nil){
|
||||
if(Dflag)
|
||||
fprint(2, "msgFlush: remove %d from %d list\n",
|
||||
old->flush->t.tag, old->t.tag);
|
||||
m->flush = flush->flush;
|
||||
flush->flush = nil;
|
||||
msgMunlink(flush);
|
||||
msgFree(flush);
|
||||
}
|
||||
old->flush = m;
|
||||
m->nowq = 1;
|
||||
|
||||
if(Dflag)
|
||||
fprint(2, "msgFlush: add %d to %d queue\n",
|
||||
m->t.tag, old->t.tag);
|
||||
vtUnlock(con->mlock);
|
||||
}
|
||||
|
||||
static void
|
||||
msgProc(void*)
|
||||
{
|
||||
Msg *m;
|
||||
char *e;
|
||||
Con *con;
|
||||
|
||||
vtThreadSetName("msgProc");
|
||||
|
||||
for(;;){
|
||||
/*
|
||||
* If surplus to requirements, exit.
|
||||
* If not, wait for and pull a message off
|
||||
* the read queue.
|
||||
*/
|
||||
vtLock(mbox.rlock);
|
||||
if(mbox.nproc > mbox.maxproc){
|
||||
mbox.nproc--;
|
||||
vtUnlock(mbox.rlock);
|
||||
break;
|
||||
}
|
||||
while(mbox.rhead == nil)
|
||||
vtSleep(mbox.rrendez);
|
||||
m = mbox.rhead;
|
||||
mbox.rhead = m->rwnext;
|
||||
m->rwnext = nil;
|
||||
vtUnlock(mbox.rlock);
|
||||
|
||||
con = m->con;
|
||||
e = nil;
|
||||
|
||||
/*
|
||||
* If the message has been flushed before
|
||||
* any 9P processing has started, mark it so
|
||||
* none will be attempted.
|
||||
*/
|
||||
vtLock(con->mlock);
|
||||
if(m->state == MsgF)
|
||||
e = "flushed";
|
||||
else
|
||||
m->state = Msg9;
|
||||
vtUnlock(con->mlock);
|
||||
|
||||
if(e == nil){
|
||||
/*
|
||||
* explain this
|
||||
*/
|
||||
vtLock(con->lock);
|
||||
if(m->t.type == Tversion){
|
||||
con->version = m;
|
||||
con->state = ConDown;
|
||||
while(con->mhead != m)
|
||||
vtSleep(con->rendez);
|
||||
assert(con->state == ConDown);
|
||||
if(con->version == m){
|
||||
con->version = nil;
|
||||
con->state = ConInit;
|
||||
}
|
||||
else
|
||||
e = "Tversion aborted";
|
||||
}
|
||||
else if(con->state != ConUp)
|
||||
e = "connection not ready";
|
||||
vtUnlock(con->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispatch if not error already.
|
||||
*/
|
||||
m->r.tag = m->t.tag;
|
||||
if(e == nil && !(*rFcall[m->t.type])(m))
|
||||
e = vtGetError();
|
||||
if(e != nil){
|
||||
m->r.type = Rerror;
|
||||
m->r.ename = e;
|
||||
}
|
||||
else
|
||||
m->r.type = m->t.type+1;
|
||||
|
||||
/*
|
||||
* Put the message (with reply) on the
|
||||
* write queue and wakeup the write process.
|
||||
*/
|
||||
if(!m->nowq){
|
||||
vtLock(con->wlock);
|
||||
if(con->whead == nil)
|
||||
con->whead = m;
|
||||
else
|
||||
con->wtail->rwnext = m;
|
||||
con->wtail = m;
|
||||
vtWakeup(con->wrendez);
|
||||
vtUnlock(con->wlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
msgRead(void* v)
|
||||
{
|
||||
Msg *m;
|
||||
Con *con;
|
||||
int eof, fd, n;
|
||||
|
||||
vtThreadSetName("msgRead");
|
||||
|
||||
con = v;
|
||||
fd = con->fd;
|
||||
eof = 0;
|
||||
|
||||
while(!eof){
|
||||
m = msgAlloc(con);
|
||||
|
||||
while((n = read9pmsg(fd, m->data, con->msize)) == 0)
|
||||
;
|
||||
if(n < 0){
|
||||
m->t.type = Tversion;
|
||||
m->t.fid = NOFID;
|
||||
m->t.tag = NOTAG;
|
||||
m->t.msize = con->msize;
|
||||
m->t.version = "9PEoF";
|
||||
eof = 1;
|
||||
}
|
||||
else if(convM2S(m->data, n, &m->t) != n){
|
||||
if(Dflag)
|
||||
fprint(2, "msgRead: convM2S error: %s\n",
|
||||
con->name);
|
||||
msgFree(m);
|
||||
continue;
|
||||
}
|
||||
if(Dflag)
|
||||
fprint(2, "msgRead %p: t %F\n", con, &m->t);
|
||||
|
||||
vtLock(con->mlock);
|
||||
if(con->mtail != nil){
|
||||
m->mprev = con->mtail;
|
||||
con->mtail->mnext = m;
|
||||
}
|
||||
else{
|
||||
con->mhead = m;
|
||||
m->mprev = nil;
|
||||
}
|
||||
con->mtail = m;
|
||||
vtUnlock(con->mlock);
|
||||
|
||||
vtLock(mbox.rlock);
|
||||
if(mbox.rhead == nil){
|
||||
mbox.rhead = m;
|
||||
if(!vtWakeup(mbox.rrendez)){
|
||||
if(mbox.nproc < mbox.maxproc){
|
||||
if(vtThread(msgProc, nil) > 0)
|
||||
mbox.nproc++;
|
||||
}
|
||||
else
|
||||
mbox.nprocstarve++;
|
||||
}
|
||||
/*
|
||||
* don't need this surely?
|
||||
vtWakeup(mbox.rrendez);
|
||||
*/
|
||||
}
|
||||
else
|
||||
mbox.rtail->rwnext = m;
|
||||
mbox.rtail = m;
|
||||
vtUnlock(mbox.rlock);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
msgWrite(void* v)
|
||||
{
|
||||
Con *con;
|
||||
int eof, n;
|
||||
Msg *flush, *m;
|
||||
|
||||
vtThreadSetName("msgWrite");
|
||||
|
||||
con = v;
|
||||
if(vtThread(msgRead, con) < 0){
|
||||
conFree(con);
|
||||
return;
|
||||
}
|
||||
|
||||
for(;;){
|
||||
/*
|
||||
* Wait for and pull a message off the write queue.
|
||||
*/
|
||||
vtLock(con->wlock);
|
||||
while(con->whead == nil)
|
||||
vtSleep(con->wrendez);
|
||||
m = con->whead;
|
||||
con->whead = m->rwnext;
|
||||
m->rwnext = nil;
|
||||
assert(!m->nowq);
|
||||
vtUnlock(con->wlock);
|
||||
|
||||
eof = 0;
|
||||
|
||||
/*
|
||||
* Write each message (if it hasn't been flushed)
|
||||
* followed by any messages waiting for it to complete.
|
||||
*/
|
||||
vtLock(con->mlock);
|
||||
while(m != nil){
|
||||
msgMunlink(m);
|
||||
|
||||
if(Dflag)
|
||||
fprint(2, "msgWrite %d: r %F\n",
|
||||
m->state, &m->r);
|
||||
|
||||
if(m->state != MsgF){
|
||||
m->state = MsgW;
|
||||
vtUnlock(con->mlock);
|
||||
|
||||
n = convS2M(&m->r, con->data, con->msize);
|
||||
if(write(con->fd, con->data, n) != n)
|
||||
eof = 1;
|
||||
|
||||
vtLock(con->mlock);
|
||||
}
|
||||
|
||||
if((flush = m->flush) != nil){
|
||||
assert(flush->nowq);
|
||||
m->flush = nil;
|
||||
}
|
||||
msgFree(m);
|
||||
m = flush;
|
||||
}
|
||||
vtUnlock(con->mlock);
|
||||
|
||||
vtLock(con->lock);
|
||||
if(eof && con->fd >= 0){
|
||||
close(con->fd);
|
||||
con->fd = -1;
|
||||
}
|
||||
if(con->state == ConDown)
|
||||
vtWakeup(con->rendez);
|
||||
if(con->state == ConMoribund && con->mhead == nil){
|
||||
vtUnlock(con->lock);
|
||||
conFree(con);
|
||||
break;
|
||||
}
|
||||
vtUnlock(con->lock);
|
||||
}
|
||||
}
|
||||
|
||||
Con*
|
||||
conAlloc(int fd, char* name, int flags)
|
||||
{
|
||||
Con *con;
|
||||
char buf[128], *p;
|
||||
int rfd, n;
|
||||
|
||||
vtLock(cbox.alock);
|
||||
while(cbox.ahead == nil){
|
||||
if(cbox.ncon >= cbox.maxcon){
|
||||
cbox.nconstarve++;
|
||||
vtSleep(cbox.arendez);
|
||||
continue;
|
||||
}
|
||||
con = vtMemAllocZ(sizeof(Con));
|
||||
con->lock = vtLockAlloc();
|
||||
con->rendez = vtRendezAlloc(con->lock);
|
||||
con->data = vtMemAlloc(cbox.msize);
|
||||
con->msize = cbox.msize;
|
||||
con->alock = vtLockAlloc();
|
||||
con->mlock = vtLockAlloc();
|
||||
con->mrendez = vtRendezAlloc(con->mlock);
|
||||
con->wlock = vtLockAlloc();
|
||||
con->wrendez = vtRendezAlloc(con->wlock);
|
||||
con->fidlock = vtLockAlloc();
|
||||
|
||||
cbox.ncon++;
|
||||
cbox.ahead = con;
|
||||
break;
|
||||
}
|
||||
con = cbox.ahead;
|
||||
cbox.ahead = con->anext;
|
||||
con->anext = nil;
|
||||
|
||||
if(cbox.ctail != nil){
|
||||
con->cprev = cbox.ctail;
|
||||
cbox.ctail->cnext = con;
|
||||
}
|
||||
else{
|
||||
cbox.chead = con;
|
||||
con->cprev = nil;
|
||||
}
|
||||
cbox.ctail = con;
|
||||
|
||||
assert(con->mhead == nil);
|
||||
assert(con->whead == nil);
|
||||
assert(con->fhead == nil);
|
||||
assert(con->nfid == 0);
|
||||
|
||||
con->state = ConNew;
|
||||
con->fd = fd;
|
||||
if(con->name != nil){
|
||||
vtMemFree(con->name);
|
||||
con->name = nil;
|
||||
}
|
||||
if(name != nil)
|
||||
con->name = vtStrDup(name);
|
||||
else
|
||||
con->name = vtStrDup("unknown");
|
||||
con->remote[0] = 0;
|
||||
snprint(buf, sizeof buf, "%s/remote", con->name);
|
||||
if((rfd = open(buf, OREAD)) >= 0){
|
||||
n = read(rfd, buf, sizeof buf-1);
|
||||
close(rfd);
|
||||
if(n > 0){
|
||||
buf[n] = 0;
|
||||
if((p = strchr(buf, '\n')) != nil)
|
||||
*p = 0;
|
||||
strecpy(con->remote, con->remote+sizeof con->remote, buf);
|
||||
}
|
||||
}
|
||||
con->flags = flags;
|
||||
con->isconsole = 0;
|
||||
vtUnlock(cbox.alock);
|
||||
|
||||
if(vtThread(msgWrite, con) < 0){
|
||||
conFree(con);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdMsg(int argc, char* argv[])
|
||||
{
|
||||
char *p;
|
||||
char *usage = "usage: msg [-m nmsg] [-p nproc]";
|
||||
int maxmsg, nmsg, nmsgstarve, maxproc, nproc, nprocstarve;
|
||||
|
||||
maxmsg = maxproc = 0;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 'm':
|
||||
p = ARGF();
|
||||
if(p == nil)
|
||||
return cliError(usage);
|
||||
maxmsg = strtol(argv[0], &p, 0);
|
||||
if(maxmsg <= 0 || p == argv[0] || *p != '\0')
|
||||
return cliError(usage);
|
||||
break;
|
||||
case 'p':
|
||||
p = ARGF();
|
||||
if(p == nil)
|
||||
return cliError(usage);
|
||||
maxproc = strtol(argv[0], &p, 0);
|
||||
if(maxproc <= 0 || p == argv[0] || *p != '\0')
|
||||
return cliError(usage);
|
||||
break;
|
||||
}ARGEND
|
||||
if(argc)
|
||||
return cliError(usage);
|
||||
|
||||
vtLock(mbox.alock);
|
||||
if(maxmsg)
|
||||
mbox.maxmsg = maxmsg;
|
||||
maxmsg = mbox.maxmsg;
|
||||
nmsg = mbox.nmsg;
|
||||
nmsgstarve = mbox.nmsgstarve;
|
||||
vtUnlock(mbox.alock);
|
||||
|
||||
vtLock(mbox.rlock);
|
||||
if(maxproc)
|
||||
mbox.maxproc = maxproc;
|
||||
maxproc = mbox.maxproc;
|
||||
nproc = mbox.nproc;
|
||||
nprocstarve = mbox.nprocstarve;
|
||||
vtUnlock(mbox.rlock);
|
||||
|
||||
consPrint("\tmsg -m %d -p %d\n", maxmsg, maxproc);
|
||||
consPrint("\tnmsg %d nmsgstarve %d nproc %d nprocstarve %d\n",
|
||||
nmsg, nmsgstarve, nproc, nprocstarve);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
scmp(Fid *a, Fid *b)
|
||||
{
|
||||
if(a == 0)
|
||||
return 1;
|
||||
if(b == 0)
|
||||
return -1;
|
||||
return strcmp(a->uname, b->uname);
|
||||
}
|
||||
|
||||
static Fid*
|
||||
fidMerge(Fid *a, Fid *b)
|
||||
{
|
||||
Fid *s, **l;
|
||||
|
||||
l = &s;
|
||||
while(a || b){
|
||||
if(scmp(a, b) < 0){
|
||||
*l = a;
|
||||
l = &a->sort;
|
||||
a = a->sort;
|
||||
}else{
|
||||
*l = b;
|
||||
l = &b->sort;
|
||||
b = b->sort;
|
||||
}
|
||||
}
|
||||
*l = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static Fid*
|
||||
fidMergeSort(Fid *f)
|
||||
{
|
||||
int delay;
|
||||
Fid *a, *b;
|
||||
|
||||
if(f == nil)
|
||||
return nil;
|
||||
if(f->sort == nil)
|
||||
return f;
|
||||
|
||||
a = b = f;
|
||||
delay = 1;
|
||||
while(a && b){
|
||||
if(delay) /* easy way to handle 2-element list */
|
||||
delay = 0;
|
||||
else
|
||||
a = a->sort;
|
||||
if(b = b->sort)
|
||||
b = b->sort;
|
||||
}
|
||||
|
||||
b = a->sort;
|
||||
a->sort = nil;
|
||||
|
||||
a = fidMergeSort(f);
|
||||
b = fidMergeSort(b);
|
||||
|
||||
return fidMerge(a, b);
|
||||
}
|
||||
|
||||
static int
|
||||
cmdWho(int argc, char* argv[])
|
||||
{
|
||||
char *usage = "usage: who";
|
||||
int i, l1, l2, l;
|
||||
Con *con;
|
||||
Fid *fid, *last;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
}ARGEND
|
||||
|
||||
if(argc > 0)
|
||||
return cliError(usage);
|
||||
|
||||
vtRLock(cbox.clock);
|
||||
l1 = 0;
|
||||
l2 = 0;
|
||||
for(con=cbox.chead; con; con=con->cnext){
|
||||
if((l = strlen(con->name)) > l1)
|
||||
l1 = l;
|
||||
if((l = strlen(con->remote)) > l2)
|
||||
l2 = l;
|
||||
}
|
||||
for(con=cbox.chead; con; con=con->cnext){
|
||||
consPrint("\t%-*s %-*s", l1, con->name, l2, con->remote);
|
||||
vtLock(con->fidlock);
|
||||
last = nil;
|
||||
for(i=0; i<NFidHash; i++)
|
||||
for(fid=con->fidhash[i]; fid; fid=fid->hash)
|
||||
if(fid->fidno != NOFID && fid->uname){
|
||||
fid->sort = last;
|
||||
last = fid;
|
||||
}
|
||||
fid = fidMergeSort(last);
|
||||
last = nil;
|
||||
for(; fid; last=fid, fid=fid->sort)
|
||||
if(last==nil || strcmp(fid->uname, last->uname) != 0)
|
||||
consPrint(" %q", fid->uname);
|
||||
vtUnlock(con->fidlock);
|
||||
consPrint("\n");
|
||||
}
|
||||
vtRUnlock(cbox.clock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
msgInit(void)
|
||||
{
|
||||
mbox.alock = vtLockAlloc();
|
||||
mbox.arendez = vtRendezAlloc(mbox.alock);
|
||||
|
||||
mbox.rlock = vtLockAlloc();
|
||||
mbox.rrendez = vtRendezAlloc(mbox.rlock);
|
||||
|
||||
mbox.maxmsg = NMsgInit;
|
||||
mbox.maxproc = NMsgProcInit;
|
||||
mbox.msize = NMsizeInit;
|
||||
|
||||
cliAddCmd("msg", cmdMsg);
|
||||
}
|
||||
|
||||
static int
|
||||
cmdCon(int argc, char* argv[])
|
||||
{
|
||||
char *p;
|
||||
Con *con;
|
||||
char *usage = "usage: con [-m ncon]";
|
||||
int maxcon, ncon, nconstarve;
|
||||
|
||||
maxcon = 0;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 'm':
|
||||
p = ARGF();
|
||||
if(p == nil)
|
||||
return cliError(usage);
|
||||
maxcon = strtol(argv[0], &p, 0);
|
||||
if(maxcon <= 0 || p == argv[0] || *p != '\0')
|
||||
return cliError(usage);
|
||||
break;
|
||||
}ARGEND
|
||||
if(argc)
|
||||
return cliError(usage);
|
||||
|
||||
vtLock(cbox.clock);
|
||||
if(maxcon)
|
||||
cbox.maxcon = maxcon;
|
||||
maxcon = cbox.maxcon;
|
||||
ncon = cbox.ncon;
|
||||
nconstarve = cbox.nconstarve;
|
||||
vtUnlock(cbox.clock);
|
||||
|
||||
consPrint("\tcon -m %d\n", maxcon);
|
||||
consPrint("\tncon %d nconstarve %d\n", ncon, nconstarve);
|
||||
|
||||
vtRLock(cbox.clock);
|
||||
for(con = cbox.chead; con != nil; con = con->cnext){
|
||||
consPrint("\t%s\n", con->name);
|
||||
}
|
||||
vtRUnlock(cbox.clock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
conInit(void)
|
||||
{
|
||||
cbox.alock = vtLockAlloc();
|
||||
cbox.arendez = vtRendezAlloc(cbox.alock);
|
||||
|
||||
cbox.clock = vtLockAlloc();
|
||||
|
||||
cbox.maxcon = NConInit;
|
||||
cbox.msize = NMsizeInit;
|
||||
|
||||
cliAddCmd("con", cmdCon);
|
||||
cliAddCmd("who", cmdWho);
|
||||
}
|
||||
242
src/cmd/fossil/9srv.c
Normal file
242
src/cmd/fossil/9srv.c
Normal file
@ -0,0 +1,242 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
typedef struct Srv Srv;
|
||||
struct Srv {
|
||||
int fd;
|
||||
int srvfd;
|
||||
char* service;
|
||||
char* mntpnt;
|
||||
|
||||
Srv* next;
|
||||
Srv* prev;
|
||||
};
|
||||
|
||||
static struct {
|
||||
VtLock* lock;
|
||||
|
||||
Srv* head;
|
||||
Srv* tail;
|
||||
} sbox;
|
||||
|
||||
static int
|
||||
srvFd(char* name, int mode, int fd, char** mntpnt)
|
||||
{
|
||||
int n, srvfd;
|
||||
char *p, buf[10];
|
||||
|
||||
/*
|
||||
* Drop a file descriptor with given name and mode into /srv.
|
||||
* Create with ORCLOSE and don't close srvfd so it will be removed
|
||||
* automatically on process exit.
|
||||
*/
|
||||
p = smprint("/srv/%s", name);
|
||||
if((srvfd = create(p, ORCLOSE|OWRITE, mode)) < 0){
|
||||
vtMemFree(p);
|
||||
p = smprint("#s/%s", name);
|
||||
if((srvfd = create(p, ORCLOSE|OWRITE, mode)) < 0){
|
||||
vtSetError("create %s: %r", p);
|
||||
vtMemFree(p);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
n = snprint(buf, sizeof(buf), "%d", fd);
|
||||
if(write(srvfd, buf, n) < 0){
|
||||
close(srvfd);
|
||||
vtSetError("write %s: %r", p);
|
||||
vtMemFree(p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*mntpnt = p;
|
||||
|
||||
return srvfd;
|
||||
}
|
||||
|
||||
static void
|
||||
srvFree(Srv* srv)
|
||||
{
|
||||
if(srv->prev != nil)
|
||||
srv->prev->next = srv->next;
|
||||
else
|
||||
sbox.head = srv->next;
|
||||
if(srv->next != nil)
|
||||
srv->next->prev = srv->prev;
|
||||
else
|
||||
sbox.tail = srv->prev;
|
||||
|
||||
if(srv->srvfd != -1)
|
||||
close(srv->srvfd);
|
||||
vtMemFree(srv->service);
|
||||
vtMemFree(srv->mntpnt);
|
||||
vtMemFree(srv);
|
||||
}
|
||||
|
||||
static Srv*
|
||||
srvAlloc(char* service, int mode, int fd)
|
||||
{
|
||||
Dir *dir;
|
||||
Srv *srv;
|
||||
int srvfd;
|
||||
char *mntpnt;
|
||||
|
||||
vtLock(sbox.lock);
|
||||
for(srv = sbox.head; srv != nil; srv = srv->next){
|
||||
if(strcmp(srv->service, service) != 0)
|
||||
continue;
|
||||
/*
|
||||
* If the service exists, but is stale,
|
||||
* free it up and let the name be reused.
|
||||
*/
|
||||
if((dir = dirfstat(srv->srvfd)) != nil){
|
||||
free(dir);
|
||||
vtSetError("srv: already serving '%s'", service);
|
||||
vtUnlock(sbox.lock);
|
||||
return nil;
|
||||
}
|
||||
srvFree(srv);
|
||||
break;
|
||||
}
|
||||
|
||||
if((srvfd = srvFd(service, mode, fd, &mntpnt)) < 0){
|
||||
vtUnlock(sbox.lock);
|
||||
return nil;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
srv = vtMemAllocZ(sizeof(Srv));
|
||||
srv->srvfd = srvfd;
|
||||
srv->service = vtStrDup(service);
|
||||
srv->mntpnt = mntpnt;
|
||||
|
||||
if(sbox.tail != nil){
|
||||
srv->prev = sbox.tail;
|
||||
sbox.tail->next = srv;
|
||||
}
|
||||
else{
|
||||
sbox.head = srv;
|
||||
srv->prev = nil;
|
||||
}
|
||||
sbox.tail = srv;
|
||||
vtUnlock(sbox.lock);
|
||||
|
||||
return srv;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdSrv(int argc, char* argv[])
|
||||
{
|
||||
Con *con;
|
||||
Srv *srv;
|
||||
char *usage = "usage: srv [-APWdp] [service]";
|
||||
int conflags, dflag, fd[2], mode, pflag, r;
|
||||
|
||||
dflag = 0;
|
||||
pflag = 0;
|
||||
conflags = 0;
|
||||
mode = 0666;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 'A':
|
||||
conflags |= ConNoAuthCheck;
|
||||
break;
|
||||
case 'I':
|
||||
conflags |= ConIPCheck;
|
||||
break;
|
||||
case 'N':
|
||||
conflags |= ConNoneAllow;
|
||||
break;
|
||||
case 'P':
|
||||
conflags |= ConNoPermCheck;
|
||||
mode = 0600;
|
||||
break;
|
||||
case 'W':
|
||||
conflags |= ConWstatAllow;
|
||||
mode = 0600;
|
||||
break;
|
||||
case 'd':
|
||||
dflag = 1;
|
||||
break;
|
||||
case 'p':
|
||||
pflag = 1;
|
||||
mode = 0600;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(pflag && (conflags&ConNoPermCheck)){
|
||||
vtSetError("srv: cannot use -P with -p");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(argc){
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 0:
|
||||
vtRLock(sbox.lock);
|
||||
for(srv = sbox.head; srv != nil; srv = srv->next)
|
||||
consPrint("\t%s\t%d\n", srv->service, srv->srvfd);
|
||||
vtRUnlock(sbox.lock);
|
||||
|
||||
return 1;
|
||||
case 1:
|
||||
if(!dflag)
|
||||
break;
|
||||
|
||||
vtLock(sbox.lock);
|
||||
for(srv = sbox.head; srv != nil; srv = srv->next){
|
||||
if(strcmp(srv->service, argv[0]) != 0)
|
||||
continue;
|
||||
srvFree(srv);
|
||||
break;
|
||||
}
|
||||
vtUnlock(sbox.lock);
|
||||
|
||||
if(srv == nil){
|
||||
vtSetError("srv: '%s' not found", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(pipe(fd) < 0){
|
||||
vtSetError("srv pipe: %r");
|
||||
return 0;
|
||||
}
|
||||
if((srv = srvAlloc(argv[0], mode, fd[0])) == nil){
|
||||
close(fd[0]); close(fd[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(pflag)
|
||||
r = consOpen(fd[1], srv->srvfd, -1);
|
||||
else{
|
||||
con = conAlloc(fd[1], srv->mntpnt, conflags);
|
||||
if(con == nil)
|
||||
r = 0;
|
||||
else
|
||||
r = 1;
|
||||
}
|
||||
if(r == 0){
|
||||
close(fd[1]);
|
||||
vtLock(sbox.lock);
|
||||
srvFree(srv);
|
||||
vtUnlock(sbox.lock);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
srvInit(void)
|
||||
{
|
||||
sbox.lock = vtLockAlloc();
|
||||
|
||||
cliAddCmd("srv", cmdSrv);
|
||||
|
||||
return 1;
|
||||
}
|
||||
948
src/cmd/fossil/9user.c
Normal file
948
src/cmd/fossil/9user.c
Normal file
@ -0,0 +1,948 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
enum {
|
||||
NUserHash = 1009,
|
||||
};
|
||||
|
||||
typedef struct Ubox Ubox;
|
||||
typedef struct User User;
|
||||
|
||||
struct User {
|
||||
char* uid;
|
||||
char* uname;
|
||||
char* leader;
|
||||
char** group;
|
||||
int ngroup;
|
||||
|
||||
User* next; /* */
|
||||
User* ihash; /* lookup by .uid */
|
||||
User* nhash; /* lookup by .uname */
|
||||
};
|
||||
|
||||
#pragma varargck type "U" User*
|
||||
|
||||
struct Ubox {
|
||||
User* head;
|
||||
User* tail;
|
||||
int nuser;
|
||||
int len;
|
||||
|
||||
User* ihash[NUserHash]; /* lookup by .uid */
|
||||
User* nhash[NUserHash]; /* lookup by .uname */
|
||||
};
|
||||
|
||||
static struct {
|
||||
VtLock* lock;
|
||||
|
||||
Ubox* box;
|
||||
} ubox;
|
||||
|
||||
static char usersDefault[] = {
|
||||
"adm:adm:adm:sys\n"
|
||||
"none:none::\n"
|
||||
"noworld:noworld::\n"
|
||||
"sys:sys::glenda\n"
|
||||
"glenda:glenda:glenda:\n"
|
||||
};
|
||||
|
||||
static char* usersMandatory[] = {
|
||||
"adm",
|
||||
"none",
|
||||
"noworld",
|
||||
"sys",
|
||||
nil,
|
||||
};
|
||||
|
||||
char* uidadm = "adm";
|
||||
char* unamenone = "none";
|
||||
char* uidnoworld = "noworld";
|
||||
|
||||
static u32int
|
||||
userHash(char* s)
|
||||
{
|
||||
uchar *p;
|
||||
u32int hash;
|
||||
|
||||
hash = 0;
|
||||
for(p = (uchar*)s; *p != '\0'; p++)
|
||||
hash = hash*7 + *p;
|
||||
|
||||
return hash % NUserHash;
|
||||
}
|
||||
|
||||
static User*
|
||||
_userByUid(Ubox* box, char* uid)
|
||||
{
|
||||
User *u;
|
||||
|
||||
if(box != nil){
|
||||
for(u = box->ihash[userHash(uid)]; u != nil; u = u->ihash){
|
||||
if(strcmp(u->uid, uid) == 0)
|
||||
return u;
|
||||
}
|
||||
}
|
||||
vtSetError("uname: uid '%s' not found", uid);
|
||||
return nil;
|
||||
}
|
||||
|
||||
char*
|
||||
unameByUid(char* uid)
|
||||
{
|
||||
User *u;
|
||||
char *uname;
|
||||
|
||||
vtRLock(ubox.lock);
|
||||
if((u = _userByUid(ubox.box, uid)) == nil){
|
||||
vtRUnlock(ubox.lock);
|
||||
return nil;
|
||||
}
|
||||
uname = vtStrDup(u->uname);
|
||||
vtRUnlock(ubox.lock);
|
||||
|
||||
return uname;
|
||||
}
|
||||
|
||||
static User*
|
||||
_userByUname(Ubox* box, char* uname)
|
||||
{
|
||||
User *u;
|
||||
|
||||
if(box != nil){
|
||||
for(u = box->nhash[userHash(uname)]; u != nil; u = u->nhash){
|
||||
if(strcmp(u->uname, uname) == 0)
|
||||
return u;
|
||||
}
|
||||
}
|
||||
vtSetError("uname: uname '%s' not found", uname);
|
||||
return nil;
|
||||
}
|
||||
|
||||
char*
|
||||
uidByUname(char* uname)
|
||||
{
|
||||
User *u;
|
||||
char *uid;
|
||||
|
||||
vtRLock(ubox.lock);
|
||||
if((u = _userByUname(ubox.box, uname)) == nil){
|
||||
vtRUnlock(ubox.lock);
|
||||
return nil;
|
||||
}
|
||||
uid = vtStrDup(u->uid);
|
||||
vtRUnlock(ubox.lock);
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
static int
|
||||
_groupMember(Ubox* box, char* group, char* member, int whenNoGroup)
|
||||
{
|
||||
int i;
|
||||
User *g, *m;
|
||||
|
||||
/*
|
||||
* Is 'member' a member of 'group'?
|
||||
* Note that 'group' is a 'uid' and not a 'uname'.
|
||||
* A 'member' is automatically in their own group.
|
||||
*/
|
||||
if((g = _userByUid(box, group)) == nil)
|
||||
return whenNoGroup;
|
||||
if((m = _userByUname(box, member)) == nil)
|
||||
return 0;
|
||||
if(m == g)
|
||||
return 1;
|
||||
for(i = 0; i < g->ngroup; i++){
|
||||
if(strcmp(g->group[i], member) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
groupWriteMember(char* uname)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If there is a ``write'' group, then only its members can write
|
||||
* to the file system, no matter what the permission bits say.
|
||||
*
|
||||
* To users not in the ``write'' group, the file system appears
|
||||
* read only. This is used to serve sources.cs.bell-labs.com
|
||||
* to the world.
|
||||
*
|
||||
* Note that if there is no ``write'' group, then this routine
|
||||
* makes it look like everyone is a member -- the opposite
|
||||
* of what groupMember does.
|
||||
*
|
||||
* We use this for sources.cs.bell-labs.com.
|
||||
* If this slows things down too much on systems that don't
|
||||
* use this functionality, we could cache the write group lookup.
|
||||
*/
|
||||
|
||||
vtRLock(ubox.lock);
|
||||
ret = _groupMember(ubox.box, "write", uname, 1);
|
||||
vtRUnlock(ubox.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
_groupRemMember(Ubox* box, User* g, char* member)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(_userByUname(box, member) == nil)
|
||||
return 0;
|
||||
|
||||
for(i = 0; i < g->ngroup; i++){
|
||||
if(strcmp(g->group[i], member) == 0)
|
||||
break;
|
||||
}
|
||||
if(i >= g->ngroup){
|
||||
if(strcmp(g->uname, member) == 0)
|
||||
vtSetError("uname: '%s' always in own group", member);
|
||||
else
|
||||
vtSetError("uname: '%s' not in group '%s'",
|
||||
member, g->uname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vtMemFree(g->group[i]);
|
||||
|
||||
box->len -= strlen(member);
|
||||
if(g->ngroup > 1)
|
||||
box->len--;
|
||||
g->ngroup--;
|
||||
switch(g->ngroup){
|
||||
case 0:
|
||||
vtMemFree(g->group);
|
||||
g->group = nil;
|
||||
break;
|
||||
default:
|
||||
for(; i < g->ngroup; i++)
|
||||
g->group[i] = g->group[i+1];
|
||||
g->group[i] = nil; /* prevent accidents */
|
||||
g->group = vtMemRealloc(g->group, g->ngroup * sizeof(char*));
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
_groupAddMember(Ubox* box, User* g, char* member)
|
||||
{
|
||||
User *u;
|
||||
|
||||
if((u = _userByUname(box, member)) == nil)
|
||||
return 0;
|
||||
if(_groupMember(box, g->uid, u->uname, 0)){
|
||||
if(strcmp(g->uname, member) == 0)
|
||||
vtSetError("uname: '%s' always in own group", member);
|
||||
else
|
||||
vtSetError("uname: '%s' already in group '%s'",
|
||||
member, g->uname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g->group = vtMemRealloc(g->group, (g->ngroup+1)*sizeof(char*));
|
||||
g->group[g->ngroup] = vtStrDup(member);
|
||||
box->len += strlen(member);
|
||||
g->ngroup++;
|
||||
if(g->ngroup > 1)
|
||||
box->len++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
groupMember(char* group, char* member)
|
||||
{
|
||||
int r;
|
||||
|
||||
if(group == nil)
|
||||
return 0;
|
||||
|
||||
vtRLock(ubox.lock);
|
||||
r = _groupMember(ubox.box, group, member, 0);
|
||||
vtRUnlock(ubox.lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
groupLeader(char* group, char* member)
|
||||
{
|
||||
int r;
|
||||
User *g;
|
||||
|
||||
/*
|
||||
* Is 'member' the leader of 'group'?
|
||||
* Note that 'group' is a 'uid' and not a 'uname'.
|
||||
* Uname 'none' cannot be a group leader.
|
||||
*/
|
||||
if(strcmp(member, unamenone) == 0 || group == nil)
|
||||
return 0;
|
||||
|
||||
vtRLock(ubox.lock);
|
||||
if((g = _userByUid(ubox.box, group)) == nil){
|
||||
vtRUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
if(g->leader != nil){
|
||||
if(strcmp(g->leader, member) == 0){
|
||||
vtRUnlock(ubox.lock);
|
||||
return 1;
|
||||
}
|
||||
r = 0;
|
||||
}
|
||||
else
|
||||
r = _groupMember(ubox.box, group, member, 0);
|
||||
vtRUnlock(ubox.lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
userFree(User* u)
|
||||
{
|
||||
int i;
|
||||
|
||||
vtMemFree(u->uid);
|
||||
vtMemFree(u->uname);
|
||||
if(u->leader != nil)
|
||||
vtMemFree(u->leader);
|
||||
if(u->ngroup){
|
||||
for(i = 0; i < u->ngroup; i++)
|
||||
vtMemFree(u->group[i]);
|
||||
vtMemFree(u->group);
|
||||
}
|
||||
vtMemFree(u);
|
||||
}
|
||||
|
||||
static User*
|
||||
userAlloc(char* uid, char* uname)
|
||||
{
|
||||
User *u;
|
||||
|
||||
u = vtMemAllocZ(sizeof(User));
|
||||
u->uid = vtStrDup(uid);
|
||||
u->uname = vtStrDup(uname);
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
int
|
||||
validUserName(char* name)
|
||||
{
|
||||
Rune *r;
|
||||
static Rune invalid[] = L"#:,()";
|
||||
|
||||
for(r = invalid; *r != '\0'; r++){
|
||||
if(utfrune(name, *r))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
userFmt(Fmt* fmt)
|
||||
{
|
||||
User *u;
|
||||
int i, r;
|
||||
|
||||
u = va_arg(fmt->args, User*);
|
||||
|
||||
r = fmtprint(fmt, "%s:%s:", u->uid, u->uname);
|
||||
if(u->leader != nil)
|
||||
r += fmtprint(fmt, u->leader);
|
||||
r += fmtprint(fmt, ":");
|
||||
if(u->ngroup){
|
||||
r += fmtprint(fmt, u->group[0]);
|
||||
for(i = 1; i < u->ngroup; i++)
|
||||
r += fmtprint(fmt, ",%s", u->group[i]);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
usersFileWrite(Ubox* box)
|
||||
{
|
||||
Fs *fs;
|
||||
User *u;
|
||||
int i, r;
|
||||
Fsys *fsys;
|
||||
char *p, *q, *s;
|
||||
File *dir, *file;
|
||||
|
||||
if((fsys = fsysGet("main")) == nil)
|
||||
return 0;
|
||||
fsysFsRlock(fsys);
|
||||
fs = fsysGetFs(fsys);
|
||||
|
||||
/*
|
||||
* BUG:
|
||||
* the owner/group/permissions need to be thought out.
|
||||
*/
|
||||
r = 0;
|
||||
if((dir = fileOpen(fs, "/active")) == nil)
|
||||
goto tidy0;
|
||||
if((file = fileWalk(dir, uidadm)) == nil)
|
||||
file = fileCreate(dir, uidadm, ModeDir|0775, uidadm);
|
||||
fileDecRef(dir);
|
||||
if(file == nil)
|
||||
goto tidy;
|
||||
dir = file;
|
||||
if((file = fileWalk(dir, "users")) == nil)
|
||||
file = fileCreate(dir, "users", 0664, uidadm);
|
||||
fileDecRef(dir);
|
||||
if(file == nil)
|
||||
goto tidy;
|
||||
if(!fileTruncate(file, uidadm))
|
||||
goto tidy;
|
||||
|
||||
p = s = vtMemAlloc(box->len+1);
|
||||
q = p + box->len+1;
|
||||
for(u = box->head; u != nil; u = u->next){
|
||||
p += snprint(p, q-p, "%s:%s:", u->uid, u->uname);
|
||||
if(u->leader != nil)
|
||||
p+= snprint(p, q-p, u->leader);
|
||||
p += snprint(p, q-p, ":");
|
||||
if(u->ngroup){
|
||||
p += snprint(p, q-p, u->group[0]);
|
||||
for(i = 1; i < u->ngroup; i++)
|
||||
p += snprint(p, q-p, ",%s", u->group[i]);
|
||||
}
|
||||
p += snprint(p, q-p, "\n");
|
||||
}
|
||||
r = fileWrite(file, s, box->len, 0, uidadm);
|
||||
vtMemFree(s);
|
||||
|
||||
tidy:
|
||||
if(file != nil)
|
||||
fileDecRef(file);
|
||||
tidy0:
|
||||
fsysFsRUnlock(fsys);
|
||||
fsysPut(fsys);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
uboxRemUser(Ubox* box, User *u)
|
||||
{
|
||||
User **h, *up;
|
||||
|
||||
h = &box->ihash[userHash(u->uid)];
|
||||
for(up = *h; up != nil && up != u; up = up->ihash)
|
||||
h = &up->ihash;
|
||||
assert(up == u);
|
||||
*h = up->ihash;
|
||||
box->len -= strlen(u->uid);
|
||||
|
||||
h = &box->nhash[userHash(u->uname)];
|
||||
for(up = *h; up != nil && up != u; up = up->nhash)
|
||||
h = &up->nhash;
|
||||
assert(up == u);
|
||||
*h = up->nhash;
|
||||
box->len -= strlen(u->uname);
|
||||
|
||||
h = &box->head;
|
||||
for(up = *h; up != nil && strcmp(up->uid, u->uid) != 0; up = up->next)
|
||||
h = &up->next;
|
||||
assert(up == u);
|
||||
*h = u->next;
|
||||
u->next = nil;
|
||||
|
||||
box->len -= 4;
|
||||
box->nuser--;
|
||||
}
|
||||
|
||||
static void
|
||||
uboxAddUser(Ubox* box, User* u)
|
||||
{
|
||||
User **h, *up;
|
||||
|
||||
h = &box->ihash[userHash(u->uid)];
|
||||
u->ihash = *h;
|
||||
*h = u;
|
||||
box->len += strlen(u->uid);
|
||||
|
||||
h = &box->nhash[userHash(u->uname)];
|
||||
u->nhash = *h;
|
||||
*h = u;
|
||||
box->len += strlen(u->uname);
|
||||
|
||||
h = &box->head;
|
||||
for(up = *h; up != nil && strcmp(up->uid, u->uid) < 0; up = up->next)
|
||||
h = &up->next;
|
||||
u->next = *h;
|
||||
*h = u;
|
||||
|
||||
box->len += 4;
|
||||
box->nuser++;
|
||||
}
|
||||
|
||||
static void
|
||||
uboxDump(Ubox* box)
|
||||
{
|
||||
User* u;
|
||||
|
||||
consPrint("nuser %d len = %d\n", box->nuser, box->len);
|
||||
|
||||
for(u = box->head; u != nil; u = u->next)
|
||||
consPrint("%U\n", u);
|
||||
}
|
||||
|
||||
static void
|
||||
uboxFree(Ubox* box)
|
||||
{
|
||||
User *next, *u;
|
||||
|
||||
for(u = box->head; u != nil; u = next){
|
||||
next = u->next;
|
||||
userFree(u);
|
||||
}
|
||||
vtMemFree(box);
|
||||
}
|
||||
|
||||
static int
|
||||
uboxInit(char* users, int len)
|
||||
{
|
||||
User *g, *u;
|
||||
Ubox *box, *obox;
|
||||
int blank, comment, i, nline, nuser;
|
||||
char *buf, *f[5], **line, *p, *q, *s;
|
||||
|
||||
/*
|
||||
* Strip out whitespace and comments.
|
||||
* Note that comments are pointless, they disappear
|
||||
* when the server writes the database back out.
|
||||
*/
|
||||
blank = 1;
|
||||
comment = nline = 0;
|
||||
|
||||
s = p = buf = vtMemAlloc(len+1);
|
||||
for(q = users; *q != '\0'; q++){
|
||||
if(*q == '\r' || *q == '\t' || *q == ' ')
|
||||
continue;
|
||||
if(*q == '\n'){
|
||||
if(!blank){
|
||||
if(p != s){
|
||||
*p++ = '\n';
|
||||
nline++;
|
||||
s = p;
|
||||
}
|
||||
blank = 1;
|
||||
}
|
||||
comment = 0;
|
||||
continue;
|
||||
}
|
||||
if(*q == '#')
|
||||
comment = 1;
|
||||
blank = 0;
|
||||
if(!comment)
|
||||
*p++ = *q;
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
line = vtMemAllocZ((nline+2)*sizeof(char*));
|
||||
if((i = gettokens(buf, line, nline+2, "\n")) != nline){
|
||||
fprint(2, "nline %d (%d) botch\n", nline, i);
|
||||
vtMemFree(line);
|
||||
vtMemFree(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything is updated in a local Ubox until verified.
|
||||
*/
|
||||
box = vtMemAllocZ(sizeof(Ubox));
|
||||
|
||||
/*
|
||||
* First pass - check format, check for duplicates
|
||||
* and enter in hash buckets.
|
||||
*/
|
||||
nuser = 0;
|
||||
for(i = 0; i < nline; i++){
|
||||
s = vtStrDup(line[i]);
|
||||
if(getfields(s, f, nelem(f), 0, ":") != 4){
|
||||
fprint(2, "bad line '%s'\n", line[i]);
|
||||
vtMemFree(s);
|
||||
continue;
|
||||
}
|
||||
if(*f[0] == '\0' || *f[1] == '\0'){
|
||||
fprint(2, "bad line '%s'\n", line[i]);
|
||||
vtMemFree(s);
|
||||
continue;
|
||||
}
|
||||
if(!validUserName(f[0])){
|
||||
fprint(2, "invalid uid '%s'\n", f[0]);
|
||||
vtMemFree(s);
|
||||
continue;
|
||||
}
|
||||
if(_userByUid(box, f[0]) != nil){
|
||||
fprint(2, "duplicate uid '%s'\n", f[0]);
|
||||
vtMemFree(s);
|
||||
continue;
|
||||
}
|
||||
if(!validUserName(f[1])){
|
||||
fprint(2, "invalid uname '%s'\n", f[0]);
|
||||
vtMemFree(s);
|
||||
continue;
|
||||
}
|
||||
if(_userByUname(box, f[1]) != nil){
|
||||
fprint(2, "duplicate uname '%s'\n", f[1]);
|
||||
vtMemFree(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
u = userAlloc(f[0], f[1]);
|
||||
uboxAddUser(box, u);
|
||||
line[nuser] = line[i];
|
||||
nuser++;
|
||||
|
||||
vtMemFree(s);
|
||||
}
|
||||
assert(box->nuser == nuser);
|
||||
|
||||
/*
|
||||
* Second pass - fill in leader and group information.
|
||||
*/
|
||||
for(i = 0; i < nuser; i++){
|
||||
s = vtStrDup(line[i]);
|
||||
getfields(s, f, nelem(f), 0, ":");
|
||||
|
||||
assert(g = _userByUname(box, f[1]));
|
||||
if(*f[2] != '\0'){
|
||||
if((u = _userByUname(box, f[2])) == nil)
|
||||
g->leader = vtStrDup(g->uname);
|
||||
else
|
||||
g->leader = vtStrDup(u->uname);
|
||||
box->len += strlen(g->leader);
|
||||
}
|
||||
for(p = f[3]; p != nil; p = q){
|
||||
if((q = utfrune(p, L',')) != nil)
|
||||
*q++ = '\0';
|
||||
if(!_groupAddMember(box, g, p)){
|
||||
// print/log error here
|
||||
}
|
||||
}
|
||||
|
||||
vtMemFree(s);
|
||||
}
|
||||
|
||||
vtMemFree(line);
|
||||
vtMemFree(buf);
|
||||
|
||||
for(i = 0; usersMandatory[i] != nil; i++){
|
||||
if((u = _userByUid(box, usersMandatory[i])) == nil){
|
||||
vtSetError("user '%s' is mandatory", usersMandatory[i]);
|
||||
uboxFree(box);
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(u->uid, u->uname) != 0){
|
||||
vtSetError("uid/uname for user '%s' must match",
|
||||
usersMandatory[i]);
|
||||
uboxFree(box);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
vtLock(ubox.lock);
|
||||
obox = ubox.box;
|
||||
ubox.box = box;
|
||||
vtUnlock(ubox.lock);
|
||||
|
||||
if(obox != nil)
|
||||
uboxFree(obox);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
usersFileRead(char* path)
|
||||
{
|
||||
char *p;
|
||||
File *file;
|
||||
Fsys *fsys;
|
||||
int len, r;
|
||||
uvlong size;
|
||||
|
||||
if((fsys = fsysGet("main")) == nil)
|
||||
return 0;
|
||||
fsysFsRlock(fsys);
|
||||
|
||||
if(path == nil)
|
||||
path = "/active/adm/users";
|
||||
|
||||
r = 0;
|
||||
if((file = fileOpen(fsysGetFs(fsys), path)) != nil){
|
||||
if(fileGetSize(file, &size)){
|
||||
len = size;
|
||||
p = vtMemAlloc(size+1);
|
||||
if(fileRead(file, p, len, 0) == len){
|
||||
p[len] = '\0';
|
||||
r = uboxInit(p, len);
|
||||
}
|
||||
}
|
||||
fileDecRef(file);
|
||||
}
|
||||
|
||||
fsysFsRUnlock(fsys);
|
||||
fsysPut(fsys);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdUname(int argc, char* argv[])
|
||||
{
|
||||
User *u, *up;
|
||||
int d, dflag, i, r;
|
||||
char *p, *uid, *uname;
|
||||
char *createfmt = "fsys main create /active/usr/%s %s %s d775";
|
||||
char *usage = "usage: uname [-d] uname [uid|:uid|%%newname|=leader|+member|-member]";
|
||||
|
||||
dflag = 0;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 'd':
|
||||
dflag = 1;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(argc < 1){
|
||||
if(!dflag)
|
||||
return cliError(usage);
|
||||
vtRLock(ubox.lock);
|
||||
uboxDump(ubox.box);
|
||||
vtRUnlock(ubox.lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uname = argv[0];
|
||||
argc--; argv++;
|
||||
|
||||
if(argc == 0){
|
||||
vtRLock(ubox.lock);
|
||||
if((u = _userByUname(ubox.box, uname)) == nil){
|
||||
vtRUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
consPrint("\t%U\n", u);
|
||||
vtRUnlock(ubox.lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vtLock(ubox.lock);
|
||||
u = _userByUname(ubox.box, uname);
|
||||
while(argc--){
|
||||
if(argv[0][0] == '%'){
|
||||
if(u == nil){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
p = &argv[0][1];
|
||||
if((up = _userByUname(ubox.box, p)) != nil){
|
||||
vtSetError("uname: uname '%s' already exists",
|
||||
up->uname);
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
for(i = 0; usersMandatory[i] != nil; i++){
|
||||
if(strcmp(usersMandatory[i], uname) != 0)
|
||||
continue;
|
||||
vtSetError("uname: uname '%s' is mandatory",
|
||||
uname);
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
d = strlen(p) - strlen(u->uname);
|
||||
for(up = ubox.box->head; up != nil; up = up->next){
|
||||
if(up->leader != nil){
|
||||
if(strcmp(up->leader, u->uname) == 0){
|
||||
vtMemFree(up->leader);
|
||||
up->leader = vtStrDup(p);
|
||||
ubox.box->len += d;
|
||||
}
|
||||
}
|
||||
for(i = 0; i < up->ngroup; i++){
|
||||
if(strcmp(up->group[i], u->uname) != 0)
|
||||
continue;
|
||||
vtMemFree(up->group[i]);
|
||||
up->group[i] = vtStrDup(p);
|
||||
ubox.box->len += d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uboxRemUser(ubox.box, u);
|
||||
vtMemFree(u->uname);
|
||||
u->uname = vtStrDup(p);
|
||||
uboxAddUser(ubox.box, u);
|
||||
}
|
||||
else if(argv[0][0] == '='){
|
||||
if(u == nil){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
|
||||
if(argv[0][1] != '\0'){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(u->leader != nil){
|
||||
ubox.box->len -= strlen(u->leader);
|
||||
vtMemFree(u->leader);
|
||||
u->leader = nil;
|
||||
}
|
||||
if(up != nil){
|
||||
u->leader = vtStrDup(up->uname);
|
||||
ubox.box->len += strlen(u->leader);
|
||||
}
|
||||
}
|
||||
else if(argv[0][0] == '+'){
|
||||
if(u == nil){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
if(!_groupAddMember(ubox.box, u, up->uname)){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if(argv[0][0] == '-'){
|
||||
if(u == nil){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
if((up = _userByUname(ubox.box, &argv[0][1])) == nil){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
if(!_groupRemMember(ubox.box, u, up->uname)){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(u != nil){
|
||||
vtSetError("uname: uname '%s' already exists",
|
||||
u->uname);
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uid = argv[0];
|
||||
if(*uid == ':')
|
||||
uid++;
|
||||
if((u = _userByUid(ubox.box, uid)) != nil){
|
||||
vtSetError("uname: uid '%s' already exists",
|
||||
u->uid);
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u = userAlloc(uid, uname);
|
||||
uboxAddUser(ubox.box, u);
|
||||
if(argv[0][0] != ':'){
|
||||
// should have an option for the mode and gid
|
||||
p = smprint(createfmt, uname, uname, uname);
|
||||
r = cliExec(p);
|
||||
vtMemFree(p);
|
||||
if(r == 0){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
argv++;
|
||||
}
|
||||
|
||||
if(usersFileWrite(ubox.box) == 0){
|
||||
vtUnlock(ubox.lock);
|
||||
return 0;
|
||||
}
|
||||
if(dflag)
|
||||
uboxDump(ubox.box);
|
||||
vtUnlock(ubox.lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdUsers(int argc, char* argv[])
|
||||
{
|
||||
Ubox *box;
|
||||
int dflag, r, wflag;
|
||||
char *file;
|
||||
char *usage = "usage: users [-d | -r file] [-w]";
|
||||
|
||||
dflag = wflag = 0;
|
||||
file = nil;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 'd':
|
||||
dflag = 1;
|
||||
break;
|
||||
case 'r':
|
||||
file = ARGF();
|
||||
if(file == nil)
|
||||
return cliError(usage);
|
||||
break;
|
||||
case 'w':
|
||||
wflag = 1;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(argc)
|
||||
return cliError(usage);
|
||||
|
||||
if(dflag && file)
|
||||
return cliError("cannot use -d and -r together");
|
||||
|
||||
if(dflag)
|
||||
uboxInit(usersDefault, sizeof(usersDefault));
|
||||
else if(file){
|
||||
if(usersFileRead(file) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
vtRLock(ubox.lock);
|
||||
box = ubox.box;
|
||||
consPrint("\tnuser %d len %d\n", box->nuser, box->len);
|
||||
|
||||
r = 1;
|
||||
if(wflag)
|
||||
r = usersFileWrite(box);
|
||||
vtRUnlock(ubox.lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
usersInit(void)
|
||||
{
|
||||
fmtinstall('U', userFmt);
|
||||
|
||||
ubox.lock = vtLockAlloc();
|
||||
uboxInit(usersDefault, sizeof(usersDefault));
|
||||
|
||||
cliAddCmd("users", cmdUsers);
|
||||
cliAddCmd("uname", cmdUname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
112
src/cmd/fossil/Ccli.c
Normal file
112
src/cmd/fossil/Ccli.c
Normal file
@ -0,0 +1,112 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
typedef struct {
|
||||
char* argv0;
|
||||
int (*cmd)(int, char*[]);
|
||||
} Cmd;
|
||||
|
||||
static struct {
|
||||
VtLock* lock;
|
||||
Cmd* cmd;
|
||||
int ncmd;
|
||||
int hi;
|
||||
} cbox;
|
||||
|
||||
enum {
|
||||
NCmdIncr = 20,
|
||||
};
|
||||
|
||||
int
|
||||
cliError(char* fmt, ...)
|
||||
{
|
||||
char *p;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
p = vsmprint(fmt, arg);
|
||||
vtSetError("%s", p);
|
||||
free(p);
|
||||
va_end(arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
cliExec(char* buf)
|
||||
{
|
||||
int argc, i, r;
|
||||
char *argv[20], *p;
|
||||
|
||||
p = vtStrDup(buf);
|
||||
if((argc = tokenize(p, argv, nelem(argv)-1)) == 0){
|
||||
vtMemFree(p);
|
||||
return 1;
|
||||
}
|
||||
argv[argc] = 0;
|
||||
|
||||
if(argv[0][0] == '#'){
|
||||
vtMemFree(p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vtLock(cbox.lock);
|
||||
for(i = 0; i < cbox.hi; i++){
|
||||
if(strcmp(cbox.cmd[i].argv0, argv[0]) == 0){
|
||||
vtUnlock(cbox.lock);
|
||||
if(!(r = cbox.cmd[i].cmd(argc, argv)))
|
||||
consPrint("%s\n", vtGetError());
|
||||
vtMemFree(p);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
vtUnlock(cbox.lock);
|
||||
|
||||
consPrint("%s: - eh?\n", argv[0]);
|
||||
vtMemFree(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
cliAddCmd(char* argv0, int (*cmd)(int, char*[]))
|
||||
{
|
||||
int i;
|
||||
Cmd *opt;
|
||||
|
||||
vtLock(cbox.lock);
|
||||
for(i = 0; i < cbox.hi; i++){
|
||||
if(strcmp(argv0, cbox.cmd[i].argv0) == 0){
|
||||
vtUnlock(cbox.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(i >= cbox.hi){
|
||||
if(cbox.hi >= cbox.ncmd){
|
||||
cbox.cmd = vtMemRealloc(cbox.cmd,
|
||||
(cbox.ncmd+NCmdIncr)*sizeof(Cmd));
|
||||
memset(&cbox.cmd[cbox.ncmd], 0, NCmdIncr*sizeof(Cmd));
|
||||
cbox.ncmd += NCmdIncr;
|
||||
}
|
||||
}
|
||||
|
||||
opt = &cbox.cmd[cbox.hi];
|
||||
opt->argv0 = argv0;
|
||||
opt->cmd = cmd;
|
||||
cbox.hi++;
|
||||
vtUnlock(cbox.lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
cliInit(void)
|
||||
{
|
||||
cbox.lock = vtLockAlloc();
|
||||
cbox.cmd = vtMemAllocZ(NCmdIncr*sizeof(Cmd));
|
||||
cbox.ncmd = NCmdIncr;
|
||||
cbox.hi = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
459
src/cmd/fossil/Ccmd.c
Normal file
459
src/cmd/fossil/Ccmd.c
Normal file
@ -0,0 +1,459 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
static struct {
|
||||
VtLock* lock;
|
||||
|
||||
Con* con;
|
||||
int confd[2];
|
||||
ushort tag;
|
||||
} cbox;
|
||||
|
||||
static ulong
|
||||
cmd9pStrtoul(char* s)
|
||||
{
|
||||
if(strcmp(s, "~0") == 0)
|
||||
return ~0UL;
|
||||
return strtoul(s, 0, 0);
|
||||
}
|
||||
|
||||
static uvlong
|
||||
cmd9pStrtoull(char* s)
|
||||
{
|
||||
if(strcmp(s, "~0") == 0)
|
||||
return ~0ULL;
|
||||
return strtoull(s, 0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTag(Fcall*, int, char **argv)
|
||||
{
|
||||
cbox.tag = strtoul(argv[0], 0, 0)-1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTwstat(Fcall* f, int, char **argv)
|
||||
{
|
||||
Dir d;
|
||||
static uchar buf[DIRMAX];
|
||||
|
||||
memset(&d, 0, sizeof d);
|
||||
nulldir(&d);
|
||||
d.name = argv[1];
|
||||
d.uid = argv[2];
|
||||
d.gid = argv[3];
|
||||
d.mode = cmd9pStrtoul(argv[4]);
|
||||
d.mtime = cmd9pStrtoul(argv[5]);
|
||||
d.length = cmd9pStrtoull(argv[6]);
|
||||
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
f->stat = buf;
|
||||
f->nstat = convD2M(&d, buf, sizeof buf);
|
||||
if(f->nstat < BIT16SZ){
|
||||
vtSetError("Twstat: convD2M failed (internal error)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTstat(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTremove(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTclunk(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTwrite(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
f->offset = strtoll(argv[1], 0, 0);
|
||||
f->data = argv[2];
|
||||
f->count = strlen(argv[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTread(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
f->offset = strtoll(argv[1], 0, 0);
|
||||
f->count = strtol(argv[2], 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTcreate(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
f->name = argv[1];
|
||||
f->perm = strtol(argv[2], 0, 8);
|
||||
f->mode = strtol(argv[3], 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTopen(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
f->mode = strtol(argv[1], 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTwalk(Fcall* f, int argc, char** argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(argc < 2){
|
||||
vtSetError("usage: Twalk tag fid newfid [name...]");
|
||||
return 0;
|
||||
}
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
f->newfid = strtol(argv[1], 0, 0);
|
||||
f->nwname = argc-2;
|
||||
if(f->nwname > MAXWELEM){
|
||||
vtSetError("Twalk: too many names");
|
||||
return 0;
|
||||
}
|
||||
for(i = 0; i < argc-2; i++)
|
||||
f->wname[i] = argv[2+i];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTflush(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->oldtag = strtol(argv[0], 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTattach(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->fid = strtol(argv[0], 0, 0);
|
||||
f->afid = strtol(argv[1], 0, 0);
|
||||
f->uname = argv[2];
|
||||
f->aname = argv[3];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTauth(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->afid = strtol(argv[0], 0, 0);
|
||||
f->uname = argv[1];
|
||||
f->aname = argv[2];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd9pTversion(Fcall* f, int, char** argv)
|
||||
{
|
||||
f->msize = strtoul(argv[0], 0, 0);
|
||||
if(f->msize > cbox.con->msize){
|
||||
vtSetError("msize too big");
|
||||
return 0;
|
||||
}
|
||||
f->version = argv[1];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct Cmd9p Cmd9p;
|
||||
struct Cmd9p {
|
||||
char* name;
|
||||
int type;
|
||||
int argc;
|
||||
char* usage;
|
||||
int (*f)(Fcall*, int, char**);
|
||||
};
|
||||
|
||||
static Cmd9p cmd9pTmsg[] = {
|
||||
"Tversion", Tversion, 2, "msize version", cmd9pTversion,
|
||||
"Tauth", Tauth, 3, "afid uname aname", cmd9pTauth,
|
||||
"Tflush", Tflush, 1, "oldtag", cmd9pTflush,
|
||||
"Tattach", Tattach, 4, "fid afid uname aname", cmd9pTattach,
|
||||
"Twalk", Twalk, 0, "fid newfid [name...]", cmd9pTwalk,
|
||||
"Topen", Topen, 2, "fid mode", cmd9pTopen,
|
||||
"Tcreate", Tcreate, 4, "fid name perm mode", cmd9pTcreate,
|
||||
"Tread", Tread, 3, "fid offset count", cmd9pTread,
|
||||
"Twrite", Twrite, 3, "fid offset data", cmd9pTwrite,
|
||||
"Tclunk", Tclunk, 1, "fid", cmd9pTclunk,
|
||||
"Tremove", Tremove, 1, "fid", cmd9pTremove,
|
||||
"Tstat", Tstat, 1, "fid", cmd9pTstat,
|
||||
"Twstat", Twstat, 7, "fid name uid gid mode mtime length", cmd9pTwstat,
|
||||
"nexttag", 0, 0, "", cmd9pTag,
|
||||
};
|
||||
|
||||
static int
|
||||
cmd9p(int argc, char* argv[])
|
||||
{
|
||||
int i, n;
|
||||
Fcall f, t;
|
||||
uchar *buf;
|
||||
char *usage;
|
||||
u32int msize;
|
||||
|
||||
usage = "usage: 9p T-message ...";
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
}ARGEND
|
||||
if(argc < 1)
|
||||
return cliError(usage);
|
||||
|
||||
for(i = 0; i < nelem(cmd9pTmsg); i++){
|
||||
if(strcmp(cmd9pTmsg[i].name, argv[0]) == 0)
|
||||
break;
|
||||
}
|
||||
if(i == nelem(cmd9pTmsg))
|
||||
return cliError(usage);
|
||||
argc--;
|
||||
argv++;
|
||||
if(cmd9pTmsg[i].argc && argc != cmd9pTmsg[i].argc){
|
||||
vtSetError("usage: %s %s",
|
||||
cmd9pTmsg[i].name, cmd9pTmsg[i].usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
t.type = cmd9pTmsg[i].type;
|
||||
if(t.type == Tversion)
|
||||
t.tag = NOTAG;
|
||||
else
|
||||
t.tag = ++cbox.tag;
|
||||
msize = cbox.con->msize;
|
||||
if(!cmd9pTmsg[i].f(&t, argc, argv))
|
||||
return 0;
|
||||
buf = vtMemAlloc(msize);
|
||||
n = convS2M(&t, buf, msize);
|
||||
if(n <= BIT16SZ){
|
||||
vtSetError("%s: convS2M error", cmd9pTmsg[i].name);
|
||||
vtMemFree(buf);
|
||||
return 0;
|
||||
}
|
||||
if(write(cbox.confd[0], buf, n) != n){
|
||||
vtSetError("%s: write error: %r", cmd9pTmsg[i].name);
|
||||
vtMemFree(buf);
|
||||
return 0;
|
||||
}
|
||||
consPrint("\t-> %F\n", &t);
|
||||
|
||||
if((n = read9pmsg(cbox.confd[0], buf, msize)) <= 0){
|
||||
vtSetError("%s: read error: %r", cmd9pTmsg[i].name);
|
||||
vtMemFree(buf);
|
||||
return 0;
|
||||
}
|
||||
if(convM2S(buf, n, &f) == 0){
|
||||
vtSetError("%s: convM2S error", cmd9pTmsg[i].name);
|
||||
vtMemFree(buf);
|
||||
return 0;
|
||||
}
|
||||
consPrint("\t<- %F\n", &f);
|
||||
|
||||
vtMemFree(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdDot(int argc, char* argv[])
|
||||
{
|
||||
long l;
|
||||
Dir *dir;
|
||||
int fd, r;
|
||||
vlong length;
|
||||
char *f, *p, *s, *usage;
|
||||
|
||||
usage = "usage: . file";
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
}ARGEND
|
||||
if(argc != 1)
|
||||
return cliError(usage);
|
||||
|
||||
if((dir = dirstat(argv[0])) == nil)
|
||||
return cliError(". dirstat %s: %r", argv[0]);
|
||||
length = dir->length;
|
||||
free(dir);
|
||||
|
||||
r = 1;
|
||||
if(length != 0){
|
||||
/*
|
||||
* Read the whole file in.
|
||||
*/
|
||||
if((fd = open(argv[0], OREAD)) < 0)
|
||||
return cliError(". open %s: %r", argv[0]);
|
||||
f = vtMemAlloc(dir->length+1);
|
||||
if((l = read(fd, f, length)) < 0){
|
||||
vtMemFree(f);
|
||||
close(fd);
|
||||
return cliError(". read %s: %r", argv[0]);
|
||||
}
|
||||
close(fd);
|
||||
f[l] = '\0';
|
||||
|
||||
/*
|
||||
* Call cliExec() for each line.
|
||||
*/
|
||||
for(p = s = f; *p != '\0'; p++){
|
||||
if(*p == '\n'){
|
||||
*p = '\0';
|
||||
if(cliExec(s) == 0){
|
||||
r = 0;
|
||||
consPrint("%s: %R\n", s);
|
||||
}
|
||||
s = p+1;
|
||||
}
|
||||
}
|
||||
vtMemFree(f);
|
||||
}
|
||||
|
||||
if(r == 0)
|
||||
vtSetError("errors in . %#q", argv[0]);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdDflag(int argc, char* argv[])
|
||||
{
|
||||
char *usage;
|
||||
|
||||
usage = "usage: dflag";
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
}ARGEND
|
||||
if(argc)
|
||||
return cliError(usage);
|
||||
|
||||
Dflag ^= 1;
|
||||
consPrint("dflag %d\n", Dflag);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdEcho(int argc, char* argv[])
|
||||
{
|
||||
char *usage;
|
||||
int i, nflag;
|
||||
|
||||
nflag = 0;
|
||||
usage = "usage: echo [-n] ...";
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
return cliError(usage);
|
||||
case 'n':
|
||||
nflag = 1;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
for(i = 0; i < argc; i++){
|
||||
if(i != 0)
|
||||
consPrint(" %s", argv[i]);
|
||||
else
|
||||
consPrint(argv[i]);
|
||||
}
|
||||
if(!nflag)
|
||||
consPrint("\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmdBind(int argc, char* argv[])
|
||||
{
|
||||
ulong flag = 0;
|
||||
char *usage;
|
||||
|
||||
usage = "usage: bind [-b|-a|-c|-bc|-ac] new old";
|
||||
|
||||
ARGBEGIN{
|
||||
case 'a':
|
||||
flag |= MAFTER;
|
||||
break;
|
||||
case 'b':
|
||||
flag |= MBEFORE;
|
||||
break;
|
||||
case 'c':
|
||||
flag |= MCREATE;
|
||||
break;
|
||||
default:
|
||||
return cliError(usage);
|
||||
}ARGEND
|
||||
|
||||
if(argc != 2 || (flag&MAFTER)&&(flag&MBEFORE))
|
||||
return cliError(usage);
|
||||
|
||||
if(bind(argv[0], argv[1], flag) < 0){
|
||||
/* try to give a less confusing error than the default */
|
||||
if(access(argv[0], 0) < 0)
|
||||
return cliError("bind: %s: %r", argv[0]);
|
||||
else if(access(argv[1], 0) < 0)
|
||||
return cliError("bind: %s: %r", argv[1]);
|
||||
else
|
||||
return cliError("bind %s %s: %r", argv[0], argv[1]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
cmdInit(void)
|
||||
{
|
||||
cbox.lock = vtLockAlloc();
|
||||
cbox.confd[0] = cbox.confd[1] = -1;
|
||||
|
||||
cliAddCmd(".", cmdDot);
|
||||
cliAddCmd("9p", cmd9p);
|
||||
cliAddCmd("dflag", cmdDflag);
|
||||
cliAddCmd("echo", cmdEcho);
|
||||
cliAddCmd("bind", cmdBind);
|
||||
|
||||
if(pipe(cbox.confd) < 0)
|
||||
return 0;
|
||||
if((cbox.con = conAlloc(cbox.confd[1], "console", 0)) == nil){
|
||||
close(cbox.confd[0]);
|
||||
close(cbox.confd[1]);
|
||||
cbox.confd[0] = cbox.confd[1] = -1;
|
||||
return 0;
|
||||
|
||||
}
|
||||
cbox.con->isconsole = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
398
src/cmd/fossil/Ccons.c
Normal file
398
src/cmd/fossil/Ccons.c
Normal file
@ -0,0 +1,398 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "9.h"
|
||||
|
||||
enum {
|
||||
Nl = 256, /* max. command line length */
|
||||
Nq = 8*1024, /* amount of I/O buffered */
|
||||
};
|
||||
|
||||
typedef struct Q {
|
||||
VtLock* lock;
|
||||
VtRendez* full;
|
||||
VtRendez* empty;
|
||||
|
||||
char q[Nq];
|
||||
int n;
|
||||
int r;
|
||||
int w;
|
||||
} Q;
|
||||
|
||||
typedef struct Cons {
|
||||
VtLock* lock;
|
||||
int ref;
|
||||
int closed;
|
||||
int fd;
|
||||
int srvfd;
|
||||
int ctlfd;
|
||||
Q* iq; /* points to console.iq */
|
||||
Q* oq; /* points to console.oq */
|
||||
} Cons;
|
||||
|
||||
char *currfsysname;
|
||||
|
||||
static struct {
|
||||
Q* iq; /* input */
|
||||
Q* oq; /* output */
|
||||
char l[Nl]; /* command line assembly */
|
||||
int nl; /* current line length */
|
||||
int nopens;
|
||||
|
||||
char* prompt;
|
||||
int np;
|
||||
} console;
|
||||
|
||||
static void
|
||||
consClose(Cons* cons)
|
||||
{
|
||||
vtLock(cons->lock);
|
||||
cons->closed = 1;
|
||||
|
||||
cons->ref--;
|
||||
if(cons->ref > 0){
|
||||
vtLock(cons->iq->lock);
|
||||
vtWakeup(cons->iq->full);
|
||||
vtUnlock(cons->iq->lock);
|
||||
vtLock(cons->oq->lock);
|
||||
vtWakeup(cons->oq->empty);
|
||||
vtUnlock(cons->oq->lock);
|
||||
vtUnlock(cons->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if(cons->ctlfd != -1){
|
||||
close(cons->ctlfd);
|
||||
cons->srvfd = -1;
|
||||
}
|
||||
if(cons->srvfd != -1){
|
||||
close(cons->srvfd);
|
||||
cons->srvfd = -1;
|
||||
}
|
||||
if(cons->fd != -1){
|
||||
close(cons->fd);
|
||||
cons->fd = -1;
|
||||
}
|
||||
vtUnlock(cons->lock);
|
||||
vtLockFree(cons->lock);
|
||||
vtMemFree(cons);
|
||||
console.nopens--;
|
||||
}
|
||||
|
||||
static void
|
||||
consIProc(void* v)
|
||||
{
|
||||
Q *q;
|
||||
Cons *cons;
|
||||
int n, w;
|
||||
char buf[Nq/4];
|
||||
|
||||
vtThreadSetName("consI");
|
||||
|
||||
cons = v;
|
||||
q = cons->iq;
|
||||
for(;;){
|
||||
/*
|
||||
* Can't tell the difference between zero-length read
|
||||
* and eof, so keep calling read until we get an error.
|
||||
*/
|
||||
if(cons->closed || (n = read(cons->fd, buf, Nq/4)) < 0)
|
||||
break;
|
||||
vtLock(q->lock);
|
||||
while(Nq - q->n < n && !cons->closed)
|
||||
vtSleep(q->full);
|
||||
w = Nq - q->w;
|
||||
if(w < n){
|
||||
memmove(&q->q[q->w], buf, w);
|
||||
memmove(&q->q[0], buf + w, n - w);
|
||||
}
|
||||
else
|
||||
memmove(&q->q[q->w], buf, n);
|
||||
q->w = (q->w + n) % Nq;
|
||||
q->n += n;
|
||||
vtWakeup(q->empty);
|
||||
vtUnlock(q->lock);
|
||||
}
|
||||
consClose(cons);
|
||||
}
|
||||
|
||||
static void
|
||||
consOProc(void* v)
|
||||
{
|
||||
Q *q;
|
||||
Cons *cons;
|
||||
char buf[Nq];
|
||||
int lastn, n, r;
|
||||
|
||||
vtThreadSetName("consO");
|
||||
|
||||
cons = v;
|
||||
q = cons->oq;
|
||||
vtLock(q->lock);
|
||||
lastn = 0;
|
||||
for(;;){
|
||||
while(lastn == q->n && !cons->closed)
|
||||
vtSleep(q->empty);
|
||||
if((n = q->n - lastn) > Nq)
|
||||
n = Nq;
|
||||
if(n > q->w){
|
||||
r = n - q->w;
|
||||
memmove(buf, &q->q[Nq - r], r);
|
||||
memmove(buf+r, &q->q[0], n - r);
|
||||
}
|
||||
else
|
||||
memmove(buf, &q->q[q->w - n], n);
|
||||
lastn = q->n;
|
||||
vtUnlock(q->lock);
|
||||
if(cons->closed || write(cons->fd, buf, n) < 0)
|
||||
break;
|
||||
vtLock(q->lock);
|
||||
vtWakeup(q->empty);
|
||||
}
|
||||
consClose(cons);
|
||||
}
|
||||
|
||||
int
|
||||
consOpen(int fd, int srvfd, int ctlfd)
|
||||
{
|
||||
Cons *cons;
|
||||
|
||||
cons = vtMemAllocZ(sizeof(Cons));
|
||||
cons->lock = vtLockAlloc();
|
||||
cons->fd = fd;
|
||||
cons->srvfd = srvfd;
|
||||
cons->ctlfd = ctlfd;
|
||||
cons->iq = console.iq;
|
||||
cons->oq = console.oq;
|
||||
console.nopens++;
|
||||
|
||||
vtLock(cons->lock);
|
||||
cons->ref = 2;
|
||||
cons->closed = 0;
|
||||
if(vtThread(consOProc, cons) < 0){
|
||||
cons->ref--;
|
||||
vtUnlock(cons->lock);
|
||||
consClose(cons);
|
||||
return 0;
|
||||
}
|
||||
vtUnlock(cons->lock);
|
||||
|
||||
if(ctlfd >= 0)
|
||||
consIProc(cons);
|
||||
else if(vtThread(consIProc, cons) < 0){
|
||||
consClose(cons);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
qWrite(Q* q, char* p, int n)
|
||||
{
|
||||
int w;
|
||||
|
||||
vtLock(q->lock);
|
||||
if(n > Nq - q->w){
|
||||
w = Nq - q->w;
|
||||
memmove(&q->q[q->w], p, w);
|
||||
memmove(&q->q[0], p + w, n - w);
|
||||
q->w = n - w;
|
||||
}
|
||||
else{
|
||||
memmove(&q->q[q->w], p, n);
|
||||
q->w += n;
|
||||
}
|
||||
q->n += n;
|
||||
vtWakeup(q->empty);
|
||||
vtUnlock(q->lock);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static Q*
|
||||
qAlloc(void)
|
||||
{
|
||||
Q *q;
|
||||
|
||||
q = vtMemAllocZ(sizeof(Q));
|
||||
q->lock = vtLockAlloc();
|
||||
q->full = vtRendezAlloc(q->lock);
|
||||
q->empty = vtRendezAlloc(q->lock);
|
||||
q->n = q->r = q->w = 0;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static void
|
||||
consProc(void*)
|
||||
{
|
||||
Q *q;
|
||||
int argc, i, n, r;
|
||||
char *argv[20], buf[Nq], *lp, *wbuf;
|
||||
char procname[64];
|
||||
|
||||
snprint(procname, sizeof procname, "cons %s", currfsysname);
|
||||
vtThreadSetName(procname);
|
||||
|
||||
q = console.iq;
|
||||
qWrite(console.oq, console.prompt, console.np);
|
||||
vtLock(q->lock);
|
||||
for(;;){
|
||||
while((n = q->n) == 0)
|
||||
vtSleep(q->empty);
|
||||
r = Nq - q->r;
|
||||
if(r < n){
|
||||
memmove(buf, &q->q[q->r], r);
|
||||
memmove(buf + r, &q->q[0], n - r);
|
||||
}
|
||||
else
|
||||
memmove(buf, &q->q[q->r], n);
|
||||
q->r = (q->r + n) % Nq;
|
||||
q->n -= n;
|
||||
vtWakeup(q->full);
|
||||
vtUnlock(q->lock);
|
||||
|
||||
for(i = 0; i < n; i++){
|
||||
switch(buf[i]){
|
||||
case '\004': /* ^D */
|
||||
if(console.nl == 0){
|
||||
qWrite(console.oq, "\n", 1);
|
||||
break;
|
||||
}
|
||||
/*FALLTHROUGH*/
|
||||
default:
|
||||
if(console.nl < Nl-1){
|
||||
qWrite(console.oq, &buf[i], 1);
|
||||
console.l[console.nl++] = buf[i];
|
||||
}
|
||||
continue;
|
||||
case '\b':
|
||||
if(console.nl != 0){
|
||||
qWrite(console.oq, &buf[i], 1);
|
||||
console.nl--;
|
||||
}
|
||||
continue;
|
||||
case '\n':
|
||||
qWrite(console.oq, &buf[i], 1);
|
||||
break;
|
||||
case '\025': /* ^U */
|
||||
qWrite(console.oq, "^U\n", 3);
|
||||
console.nl = 0;
|
||||
break;
|
||||
case '\027': /* ^W */
|
||||
console.l[console.nl] = '\0';
|
||||
wbuf = vtMemAlloc(console.nl+1);
|
||||
memmove(wbuf, console.l, console.nl+1);
|
||||
argc = tokenize(wbuf, argv, nelem(argv));
|
||||
if(argc > 0)
|
||||
argc--;
|
||||
console.nl = 0;
|
||||
lp = console.l;
|
||||
for(i = 0; i < argc; i++)
|
||||
lp += sprint(lp, "%q ", argv[i]);
|
||||
console.nl = lp - console.l;
|
||||
vtMemFree(wbuf);
|
||||
qWrite(console.oq, "^W\n", 3);
|
||||
if(console.nl == 0)
|
||||
break;
|
||||
qWrite(console.oq, console.l, console.nl);
|
||||
continue;
|
||||
case '\177':
|
||||
qWrite(console.oq, "\n", 1);
|
||||
console.nl = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
console.l[console.nl] = '\0';
|
||||
if(console.nl != 0)
|
||||
cliExec(console.l);
|
||||
|
||||
console.nl = 0;
|
||||
qWrite(console.oq, console.prompt, console.np);
|
||||
}
|
||||
|
||||
vtLock(q->lock);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
consWrite(char* buf, int len)
|
||||
{
|
||||
if(console.oq == nil)
|
||||
return write(2, buf, len);
|
||||
if(console.nopens == 0)
|
||||
write(2, buf, len);
|
||||
return qWrite(console.oq, buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
consPrompt(char* prompt)
|
||||
{
|
||||
char buf[ERRMAX];
|
||||
|
||||
if(prompt == nil)
|
||||
prompt = "prompt";
|
||||
|
||||
vtMemFree(console.prompt);
|
||||
console.np = snprint(buf, sizeof(buf), "%s: ", prompt);
|
||||
console.prompt = vtStrDup(buf);
|
||||
|
||||
return console.np;
|
||||
}
|
||||
|
||||
int
|
||||
consTTY(void)
|
||||
{
|
||||
int ctl, fd;
|
||||
char *name, *p;
|
||||
|
||||
name = "/dev/cons";
|
||||
if((fd = open(name, ORDWR)) < 0){
|
||||
name = "#c/cons";
|
||||
if((fd = open(name, ORDWR)) < 0){
|
||||
vtSetError("consTTY: open %s: %r", name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
p = smprint("%sctl", name);
|
||||
if((ctl = open(p, OWRITE)) < 0){
|
||||
close(fd);
|
||||
vtSetError("consTTY: open %s: %r", p);
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
||||
if(write(ctl, "rawon", 5) < 0){
|
||||
close(ctl);
|
||||
close(fd);
|
||||
vtSetError("consTTY: write %s: %r", p);
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
||||
free(p);
|
||||
|
||||
if(consOpen(fd, fd, ctl) == 0){
|
||||
close(ctl);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
consInit(void)
|
||||
{
|
||||
console.iq = qAlloc();
|
||||
console.oq = qAlloc();
|
||||
console.nl = 0;
|
||||
|
||||
consPrompt(nil);
|
||||
|
||||
if(vtThread(consProc, nil) < 0){
|
||||
vtFatal("can't start console proc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
40
src/cmd/fossil/Clog.c
Normal file
40
src/cmd/fossil/Clog.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include "stdinc.h"
|
||||
#include "9.h"
|
||||
|
||||
/*
|
||||
* To do: This will become something else ('vprint'?).
|
||||
*/
|
||||
int
|
||||
consVPrint(char* fmt, va_list args)
|
||||
{
|
||||
int len, ret;
|
||||
char buf[256];
|
||||
|
||||
len = vsnprint(buf, sizeof(buf), fmt, args);
|
||||
ret = consWrite(buf, len);
|
||||
|
||||
while (len-- > 0 && buf[len] == '\n')
|
||||
buf[len] = '\0';
|
||||
/*
|
||||
* if we do this, checking the root fossil (if /sys/log/fossil is there)
|
||||
* will spew all over the console.
|
||||
*/
|
||||
if (0)
|
||||
syslog(0, "fossil", "%s", buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* To do: This will become 'print'.
|
||||
*/
|
||||
int
|
||||
consPrint(char* fmt, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = consVPrint(fmt, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
466
src/cmd/fossil/archive.c
Normal file
466
src/cmd/fossil/archive.c
Normal file
@ -0,0 +1,466 @@
|
||||
/*
|
||||
* Archiver. In charge of sending blocks to Venti.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
#include "9.h" /* for consPrint */
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
static void archThread(void*);
|
||||
|
||||
struct Arch
|
||||
{
|
||||
int ref;
|
||||
uint blockSize;
|
||||
uint diskSize;
|
||||
Cache *c;
|
||||
Fs *fs;
|
||||
VtSession *z;
|
||||
|
||||
VtLock *lk;
|
||||
VtRendez *starve;
|
||||
VtRendez *die;
|
||||
};
|
||||
|
||||
Arch *
|
||||
archInit(Cache *c, Disk *disk, Fs *fs, VtSession *z)
|
||||
{
|
||||
Arch *a;
|
||||
|
||||
a = vtMemAllocZ(sizeof(Arch));
|
||||
|
||||
a->c = c;
|
||||
a->z = z;
|
||||
a->fs = fs;
|
||||
a->blockSize = diskBlockSize(disk);
|
||||
a->lk = vtLockAlloc();
|
||||
a->starve = vtRendezAlloc(a->lk);
|
||||
|
||||
a->ref = 2;
|
||||
vtThread(archThread, a);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void
|
||||
archFree(Arch *a)
|
||||
{
|
||||
/* kill slave */
|
||||
vtLock(a->lk);
|
||||
a->die = vtRendezAlloc(a->lk);
|
||||
vtWakeup(a->starve);
|
||||
while(a->ref > 1)
|
||||
vtSleep(a->die);
|
||||
vtUnlock(a->lk);
|
||||
vtRendezFree(a->starve);
|
||||
vtRendezFree(a->die);
|
||||
vtLockFree(a->lk);
|
||||
vtMemFree(a);
|
||||
}
|
||||
|
||||
static int
|
||||
ventiSend(Arch *a, Block *b, uchar *data)
|
||||
{
|
||||
uint n;
|
||||
uchar score[VtScoreSize];
|
||||
|
||||
if(DEBUG > 1)
|
||||
fprint(2, "ventiSend: sending %#ux %L to venti\n", b->addr, &b->l);
|
||||
n = vtZeroTruncate(vtType[b->l.type], data, a->blockSize);
|
||||
if(DEBUG > 1)
|
||||
fprint(2, "ventiSend: truncate %d to %d\n", a->blockSize, n);
|
||||
if(!vtWrite(a->z, score, vtType[b->l.type], data, n)){
|
||||
fprint(2, "ventiSend: vtWrite block %#ux failed: %R\n", b->addr);
|
||||
return 0;
|
||||
}
|
||||
if(!vtSha1Check(score, data, n)){
|
||||
uchar score2[VtScoreSize];
|
||||
vtSha1(score2, data, n);
|
||||
fprint(2, "ventiSend: vtWrite block %#ux failed vtSha1Check %V %V\n",
|
||||
b->addr, score, score2);
|
||||
return 0;
|
||||
}
|
||||
if(!vtSync(a->z))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* parameters for recursion; there are so many,
|
||||
* and some only change occasionally. this is
|
||||
* easier than spelling things out at each call.
|
||||
*/
|
||||
typedef struct Param Param;
|
||||
struct Param
|
||||
{
|
||||
/* these never change */
|
||||
uint snapEpoch; /* epoch for snapshot being archived */
|
||||
uint blockSize;
|
||||
Cache *c;
|
||||
Arch *a;
|
||||
|
||||
/* changes on every call */
|
||||
uint depth;
|
||||
|
||||
/* statistics */
|
||||
uint nfixed;
|
||||
uint nsend;
|
||||
uint nvisit;
|
||||
uint nfailsend;
|
||||
uint maxdepth;
|
||||
uint nreclaim;
|
||||
uint nfake;
|
||||
uint nreal;
|
||||
|
||||
/* these occasionally change (must save old values and put back) */
|
||||
uint dsize;
|
||||
uint psize;
|
||||
|
||||
/* return value; avoids using stack space */
|
||||
Label l;
|
||||
uchar score[VtScoreSize];
|
||||
};
|
||||
|
||||
static void
|
||||
shaBlock(uchar score[VtScoreSize], Block *b, uchar *data, uint bsize)
|
||||
{
|
||||
vtSha1(score, data, vtZeroTruncate(vtType[b->l.type], data, bsize));
|
||||
}
|
||||
|
||||
static uint
|
||||
etype(Entry *e)
|
||||
{
|
||||
uint t;
|
||||
|
||||
if(e->flags&VtEntryDir)
|
||||
t = BtDir;
|
||||
else
|
||||
t = BtData;
|
||||
return t+e->depth;
|
||||
}
|
||||
|
||||
static uchar*
|
||||
copyBlock(Block *b, u32int blockSize)
|
||||
{
|
||||
uchar *data;
|
||||
|
||||
data = vtMemAlloc(blockSize);
|
||||
if(data == nil)
|
||||
return nil;
|
||||
memmove(data, b->data, blockSize);
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk over the block tree, archiving it to Venti.
|
||||
*
|
||||
* We don't archive the snapshots. Instead we zero the
|
||||
* entries in a temporary copy of the block and archive that.
|
||||
*
|
||||
* Return value is:
|
||||
*
|
||||
* ArchFailure some error occurred
|
||||
* ArchSuccess block and all children archived
|
||||
* ArchFaked success, but block or children got copied
|
||||
*/
|
||||
enum
|
||||
{
|
||||
ArchFailure,
|
||||
ArchSuccess,
|
||||
ArchFaked,
|
||||
};
|
||||
static int
|
||||
archWalk(Param *p, u32int addr, uchar type, u32int tag)
|
||||
{
|
||||
int ret, i, x, psize, dsize;
|
||||
uchar *data, score[VtScoreSize];
|
||||
Block *b;
|
||||
Label l;
|
||||
Entry *e;
|
||||
WalkPtr w;
|
||||
|
||||
p->nvisit++;
|
||||
|
||||
b = cacheLocalData(p->c, addr, type, tag, OReadWrite,0);
|
||||
if(b == nil){
|
||||
fprint(2, "archive(%ud, %#ux): cannot find block: %R\n", p->snapEpoch, addr);
|
||||
if(strcmp(vtGetError(), ELabelMismatch) == 0){
|
||||
/* might as well plod on so we write _something_ to Venti */
|
||||
memmove(p->score, vtZeroScore, VtScoreSize);
|
||||
return ArchFaked;
|
||||
}
|
||||
return ArchFailure;
|
||||
}
|
||||
|
||||
if(DEBUG) fprint(2, "%*sarchive(%ud, %#ux): block label %L\n",
|
||||
p->depth*2, "", p->snapEpoch, b->addr, &b->l);
|
||||
p->depth++;
|
||||
if(p->depth > p->maxdepth)
|
||||
p->maxdepth = p->depth;
|
||||
|
||||
data = b->data;
|
||||
if((b->l.state&BsVenti) == 0){
|
||||
initWalk(&w, b, b->l.type==BtDir ? p->dsize : p->psize);
|
||||
for(i=0; nextWalk(&w, score, &type, &tag, &e); i++){
|
||||
if(e){
|
||||
if(!(e->flags&VtEntryActive))
|
||||
continue;
|
||||
if((e->snap && !e->archive)
|
||||
|| (e->flags&VtEntryNoArchive)){
|
||||
if(0) fprint(2, "snap; faking %#ux\n", b->addr);
|
||||
if(data == b->data){
|
||||
data = copyBlock(b, p->blockSize);
|
||||
if(data == nil){
|
||||
ret = ArchFailure;
|
||||
goto Out;
|
||||
}
|
||||
w.data = data;
|
||||
}
|
||||
memmove(e->score, vtZeroScore, VtScoreSize);
|
||||
e->depth = 0;
|
||||
e->size = 0;
|
||||
e->tag = 0;
|
||||
e->flags &= ~VtEntryLocal;
|
||||
entryPack(e, data, w.n-1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
addr = globalToLocal(score);
|
||||
if(addr == NilBlock)
|
||||
continue;
|
||||
dsize = p->dsize;
|
||||
psize = p->psize;
|
||||
if(e){
|
||||
p->dsize= e->dsize;
|
||||
p->psize = e->psize;
|
||||
}
|
||||
vtUnlock(b->lk);
|
||||
x = archWalk(p, addr, type, tag);
|
||||
vtLock(b->lk);
|
||||
if(e){
|
||||
p->dsize = dsize;
|
||||
p->psize = psize;
|
||||
}
|
||||
while(b->iostate != BioClean && b->iostate != BioDirty)
|
||||
vtSleep(b->ioready);
|
||||
switch(x){
|
||||
case ArchFailure:
|
||||
fprint(2, "archWalk %#ux failed; ptr is in %#ux offset %d\n",
|
||||
addr, b->addr, i);
|
||||
ret = ArchFailure;
|
||||
goto Out;
|
||||
case ArchFaked:
|
||||
/*
|
||||
* When we're writing the entry for an archive directory
|
||||
* (like /archive/2003/1215) then even if we've faked
|
||||
* any data, record the score unconditionally.
|
||||
* This way, we will always record the Venti score here.
|
||||
* Otherwise, temporary data or corrupted file system
|
||||
* would cause us to keep holding onto the on-disk
|
||||
* copy of the archive.
|
||||
*/
|
||||
if(e==nil || !e->archive)
|
||||
if(data == b->data){
|
||||
if(0) fprint(2, "faked %#ux, faking %#ux (%V)\n", addr, b->addr, p->score);
|
||||
data = copyBlock(b, p->blockSize);
|
||||
if(data == nil){
|
||||
ret = ArchFailure;
|
||||
goto Out;
|
||||
}
|
||||
w.data = data;
|
||||
}
|
||||
/* fall through */
|
||||
if(0) fprint(2, "falling\n");
|
||||
case ArchSuccess:
|
||||
if(e){
|
||||
memmove(e->score, p->score, VtScoreSize);
|
||||
e->flags &= ~VtEntryLocal;
|
||||
entryPack(e, data, w.n-1);
|
||||
}else
|
||||
memmove(data+(w.n-1)*VtScoreSize, p->score, VtScoreSize);
|
||||
if(data == b->data){
|
||||
blockDirty(b);
|
||||
/*
|
||||
* If b is in the active tree, then we need to note that we've
|
||||
* just removed addr from the active tree (replacing it with the
|
||||
* copy we just stored to Venti). If addr is in other snapshots,
|
||||
* this will close addr but not free it, since it has a non-empty
|
||||
* epoch range.
|
||||
*
|
||||
* If b is in the active tree but has been copied (this can happen
|
||||
* if we get killed at just the right moment), then we will
|
||||
* mistakenly leak its kids.
|
||||
*
|
||||
* The children of an archive directory (e.g., /archive/2004/0604)
|
||||
* are not treated as in the active tree.
|
||||
*/
|
||||
if((b->l.state&BsCopied)==0 && (e==nil || e->snap==0))
|
||||
blockRemoveLink(b, addr, p->l.type, p->l.tag, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ventiSend(p->a, b, data)){
|
||||
p->nfailsend++;
|
||||
ret = ArchFailure;
|
||||
goto Out;
|
||||
}
|
||||
p->nsend++;
|
||||
if(data != b->data)
|
||||
p->nfake++;
|
||||
if(data == b->data){ /* not faking it, so update state */
|
||||
p->nreal++;
|
||||
l = b->l;
|
||||
l.state |= BsVenti;
|
||||
if(!blockSetLabel(b, &l, 0)){
|
||||
ret = ArchFailure;
|
||||
goto Out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shaBlock(p->score, b, data, p->blockSize);
|
||||
if(0) fprint(2, "ventisend %V %p %p %p\n", p->score, data, b->data, w.data);
|
||||
ret = data!=b->data ? ArchFaked : ArchSuccess;
|
||||
p->l = b->l;
|
||||
Out:
|
||||
if(data != b->data)
|
||||
vtMemFree(data);
|
||||
p->depth--;
|
||||
blockPut(b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
archThread(void *v)
|
||||
{
|
||||
Arch *a = v;
|
||||
Block *b;
|
||||
Param p;
|
||||
Super super;
|
||||
int ret;
|
||||
u32int addr;
|
||||
uchar rbuf[VtRootSize];
|
||||
VtRoot root;
|
||||
|
||||
vtThreadSetName("arch");
|
||||
|
||||
for(;;){
|
||||
/* look for work */
|
||||
vtLock(a->fs->elk);
|
||||
b = superGet(a->c, &super);
|
||||
if(b == nil){
|
||||
vtUnlock(a->fs->elk);
|
||||
fprint(2, "archThread: superGet: %R\n");
|
||||
sleep(60*1000);
|
||||
continue;
|
||||
}
|
||||
addr = super.next;
|
||||
if(addr != NilBlock && super.current == NilBlock){
|
||||
super.current = addr;
|
||||
super.next = NilBlock;
|
||||
superPack(&super, b->data);
|
||||
blockDirty(b);
|
||||
}else
|
||||
addr = super.current;
|
||||
blockPut(b);
|
||||
vtUnlock(a->fs->elk);
|
||||
|
||||
if(addr == NilBlock){
|
||||
/* wait for work */
|
||||
vtLock(a->lk);
|
||||
vtSleep(a->starve);
|
||||
if(a->die != nil)
|
||||
goto Done;
|
||||
vtUnlock(a->lk);
|
||||
continue;
|
||||
}
|
||||
|
||||
sleep(10*1000); /* window of opportunity to provoke races */
|
||||
|
||||
/* do work */
|
||||
memset(&p, 0, sizeof p);
|
||||
p.blockSize = a->blockSize;
|
||||
p.dsize = 3*VtEntrySize; /* root has three Entries */
|
||||
p.c = a->c;
|
||||
p.a = a;
|
||||
|
||||
ret = archWalk(&p, addr, BtDir, RootTag);
|
||||
switch(ret){
|
||||
default:
|
||||
abort();
|
||||
case ArchFailure:
|
||||
fprint(2, "archiveBlock %#ux: %R\n", addr);
|
||||
sleep(60*1000);
|
||||
continue;
|
||||
case ArchSuccess:
|
||||
case ArchFaked:
|
||||
break;
|
||||
}
|
||||
|
||||
if(0) fprint(2, "archiveSnapshot 0x%#ux: maxdepth %ud nfixed %ud"
|
||||
" send %ud nfailsend %ud nvisit %ud"
|
||||
" nreclaim %ud nfake %ud nreal %ud\n",
|
||||
addr, p.maxdepth, p.nfixed,
|
||||
p.nsend, p.nfailsend, p.nvisit,
|
||||
p.nreclaim, p.nfake, p.nreal);
|
||||
if(0) fprint(2, "archiveBlock %V (%ud)\n", p.score, p.blockSize);
|
||||
|
||||
/* tie up vac root */
|
||||
memset(&root, 0, sizeof root);
|
||||
root.version = VtRootVersion;
|
||||
strecpy(root.type, root.type+sizeof root.type, "vac");
|
||||
strecpy(root.name, root.name+sizeof root.name, "fossil");
|
||||
memmove(root.score, p.score, VtScoreSize);
|
||||
memmove(root.prev, super.last, VtScoreSize);
|
||||
root.blockSize = a->blockSize;
|
||||
vtRootPack(&root, rbuf);
|
||||
if(!vtWrite(a->z, p.score, VtRootType, rbuf, VtRootSize)
|
||||
|| !vtSha1Check(p.score, rbuf, VtRootSize)){
|
||||
fprint(2, "vtWriteBlock %#ux: %R\n", addr);
|
||||
sleep(60*1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* record success */
|
||||
vtLock(a->fs->elk);
|
||||
b = superGet(a->c, &super);
|
||||
if(b == nil){
|
||||
vtUnlock(a->fs->elk);
|
||||
fprint(2, "archThread: superGet: %R\n");
|
||||
sleep(60*1000);
|
||||
continue;
|
||||
}
|
||||
super.current = NilBlock;
|
||||
memmove(super.last, p.score, VtScoreSize);
|
||||
superPack(&super, b->data);
|
||||
blockDirty(b);
|
||||
blockPut(b);
|
||||
vtUnlock(a->fs->elk);
|
||||
|
||||
consPrint("archive vac:%V\n", p.score);
|
||||
}
|
||||
|
||||
Done:
|
||||
a->ref--;
|
||||
vtWakeup(a->die);
|
||||
vtUnlock(a->lk);
|
||||
}
|
||||
|
||||
void
|
||||
archKick(Arch *a)
|
||||
{
|
||||
if(a == nil){
|
||||
fprint(2, "warning: archKick nil\n");
|
||||
return;
|
||||
}
|
||||
vtLock(a->lk);
|
||||
vtWakeup(a->starve);
|
||||
vtUnlock(a->lk);
|
||||
}
|
||||
421
src/cmd/fossil/bwatch.c
Normal file
421
src/cmd/fossil/bwatch.c
Normal file
@ -0,0 +1,421 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
/*
|
||||
* Lock watcher. Check that locking of blocks is always down.
|
||||
*
|
||||
* This is REALLY slow, and it won't work when the blocks aren't
|
||||
* arranged in a tree (e.g., after the first snapshot). But it's great
|
||||
* for debugging.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
MaxLock = 16,
|
||||
HashSize = 1009,
|
||||
};
|
||||
|
||||
/*
|
||||
* Thread-specific watch state.
|
||||
*/
|
||||
typedef struct WThread WThread;
|
||||
struct WThread
|
||||
{
|
||||
Block *b[MaxLock]; /* blocks currently held */
|
||||
uint nb;
|
||||
uint pid;
|
||||
};
|
||||
|
||||
typedef struct WMap WMap;
|
||||
typedef struct WEntry WEntry;
|
||||
|
||||
struct WEntry
|
||||
{
|
||||
uchar c[VtScoreSize];
|
||||
uchar p[VtScoreSize];
|
||||
int off;
|
||||
|
||||
WEntry *cprev;
|
||||
WEntry *cnext;
|
||||
WEntry *pprev;
|
||||
WEntry *pnext;
|
||||
};
|
||||
|
||||
struct WMap
|
||||
{
|
||||
VtLock *lk;
|
||||
|
||||
WEntry *hchild[HashSize];
|
||||
WEntry *hparent[HashSize];
|
||||
};
|
||||
|
||||
static WMap map;
|
||||
static void **wp;
|
||||
static uint blockSize;
|
||||
static WEntry *pool;
|
||||
uint bwatchDisabled;
|
||||
|
||||
static uint
|
||||
hash(uchar score[VtScoreSize])
|
||||
{
|
||||
uint i, h;
|
||||
|
||||
h = 0;
|
||||
for(i=0; i<VtScoreSize; i++)
|
||||
h = h*37 + score[i];
|
||||
return h%HashSize;
|
||||
}
|
||||
|
||||
#include <pool.h>
|
||||
static void
|
||||
freeWEntry(WEntry *e)
|
||||
{
|
||||
memset(e, 0, sizeof(WEntry));
|
||||
e->pnext = pool;
|
||||
pool = e;
|
||||
}
|
||||
|
||||
static WEntry*
|
||||
allocWEntry(void)
|
||||
{
|
||||
int i;
|
||||
WEntry *w;
|
||||
|
||||
w = pool;
|
||||
if(w == nil){
|
||||
w = vtMemAllocZ(1024*sizeof(WEntry));
|
||||
for(i=0; i<1024; i++)
|
||||
freeWEntry(&w[i]);
|
||||
w = pool;
|
||||
}
|
||||
pool = w->pnext;
|
||||
memset(w, 0, sizeof(WEntry));
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove all dependencies with score as a parent
|
||||
*/
|
||||
static void
|
||||
_bwatchResetParent(uchar *score)
|
||||
{
|
||||
WEntry *w, *next;
|
||||
uint h;
|
||||
|
||||
h = hash(score);
|
||||
for(w=map.hparent[h]; w; w=next){
|
||||
next = w->pnext;
|
||||
if(memcmp(w->p, score, VtScoreSize) == 0){
|
||||
if(w->pnext)
|
||||
w->pnext->pprev = w->pprev;
|
||||
if(w->pprev)
|
||||
w->pprev->pnext = w->pnext;
|
||||
else
|
||||
map.hparent[h] = w->pnext;
|
||||
if(w->cnext)
|
||||
w->cnext->cprev = w->cprev;
|
||||
if(w->cprev)
|
||||
w->cprev->cnext = w->cnext;
|
||||
else
|
||||
map.hchild[hash(w->c)] = w->cnext;
|
||||
freeWEntry(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* and child
|
||||
*/
|
||||
static void
|
||||
_bwatchResetChild(uchar *score)
|
||||
{
|
||||
WEntry *w, *next;
|
||||
uint h;
|
||||
|
||||
h = hash(score);
|
||||
for(w=map.hchild[h]; w; w=next){
|
||||
next = w->cnext;
|
||||
if(memcmp(w->c, score, VtScoreSize) == 0){
|
||||
if(w->pnext)
|
||||
w->pnext->pprev = w->pprev;
|
||||
if(w->pprev)
|
||||
w->pprev->pnext = w->pnext;
|
||||
else
|
||||
map.hparent[hash(w->p)] = w->pnext;
|
||||
if(w->cnext)
|
||||
w->cnext->cprev = w->cprev;
|
||||
if(w->cprev)
|
||||
w->cprev->cnext = w->cnext;
|
||||
else
|
||||
map.hchild[h] = w->cnext;
|
||||
freeWEntry(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uchar*
|
||||
parent(uchar c[VtScoreSize], int *off)
|
||||
{
|
||||
WEntry *w;
|
||||
uint h;
|
||||
|
||||
h = hash(c);
|
||||
for(w=map.hchild[h]; w; w=w->cnext)
|
||||
if(memcmp(w->c, c, VtScoreSize) == 0){
|
||||
*off = w->off;
|
||||
return w->p;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
addChild(uchar p[VtEntrySize], uchar c[VtEntrySize], int off)
|
||||
{
|
||||
uint h;
|
||||
WEntry *w;
|
||||
|
||||
w = allocWEntry();
|
||||
memmove(w->p, p, VtScoreSize);
|
||||
memmove(w->c, c, VtScoreSize);
|
||||
w->off = off;
|
||||
|
||||
h = hash(p);
|
||||
w->pnext = map.hparent[h];
|
||||
if(w->pnext)
|
||||
w->pnext->pprev = w;
|
||||
map.hparent[h] = w;
|
||||
|
||||
h = hash(c);
|
||||
w->cnext = map.hchild[h];
|
||||
if(w->cnext)
|
||||
w->cnext->cprev = w;
|
||||
map.hchild[h] = w;
|
||||
}
|
||||
|
||||
void
|
||||
bwatchReset(uchar score[VtScoreSize])
|
||||
{
|
||||
vtLock(map.lk);
|
||||
_bwatchResetParent(score);
|
||||
_bwatchResetChild(score);
|
||||
vtUnlock(map.lk);
|
||||
}
|
||||
|
||||
void
|
||||
bwatchInit(void)
|
||||
{
|
||||
map.lk = vtLockAlloc();
|
||||
wp = privalloc();
|
||||
*wp = nil;
|
||||
}
|
||||
|
||||
void
|
||||
bwatchSetBlockSize(uint bs)
|
||||
{
|
||||
blockSize = bs;
|
||||
}
|
||||
|
||||
static WThread*
|
||||
getWThread(void)
|
||||
{
|
||||
WThread *w;
|
||||
|
||||
w = *wp;
|
||||
if(w == nil || w->pid != getpid()){
|
||||
w = vtMemAllocZ(sizeof(WThread));
|
||||
*wp = w;
|
||||
w->pid = getpid();
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
* Derive dependencies from the contents of b.
|
||||
*/
|
||||
void
|
||||
bwatchDependency(Block *b)
|
||||
{
|
||||
int i, epb, ppb;
|
||||
Entry e;
|
||||
|
||||
if(bwatchDisabled)
|
||||
return;
|
||||
|
||||
vtLock(map.lk);
|
||||
_bwatchResetParent(b->score);
|
||||
|
||||
switch(b->l.type){
|
||||
case BtData:
|
||||
break;
|
||||
|
||||
case BtDir:
|
||||
epb = blockSize / VtEntrySize;
|
||||
for(i=0; i<epb; i++){
|
||||
entryUnpack(&e, b->data, i);
|
||||
if(!(e.flags & VtEntryActive))
|
||||
continue;
|
||||
addChild(b->score, e.score, i);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ppb = blockSize / VtScoreSize;
|
||||
for(i=0; i<ppb; i++)
|
||||
addChild(b->score, b->data+i*VtScoreSize, i);
|
||||
break;
|
||||
}
|
||||
vtUnlock(map.lk);
|
||||
}
|
||||
|
||||
static int
|
||||
depth(uchar *s)
|
||||
{
|
||||
int d, x;
|
||||
|
||||
d = -1;
|
||||
while(s){
|
||||
d++;
|
||||
s = parent(s, &x);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static int
|
||||
lockConflicts(uchar xhave[VtScoreSize], uchar xwant[VtScoreSize])
|
||||
{
|
||||
uchar *have, *want;
|
||||
int havedepth, wantdepth, havepos, wantpos;
|
||||
|
||||
have = xhave;
|
||||
want = xwant;
|
||||
|
||||
havedepth = depth(have);
|
||||
wantdepth = depth(want);
|
||||
|
||||
/*
|
||||
* walk one or the other up until they're both
|
||||
* at the same level.
|
||||
*/
|
||||
havepos = -1;
|
||||
wantpos = -1;
|
||||
have = xhave;
|
||||
want = xwant;
|
||||
while(wantdepth > havedepth){
|
||||
wantdepth--;
|
||||
want = parent(want, &wantpos);
|
||||
}
|
||||
while(havedepth > wantdepth){
|
||||
havedepth--;
|
||||
have = parent(have, &havepos);
|
||||
}
|
||||
|
||||
/*
|
||||
* walk them up simultaneously until we reach
|
||||
* a common ancestor.
|
||||
*/
|
||||
while(have && want && memcmp(have, want, VtScoreSize) != 0){
|
||||
have = parent(have, &havepos);
|
||||
want = parent(want, &wantpos);
|
||||
}
|
||||
|
||||
/*
|
||||
* not part of same tree. happens mainly with
|
||||
* newly allocated blocks.
|
||||
*/
|
||||
if(!have || !want)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* never walked want: means we want to lock
|
||||
* an ancestor of have. no no.
|
||||
*/
|
||||
if(wantpos == -1)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* never walked have: means we want to lock a
|
||||
* child of have. that's okay.
|
||||
*/
|
||||
if(havepos == -1)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* walked both: they're from different places in the tree.
|
||||
* require that the left one be locked before the right one.
|
||||
* (this is questionable, but it puts a total order on the block tree).
|
||||
*/
|
||||
return havepos < wantpos;
|
||||
}
|
||||
|
||||
static void
|
||||
stop(void)
|
||||
{
|
||||
int fd;
|
||||
char buf[32];
|
||||
|
||||
snprint(buf, sizeof buf, "#p/%d/ctl", getpid());
|
||||
fd = open(buf, OWRITE);
|
||||
write(fd, "stop", 4);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the calling thread can validly lock b.
|
||||
* That is, check that the calling thread doesn't hold
|
||||
* locks for any of b's children.
|
||||
*/
|
||||
void
|
||||
bwatchLock(Block *b)
|
||||
{
|
||||
int i;
|
||||
WThread *w;
|
||||
|
||||
if(bwatchDisabled)
|
||||
return;
|
||||
|
||||
if(b->part != PartData)
|
||||
return;
|
||||
|
||||
vtLock(map.lk);
|
||||
w = getWThread();
|
||||
for(i=0; i<w->nb; i++){
|
||||
if(lockConflicts(w->b[i]->score, b->score)){
|
||||
fprint(2, "%d: have block %V; shouldn't lock %V\n",
|
||||
w->pid, w->b[i]->score, b->score);
|
||||
stop();
|
||||
}
|
||||
}
|
||||
vtUnlock(map.lk);
|
||||
if(w->nb >= MaxLock){
|
||||
fprint(2, "%d: too many blocks held\n", w->pid);
|
||||
stop();
|
||||
}else
|
||||
w->b[w->nb++] = b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that the calling thread is about to unlock b.
|
||||
*/
|
||||
void
|
||||
bwatchUnlock(Block *b)
|
||||
{
|
||||
int i;
|
||||
WThread *w;
|
||||
|
||||
if(bwatchDisabled)
|
||||
return;
|
||||
|
||||
if(b->part != PartData)
|
||||
return;
|
||||
|
||||
w = getWThread();
|
||||
for(i=0; i<w->nb; i++)
|
||||
if(w->b[i] == b)
|
||||
break;
|
||||
if(i>=w->nb){
|
||||
fprint(2, "%d: unlock of unlocked block %V\n", w->pid, b->score);
|
||||
stop();
|
||||
}else
|
||||
w->b[i] = w->b[--w->nb];
|
||||
}
|
||||
|
||||
2114
src/cmd/fossil/cache.c
Normal file
2114
src/cmd/fossil/cache.c
Normal file
File diff suppressed because it is too large
Load Diff
799
src/cmd/fossil/check.c
Normal file
799
src/cmd/fossil/check.c
Normal file
@ -0,0 +1,799 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
static void checkDirs(Fsck*);
|
||||
static void checkEpochs(Fsck*);
|
||||
static void checkLeak(Fsck*);
|
||||
static void closenop(Fsck*, Block*, u32int);
|
||||
static void clrenop(Fsck*, Block*, int);
|
||||
static void clrinop(Fsck*, char*, MetaBlock*, int, Block*);
|
||||
static void error(Fsck*, char*, ...);
|
||||
static int getBit(uchar*, u32int);
|
||||
static int printnop(char*, ...);
|
||||
static void setBit(uchar*, u32int);
|
||||
static int walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize],
|
||||
int type, u32int tag, u32int epoch);
|
||||
static void warn(Fsck*, char*, ...);
|
||||
|
||||
#pragma varargck argpos error 2
|
||||
#pragma varargck argpos printnop 1
|
||||
#pragma varargck argpos warn 2
|
||||
|
||||
static Fsck*
|
||||
checkInit(Fsck *chk)
|
||||
{
|
||||
chk->cache = chk->fs->cache;
|
||||
chk->nblocks = cacheLocalSize(chk->cache, PartData);;
|
||||
chk->bsize = chk->fs->blockSize;
|
||||
chk->walkdepth = 0;
|
||||
chk->hint = 0;
|
||||
chk->quantum = chk->nblocks/100;
|
||||
if(chk->quantum == 0)
|
||||
chk->quantum = 1;
|
||||
if(chk->print == nil)
|
||||
chk->print = printnop;
|
||||
if(chk->clre == nil)
|
||||
chk->clre = clrenop;
|
||||
if(chk->close == nil)
|
||||
chk->close = closenop;
|
||||
if(chk->clri == nil)
|
||||
chk->clri = clrinop;
|
||||
return chk;
|
||||
}
|
||||
|
||||
/*
|
||||
* BUG: Should merge checkEpochs and checkDirs so that
|
||||
* bad blocks are only reported once, and so that errors in checkEpochs
|
||||
* can have the affected file names attached, and so that the file system
|
||||
* is only read once.
|
||||
*
|
||||
* Also should summarize the errors instead of printing for every one
|
||||
* (e.g., XXX bad or unreachable blocks in /active/usr/rsc/foo).
|
||||
*/
|
||||
|
||||
void
|
||||
fsCheck(Fsck *chk)
|
||||
{
|
||||
Block *b;
|
||||
Super super;
|
||||
|
||||
checkInit(chk);
|
||||
b = superGet(chk->cache, &super);
|
||||
if(b == nil){
|
||||
chk->print("could not load super block: %R");
|
||||
return;
|
||||
}
|
||||
blockPut(b);
|
||||
|
||||
chk->hint = super.active;
|
||||
checkEpochs(chk);
|
||||
|
||||
chk->smap = vtMemAllocZ(chk->nblocks/8+1);
|
||||
checkDirs(chk);
|
||||
vtMemFree(chk->smap);
|
||||
}
|
||||
|
||||
static void checkEpoch(Fsck*, u32int);
|
||||
|
||||
/*
|
||||
* Walk through all the blocks in the write buffer.
|
||||
* Then we can look for ones we missed -- those are leaks.
|
||||
*/
|
||||
static void
|
||||
checkEpochs(Fsck *chk)
|
||||
{
|
||||
u32int e;
|
||||
uint nb;
|
||||
|
||||
nb = chk->nblocks;
|
||||
chk->amap = vtMemAllocZ(nb/8+1);
|
||||
chk->emap = vtMemAllocZ(nb/8+1);
|
||||
chk->xmap = vtMemAllocZ(nb/8+1);
|
||||
chk->errmap = vtMemAllocZ(nb/8+1);
|
||||
|
||||
for(e = chk->fs->ehi; e >= chk->fs->elo; e--){
|
||||
memset(chk->emap, 0, chk->nblocks/8+1);
|
||||
memset(chk->xmap, 0, chk->nblocks/8+1);
|
||||
checkEpoch(chk, e);
|
||||
}
|
||||
checkLeak(chk);
|
||||
vtMemFree(chk->amap);
|
||||
vtMemFree(chk->emap);
|
||||
vtMemFree(chk->xmap);
|
||||
vtMemFree(chk->errmap);
|
||||
}
|
||||
|
||||
static void
|
||||
checkEpoch(Fsck *chk, u32int epoch)
|
||||
{
|
||||
u32int a;
|
||||
Block *b;
|
||||
Entry e;
|
||||
Label l;
|
||||
|
||||
chk->print("checking epoch %ud...\n", epoch);
|
||||
|
||||
for(a=0; a<chk->nblocks; a++){
|
||||
if(!readLabel(chk->cache, &l, (a+chk->hint)%chk->nblocks)){
|
||||
error(chk, "could not read label for addr 0x%.8#ux", a);
|
||||
continue;
|
||||
}
|
||||
if(l.tag == RootTag && l.epoch == epoch)
|
||||
break;
|
||||
}
|
||||
|
||||
if(a == chk->nblocks){
|
||||
chk->print("could not find root block for epoch %ud", epoch);
|
||||
return;
|
||||
}
|
||||
|
||||
a = (a+chk->hint)%chk->nblocks;
|
||||
b = cacheLocalData(chk->cache, a, BtDir, RootTag, OReadOnly, 0);
|
||||
if(b == nil){
|
||||
error(chk, "could not read root block 0x%.8#ux: %R", a);
|
||||
return;
|
||||
}
|
||||
|
||||
/* no one should point at root blocks */
|
||||
setBit(chk->amap, a);
|
||||
setBit(chk->emap, a);
|
||||
setBit(chk->xmap, a);
|
||||
|
||||
/*
|
||||
* First entry is the rest of the file system.
|
||||
* Second entry is link to previous epoch root,
|
||||
* just a convenience to help the search.
|
||||
*/
|
||||
if(!entryUnpack(&e, b->data, 0)){
|
||||
error(chk, "could not unpack root block 0x%.8#ux: %R", a);
|
||||
blockPut(b);
|
||||
return;
|
||||
}
|
||||
walkEpoch(chk, b, e.score, BtDir, e.tag, epoch);
|
||||
if(entryUnpack(&e, b->data, 1))
|
||||
chk->hint = globalToLocal(e.score);
|
||||
blockPut(b);
|
||||
}
|
||||
|
||||
/*
|
||||
* When b points at bb, need to check:
|
||||
*
|
||||
* (i) b.e in [bb.e, bb.eClose)
|
||||
* (ii) if b.e==bb.e, then no other b' in e points at bb.
|
||||
* (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb.
|
||||
* (iv) if b is active then no other active b' points at bb.
|
||||
* (v) if b is a past life of b' then only one of b and b' is active
|
||||
* (too hard to check)
|
||||
*/
|
||||
static int
|
||||
walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize], int type, u32int tag,
|
||||
u32int epoch)
|
||||
{
|
||||
int i, ret;
|
||||
u32int addr, ep;
|
||||
Block *bb;
|
||||
Entry e;
|
||||
|
||||
if(b && chk->walkdepth == 0 && chk->printblocks)
|
||||
chk->print("%V %d %#.8ux %#.8ux\n", b->score, b->l.type,
|
||||
b->l.tag, b->l.epoch);
|
||||
|
||||
if(!chk->useventi && globalToLocal(score) == NilBlock)
|
||||
return 1;
|
||||
|
||||
chk->walkdepth++;
|
||||
|
||||
bb = cacheGlobal(chk->cache, score, type, tag, OReadOnly);
|
||||
if(bb == nil){
|
||||
error(chk, "could not load block %V type %d tag %ux: %R",
|
||||
score, type, tag);
|
||||
chk->walkdepth--;
|
||||
return 0;
|
||||
}
|
||||
if(chk->printblocks)
|
||||
chk->print("%*s%V %d %#.8ux %#.8ux\n", chk->walkdepth*2, "",
|
||||
score, type, tag, bb->l.epoch);
|
||||
|
||||
ret = 0;
|
||||
addr = globalToLocal(score);
|
||||
if(addr == NilBlock){
|
||||
ret = 1;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if(b){
|
||||
/* (i) */
|
||||
if(b->l.epoch < bb->l.epoch || bb->l.epochClose <= b->l.epoch){
|
||||
error(chk, "walk: block %#ux [%ud, %ud) points at %#ux [%ud, %ud)",
|
||||
b->addr, b->l.epoch, b->l.epochClose,
|
||||
bb->addr, bb->l.epoch, bb->l.epochClose);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/* (ii) */
|
||||
if(b->l.epoch == epoch && bb->l.epoch == epoch){
|
||||
if(getBit(chk->emap, addr)){
|
||||
error(chk, "walk: epoch join detected: addr %#ux %L",
|
||||
bb->addr, &bb->l);
|
||||
goto Exit;
|
||||
}
|
||||
setBit(chk->emap, addr);
|
||||
}
|
||||
|
||||
/* (iii) */
|
||||
if(!(b->l.state&BsCopied) && b->l.epoch == bb->l.epoch){
|
||||
if(getBit(chk->xmap, addr)){
|
||||
error(chk, "walk: copy join detected; addr %#ux %L",
|
||||
bb->addr, &bb->l);
|
||||
goto Exit;
|
||||
}
|
||||
setBit(chk->xmap, addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* (iv) */
|
||||
if(epoch == chk->fs->ehi){
|
||||
/*
|
||||
* since epoch==fs->ehi is first, amap is same as
|
||||
* ``have seen active''
|
||||
*/
|
||||
if(getBit(chk->amap, addr)){
|
||||
error(chk, "walk: active join detected: addr %#ux %L",
|
||||
bb->addr, &bb->l);
|
||||
goto Exit;
|
||||
}
|
||||
if(bb->l.state&BsClosed)
|
||||
error(chk, "walk: addr %#ux: block is in active tree but is closed",
|
||||
addr);
|
||||
}else
|
||||
if(!getBit(chk->amap, addr))
|
||||
if(!(bb->l.state&BsClosed)){
|
||||
// error(chk, "walk: addr %#ux: block is not in active tree, not closed (%d)",
|
||||
// addr, bb->l.epochClose);
|
||||
chk->close(chk, bb, epoch+1);
|
||||
chk->nclose++;
|
||||
}
|
||||
|
||||
if(getBit(chk->amap, addr)){
|
||||
ret = 1;
|
||||
goto Exit;
|
||||
}
|
||||
setBit(chk->amap, addr);
|
||||
|
||||
if(chk->nseen++%chk->quantum == 0)
|
||||
chk->print("check: visited %d/%d blocks (%.0f%%)\n",
|
||||
chk->nseen, chk->nblocks, chk->nseen*100./chk->nblocks);
|
||||
|
||||
b = nil; /* make sure no more refs to parent */
|
||||
USED(b);
|
||||
|
||||
switch(type){
|
||||
default:
|
||||
/* pointer block */
|
||||
for(i = 0; i < chk->bsize/VtScoreSize; i++)
|
||||
if(!walkEpoch(chk, bb, bb->data + i*VtScoreSize,
|
||||
type-1, tag, epoch)){
|
||||
setBit(chk->errmap, bb->addr);
|
||||
chk->clrp(chk, bb, i);
|
||||
chk->nclrp++;
|
||||
}
|
||||
break;
|
||||
case BtData:
|
||||
break;
|
||||
case BtDir:
|
||||
for(i = 0; i < chk->bsize/VtEntrySize; i++){
|
||||
if(!entryUnpack(&e, bb->data, i)){
|
||||
// error(chk, "walk: could not unpack entry: %ux[%d]: %R",
|
||||
// addr, i);
|
||||
setBit(chk->errmap, bb->addr);
|
||||
chk->clre(chk, bb, i);
|
||||
chk->nclre++;
|
||||
continue;
|
||||
}
|
||||
if(!(e.flags & VtEntryActive))
|
||||
continue;
|
||||
if(0) fprint(2, "%x[%d] tag=%x snap=%d score=%V\n",
|
||||
addr, i, e.tag, e.snap, e.score);
|
||||
ep = epoch;
|
||||
if(e.snap != 0){
|
||||
if(e.snap >= epoch){
|
||||
// error(chk, "bad snap in entry: %ux[%d] snap = %ud: epoch = %ud",
|
||||
// addr, i, e.snap, epoch);
|
||||
setBit(chk->errmap, bb->addr);
|
||||
chk->clre(chk, bb, i);
|
||||
chk->nclre++;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(e.flags & VtEntryLocal){
|
||||
if(e.tag < UserTag)
|
||||
if(e.tag != RootTag || tag != RootTag || i != 1){
|
||||
// error(chk, "bad tag in entry: %ux[%d] tag = %ux",
|
||||
// addr, i, e.tag);
|
||||
setBit(chk->errmap, bb->addr);
|
||||
chk->clre(chk, bb, i);
|
||||
chk->nclre++;
|
||||
continue;
|
||||
}
|
||||
}else
|
||||
if(e.tag != 0){
|
||||
// error(chk, "bad tag in entry: %ux[%d] tag = %ux",
|
||||
// addr, i, e.tag);
|
||||
setBit(chk->errmap, bb->addr);
|
||||
chk->clre(chk, bb, i);
|
||||
chk->nclre++;
|
||||
continue;
|
||||
}
|
||||
if(!walkEpoch(chk, bb, e.score, entryType(&e),
|
||||
e.tag, ep)){
|
||||
setBit(chk->errmap, bb->addr);
|
||||
chk->clre(chk, bb, i);
|
||||
chk->nclre++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
Exit:
|
||||
chk->walkdepth--;
|
||||
blockPut(bb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've just walked the whole write buffer. Notice blocks that
|
||||
* aren't marked available but that we didn't visit. They are lost.
|
||||
*/
|
||||
static void
|
||||
checkLeak(Fsck *chk)
|
||||
{
|
||||
u32int a, nfree, nlost;
|
||||
Block *b;
|
||||
Label l;
|
||||
|
||||
nfree = 0;
|
||||
nlost = 0;
|
||||
|
||||
for(a = 0; a < chk->nblocks; a++){
|
||||
if(!readLabel(chk->cache, &l, a)){
|
||||
error(chk, "could not read label: addr 0x%ux %d %d: %R",
|
||||
a, l.type, l.state);
|
||||
continue;
|
||||
}
|
||||
if(getBit(chk->amap, a))
|
||||
continue;
|
||||
if(l.state == BsFree || l.epochClose <= chk->fs->elo ||
|
||||
l.epochClose == l.epoch){
|
||||
nfree++;
|
||||
setBit(chk->amap, a);
|
||||
continue;
|
||||
}
|
||||
if(l.state&BsClosed)
|
||||
continue;
|
||||
nlost++;
|
||||
// warn(chk, "unreachable block: addr 0x%ux type %d tag 0x%ux "
|
||||
// "state %s epoch %ud close %ud", a, l.type, l.tag,
|
||||
// bsStr(l.state), l.epoch, l.epochClose);
|
||||
b = cacheLocal(chk->cache, PartData, a, OReadOnly);
|
||||
if(b == nil){
|
||||
error(chk, "could not read block 0x%#.8ux", a);
|
||||
continue;
|
||||
}
|
||||
chk->close(chk, b, 0);
|
||||
chk->nclose++;
|
||||
setBit(chk->amap, a);
|
||||
blockPut(b);
|
||||
}
|
||||
chk->print("fsys blocks: total=%ud used=%ud(%.1f%%) free=%ud(%.1f%%) lost=%ud(%.1f%%)\n",
|
||||
chk->nblocks,
|
||||
chk->nblocks - nfree-nlost,
|
||||
100.*(chk->nblocks - nfree - nlost)/chk->nblocks,
|
||||
nfree, 100.*nfree/chk->nblocks,
|
||||
nlost, 100.*nlost/chk->nblocks);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check that all sources in the tree are accessible.
|
||||
*/
|
||||
static Source *
|
||||
openSource(Fsck *chk, Source *s, char *name, uchar *bm, u32int offset,
|
||||
u32int gen, int dir, MetaBlock *mb, int i, Block *b)
|
||||
{
|
||||
Source *r;
|
||||
|
||||
r = nil;
|
||||
if(getBit(bm, offset)){
|
||||
warn(chk, "multiple references to source: %s -> %d",
|
||||
name, offset);
|
||||
goto Err;
|
||||
}
|
||||
setBit(bm, offset);
|
||||
|
||||
r = sourceOpen(s, offset, OReadOnly, 0);
|
||||
if(r == nil){
|
||||
warn(chk, "could not open source: %s -> %d: %R", name, offset);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
if(r->gen != gen){
|
||||
warn(chk, "source has been removed: %s -> %d", name, offset);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
if(r->dir != dir){
|
||||
warn(chk, "dir mismatch: %s -> %d", name, offset);
|
||||
goto Err;
|
||||
}
|
||||
return r;
|
||||
Err:
|
||||
chk->clri(chk, name, mb, i, b);
|
||||
chk->nclri++;
|
||||
if(r)
|
||||
sourceClose(r);
|
||||
return nil;
|
||||
}
|
||||
|
||||
typedef struct MetaChunk MetaChunk;
|
||||
struct MetaChunk {
|
||||
ushort offset;
|
||||
ushort size;
|
||||
ushort index;
|
||||
};
|
||||
|
||||
static int
|
||||
offsetCmp(void *s0, void *s1)
|
||||
{
|
||||
MetaChunk *mc0, *mc1;
|
||||
|
||||
mc0 = s0;
|
||||
mc1 = s1;
|
||||
if(mc0->offset < mc1->offset)
|
||||
return -1;
|
||||
if(mc0->offset > mc1->offset)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fsck that MetaBlock has reasonable header, sorted entries,
|
||||
*/
|
||||
static int
|
||||
chkMetaBlock(MetaBlock *mb)
|
||||
{
|
||||
MetaChunk *mc;
|
||||
int oo, o, n, i;
|
||||
uchar *p;
|
||||
|
||||
mc = vtMemAlloc(mb->nindex*sizeof(MetaChunk));
|
||||
p = mb->buf + MetaHeaderSize;
|
||||
for(i = 0; i < mb->nindex; i++){
|
||||
mc[i].offset = p[0]<<8 | p[1];
|
||||
mc[i].size = p[2]<<8 | p[3];
|
||||
mc[i].index = i;
|
||||
p += MetaIndexSize;
|
||||
}
|
||||
|
||||
qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
|
||||
|
||||
/* check block looks ok */
|
||||
oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
||||
o = oo;
|
||||
n = 0;
|
||||
for(i = 0; i < mb->nindex; i++){
|
||||
o = mc[i].offset;
|
||||
n = mc[i].size;
|
||||
if(o < oo)
|
||||
goto Err;
|
||||
oo += n;
|
||||
}
|
||||
if(o+n > mb->size || mb->size - oo != mb->free)
|
||||
goto Err;
|
||||
|
||||
vtMemFree(mc);
|
||||
return 1;
|
||||
|
||||
Err:
|
||||
if(0){
|
||||
fprint(2, "metaChunks failed!\n");
|
||||
oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
||||
for(i=0; i<mb->nindex; i++){
|
||||
fprint(2, "\t%d: %d %d\n", i, mc[i].offset,
|
||||
mc[i].offset + mc[i].size);
|
||||
oo += mc[i].size;
|
||||
}
|
||||
fprint(2, "\tused=%d size=%d free=%d free2=%d\n",
|
||||
oo, mb->size, mb->free, mb->size - oo);
|
||||
}
|
||||
vtMemFree(mc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
scanSource(Fsck *chk, char *name, Source *r)
|
||||
{
|
||||
u32int a, nb, o;
|
||||
Block *b;
|
||||
Entry e;
|
||||
|
||||
if(!chk->useventi && globalToLocal(r->score)==NilBlock)
|
||||
return;
|
||||
if(!sourceGetEntry(r, &e)){
|
||||
error(chk, "could not get entry for %s", name);
|
||||
return;
|
||||
}
|
||||
a = globalToLocal(e.score);
|
||||
if(!chk->useventi && a==NilBlock)
|
||||
return;
|
||||
if(getBit(chk->smap, a))
|
||||
return;
|
||||
setBit(chk->smap, a);
|
||||
|
||||
nb = (sourceGetSize(r) + r->dsize-1) / r->dsize;
|
||||
for(o = 0; o < nb; o++){
|
||||
b = sourceBlock(r, o, OReadOnly);
|
||||
if(b == nil){
|
||||
error(chk, "could not read block in data file %s", name);
|
||||
continue;
|
||||
}
|
||||
if(b->addr != NilBlock && getBit(chk->errmap, b->addr)){
|
||||
warn(chk, "previously reported error in block %ux is in file %s",
|
||||
b->addr, name);
|
||||
}
|
||||
blockPut(b);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the source tree making sure that the BtData
|
||||
* sources containing directory entries are okay.
|
||||
*/
|
||||
static void
|
||||
chkDir(Fsck *chk, char *name, Source *source, Source *meta)
|
||||
{
|
||||
int i;
|
||||
u32int a1, a2, nb, o;
|
||||
char *s, *nn;
|
||||
uchar *bm;
|
||||
Block *b, *bb;
|
||||
DirEntry de;
|
||||
Entry e1, e2;
|
||||
MetaBlock mb;
|
||||
MetaEntry me;
|
||||
Source *r, *mr;
|
||||
|
||||
if(!chk->useventi && globalToLocal(source->score)==NilBlock &&
|
||||
globalToLocal(meta->score)==NilBlock)
|
||||
return;
|
||||
|
||||
if(!sourceLock2(source, meta, OReadOnly)){
|
||||
warn(chk, "could not lock sources for %s: %R", name);
|
||||
return;
|
||||
}
|
||||
if(!sourceGetEntry(source, &e1) || !sourceGetEntry(meta, &e2)){
|
||||
warn(chk, "could not load entries for %s: %R", name);
|
||||
return;
|
||||
}
|
||||
a1 = globalToLocal(e1.score);
|
||||
a2 = globalToLocal(e2.score);
|
||||
if((!chk->useventi && a1==NilBlock && a2==NilBlock)
|
||||
|| (getBit(chk->smap, a1) && getBit(chk->smap, a2))){
|
||||
sourceUnlock(source);
|
||||
sourceUnlock(meta);
|
||||
return;
|
||||
}
|
||||
setBit(chk->smap, a1);
|
||||
setBit(chk->smap, a2);
|
||||
|
||||
bm = vtMemAllocZ(sourceGetDirSize(source)/8 + 1);
|
||||
|
||||
nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize;
|
||||
for(o = 0; o < nb; o++){
|
||||
b = sourceBlock(meta, o, OReadOnly);
|
||||
if(b == nil){
|
||||
error(chk, "could not read block in meta file: %s[%ud]: %R",
|
||||
name, o);
|
||||
continue;
|
||||
}
|
||||
if(0) fprint(2, "source %V:%d block %d addr %d\n", source->score,
|
||||
source->offset, o, b->addr);
|
||||
if(b->addr != NilBlock && getBit(chk->errmap, b->addr))
|
||||
warn(chk, "previously reported error in block %ux is in %s",
|
||||
b->addr, name);
|
||||
|
||||
if(!mbUnpack(&mb, b->data, meta->dsize)){
|
||||
error(chk, "could not unpack meta block: %s[%ud]: %R",
|
||||
name, o);
|
||||
blockPut(b);
|
||||
continue;
|
||||
}
|
||||
if(!chkMetaBlock(&mb)){
|
||||
error(chk, "bad meta block: %s[%ud]: %R", name, o);
|
||||
blockPut(b);
|
||||
continue;
|
||||
}
|
||||
s = nil;
|
||||
for(i=mb.nindex-1; i>=0; i--){
|
||||
meUnpack(&me, &mb, i);
|
||||
if(!deUnpack(&de, &me)){
|
||||
error(chk,
|
||||
"could not unpack dir entry: %s[%ud][%d]: %R",
|
||||
name, o, i);
|
||||
continue;
|
||||
}
|
||||
if(s && strcmp(s, de.elem) <= 0)
|
||||
error(chk,
|
||||
"dir entry out of order: %s[%ud][%d] = %s last = %s",
|
||||
name, o, i, de.elem, s);
|
||||
vtMemFree(s);
|
||||
s = vtStrDup(de.elem);
|
||||
nn = smprint("%s/%s", name, de.elem);
|
||||
if(nn == nil){
|
||||
error(chk, "out of memory");
|
||||
continue;
|
||||
}
|
||||
if(chk->printdirs)
|
||||
if(de.mode&ModeDir)
|
||||
chk->print("%s/\n", nn);
|
||||
if(chk->printfiles)
|
||||
if(!(de.mode&ModeDir))
|
||||
chk->print("%s\n", nn);
|
||||
if(!(de.mode & ModeDir)){
|
||||
r = openSource(chk, source, nn, bm, de.entry,
|
||||
de.gen, 0, &mb, i, b);
|
||||
if(r != nil){
|
||||
if(sourceLock(r, OReadOnly)){
|
||||
scanSource(chk, nn, r);
|
||||
sourceUnlock(r);
|
||||
}
|
||||
sourceClose(r);
|
||||
}
|
||||
deCleanup(&de);
|
||||
free(nn);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = openSource(chk, source, nn, bm, de.entry,
|
||||
de.gen, 1, &mb, i, b);
|
||||
if(r == nil){
|
||||
deCleanup(&de);
|
||||
free(nn);
|
||||
continue;
|
||||
}
|
||||
|
||||
mr = openSource(chk, source, nn, bm, de.mentry,
|
||||
de.mgen, 0, &mb, i, b);
|
||||
if(mr == nil){
|
||||
sourceClose(r);
|
||||
deCleanup(&de);
|
||||
free(nn);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!(de.mode&ModeSnapshot) || chk->walksnapshots)
|
||||
chkDir(chk, nn, r, mr);
|
||||
|
||||
sourceClose(mr);
|
||||
sourceClose(r);
|
||||
deCleanup(&de);
|
||||
free(nn);
|
||||
deCleanup(&de);
|
||||
|
||||
}
|
||||
vtMemFree(s);
|
||||
blockPut(b);
|
||||
}
|
||||
|
||||
nb = sourceGetDirSize(source);
|
||||
for(o=0; o<nb; o++){
|
||||
if(getBit(bm, o))
|
||||
continue;
|
||||
r = sourceOpen(source, o, OReadOnly, 0);
|
||||
if(r == nil)
|
||||
continue;
|
||||
warn(chk, "non referenced entry in source %s[%d]", name, o);
|
||||
if((bb = sourceBlock(source, o/(source->dsize/VtEntrySize),
|
||||
OReadOnly)) != nil){
|
||||
if(bb->addr != NilBlock){
|
||||
setBit(chk->errmap, bb->addr);
|
||||
chk->clre(chk, bb, o%(source->dsize/VtEntrySize));
|
||||
chk->nclre++;
|
||||
}
|
||||
blockPut(bb);
|
||||
}
|
||||
sourceClose(r);
|
||||
}
|
||||
|
||||
sourceUnlock(source);
|
||||
sourceUnlock(meta);
|
||||
vtMemFree(bm);
|
||||
}
|
||||
|
||||
static void
|
||||
checkDirs(Fsck *chk)
|
||||
{
|
||||
Source *r, *mr;
|
||||
|
||||
sourceLock(chk->fs->source, OReadOnly);
|
||||
r = sourceOpen(chk->fs->source, 0, OReadOnly, 0);
|
||||
mr = sourceOpen(chk->fs->source, 1, OReadOnly, 0);
|
||||
sourceUnlock(chk->fs->source);
|
||||
chkDir(chk, "", r, mr);
|
||||
|
||||
sourceClose(r);
|
||||
sourceClose(mr);
|
||||
}
|
||||
|
||||
static void
|
||||
setBit(uchar *bmap, u32int addr)
|
||||
{
|
||||
if(addr == NilBlock)
|
||||
return;
|
||||
|
||||
bmap[addr>>3] |= 1 << (addr & 7);
|
||||
}
|
||||
|
||||
static int
|
||||
getBit(uchar *bmap, u32int addr)
|
||||
{
|
||||
if(addr == NilBlock)
|
||||
return 0;
|
||||
|
||||
return (bmap[addr>>3] >> (addr & 7)) & 1;
|
||||
}
|
||||
|
||||
static void
|
||||
error(Fsck *chk, char *fmt, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list arg;
|
||||
static int nerr;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof buf, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
chk->print("error: %s\n", buf);
|
||||
|
||||
// if(nerr++ > 20)
|
||||
// vtFatal("too many errors");
|
||||
}
|
||||
|
||||
static void
|
||||
warn(Fsck *chk, char *fmt, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list arg;
|
||||
static int nerr;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof buf, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
chk->print("error: %s\n", buf);
|
||||
}
|
||||
|
||||
static void
|
||||
clrenop(Fsck*, Block*, int)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
closenop(Fsck*, Block*, u32int)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
clrinop(Fsck*, char*, MetaBlock*, int, Block*)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
printnop(char*, ...)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
332
src/cmd/fossil/dat.h
Normal file
332
src/cmd/fossil/dat.h
Normal file
@ -0,0 +1,332 @@
|
||||
typedef struct Arch Arch;
|
||||
typedef struct BList BList;
|
||||
typedef struct Block Block;
|
||||
typedef struct Cache Cache;
|
||||
typedef struct Disk Disk;
|
||||
typedef struct Entry Entry;
|
||||
typedef struct Fsck Fsck;
|
||||
typedef struct Header Header;
|
||||
typedef struct Label Label;
|
||||
typedef struct Periodic Periodic;
|
||||
typedef struct Snap Snap;
|
||||
typedef struct Source Source;
|
||||
typedef struct Super Super;
|
||||
typedef struct WalkPtr WalkPtr;
|
||||
|
||||
#pragma incomplete Arch
|
||||
#pragma incomplete BList
|
||||
#pragma incomplete Cache
|
||||
#pragma incomplete Disk
|
||||
#pragma incomplete Periodic
|
||||
#pragma incomplete Snap
|
||||
|
||||
/* tunable parameters - probably should not be constants */
|
||||
enum {
|
||||
/*
|
||||
* estimate of bytes per dir entries - determines number
|
||||
* of index entries in the block
|
||||
*/
|
||||
BytesPerEntry = 100,
|
||||
/* don't allocate in block if more than this percentage full */
|
||||
FullPercentage = 80,
|
||||
FlushSize = 200, /* number of blocks to flush */
|
||||
DirtyPercentage = 50, /* maximum percentage of dirty blocks */
|
||||
};
|
||||
|
||||
enum {
|
||||
Nowaitlock,
|
||||
Waitlock,
|
||||
|
||||
NilBlock = (~0UL),
|
||||
MaxBlock = (1UL<<31),
|
||||
};
|
||||
|
||||
enum {
|
||||
HeaderMagic = 0x3776ae89,
|
||||
HeaderVersion = 1,
|
||||
HeaderOffset = 128*1024,
|
||||
HeaderSize = 512,
|
||||
SuperMagic = 0x2340a3b1,
|
||||
SuperSize = 512,
|
||||
SuperVersion = 1,
|
||||
LabelSize = 14,
|
||||
};
|
||||
|
||||
/* well known tags */
|
||||
enum {
|
||||
BadTag = 0, /* this tag should not be used */
|
||||
RootTag = 1, /* root of fs */
|
||||
EnumTag, /* root of a dir listing */
|
||||
UserTag = 32, /* all other tags should be >= UserTag */
|
||||
};
|
||||
|
||||
struct Super {
|
||||
u16int version;
|
||||
u32int epochLow;
|
||||
u32int epochHigh;
|
||||
u64int qid; /* next qid */
|
||||
u32int active; /* root of active file system */
|
||||
u32int next; /* root of next snapshot to archive */
|
||||
u32int current; /* root of snapshot currently archiving */
|
||||
uchar last[VtScoreSize]; /* last snapshot successfully archived */
|
||||
char name[128]; /* label */
|
||||
};
|
||||
|
||||
|
||||
struct Fs {
|
||||
Arch *arch; /* immutable */
|
||||
Cache *cache; /* immutable */
|
||||
int mode; /* immutable */
|
||||
int noatimeupd; /* immutable */
|
||||
int blockSize; /* immutable */
|
||||
VtSession *z; /* immutable */
|
||||
Snap *snap; /* immutable */
|
||||
/* immutable; copy here & Fsys to ease error reporting */
|
||||
char *name;
|
||||
|
||||
Periodic *metaFlush; /* periodically flushes metadata cached in files */
|
||||
|
||||
/*
|
||||
* epoch lock.
|
||||
* Most operations on the fs require a read lock of elk, ensuring that
|
||||
* the current high and low epochs do not change under foot.
|
||||
* This lock is mostly acquired via a call to fileLock or fileRlock.
|
||||
* Deletion and creation of snapshots occurs under a write lock of elk,
|
||||
* ensuring no file operations are occurring concurrently.
|
||||
*/
|
||||
VtLock *elk; /* epoch lock */
|
||||
u32int ehi; /* epoch high */
|
||||
u32int elo; /* epoch low */
|
||||
|
||||
int halted; /* epoch lock is held to halt (console initiated) */
|
||||
|
||||
Source *source; /* immutable: root of sources */
|
||||
File *file; /* immutable: root of files */
|
||||
};
|
||||
|
||||
/*
|
||||
* variant on VtEntry
|
||||
* there are extra fields when stored locally
|
||||
*/
|
||||
struct Entry {
|
||||
u32int gen; /* generation number */
|
||||
ushort psize; /* pointer block size */
|
||||
ushort dsize; /* data block size */
|
||||
uchar depth; /* unpacked from flags */
|
||||
uchar flags;
|
||||
uvlong size;
|
||||
uchar score[VtScoreSize];
|
||||
u32int tag; /* tag for local blocks: zero if stored on Venti */
|
||||
u32int snap; /* non-zero -> entering snapshot of given epoch */
|
||||
uchar archive; /* archive this snapshot: only valid for snap != 0 */
|
||||
};
|
||||
|
||||
/*
|
||||
* This is called a `stream' in the fossil paper. There used to be Sinks too.
|
||||
* We believe that Sources and Files are one-to-one.
|
||||
*/
|
||||
struct Source {
|
||||
Fs *fs; /* immutable */
|
||||
int mode; /* immutable */
|
||||
int issnapshot; /* immutable */
|
||||
u32int gen; /* immutable */
|
||||
int dsize; /* immutable */
|
||||
int dir; /* immutable */
|
||||
|
||||
Source *parent; /* immutable */
|
||||
File *file; /* immutable; point back */
|
||||
|
||||
VtLock *lk;
|
||||
int ref;
|
||||
/*
|
||||
* epoch for the source
|
||||
* for ReadWrite sources, epoch is used to lazily notice
|
||||
* sources that must be split from the snapshots.
|
||||
* for ReadOnly sources, the epoch represents the minimum epoch
|
||||
* along the chain from the root, and is used to lazily notice
|
||||
* sources that have become invalid because they belong to an old
|
||||
* snapshot.
|
||||
*/
|
||||
u32int epoch;
|
||||
Block *b; /* block containing this source */
|
||||
uchar score[VtScoreSize]; /* score of block containing this source */
|
||||
u32int scoreEpoch; /* epoch of block containing this source */
|
||||
int epb; /* immutable: entries per block in parent */
|
||||
u32int tag; /* immutable: tag of parent */
|
||||
u32int offset; /* immutable: entry offset in parent */
|
||||
};
|
||||
|
||||
|
||||
struct Header {
|
||||
ushort version;
|
||||
ushort blockSize;
|
||||
ulong super; /* super blocks */
|
||||
ulong label; /* start of labels */
|
||||
ulong data; /* end of labels - start of data blocks */
|
||||
ulong end; /* end of data blocks */
|
||||
};
|
||||
|
||||
/*
|
||||
* contains a one block buffer
|
||||
* to avoid problems of the block changing underfoot
|
||||
* and to enable an interface that supports unget.
|
||||
*/
|
||||
struct DirEntryEnum {
|
||||
File *file;
|
||||
|
||||
u32int boff; /* block offset */
|
||||
|
||||
int i, n;
|
||||
DirEntry *buf;
|
||||
};
|
||||
|
||||
/* Block states */
|
||||
enum {
|
||||
BsFree = 0, /* available for allocation */
|
||||
BsBad = 0xFF, /* something is wrong with this block */
|
||||
|
||||
/* bit fields */
|
||||
BsAlloc = 1<<0, /* block is in use */
|
||||
BsCopied = 1<<1,/* block has been copied (usually in preparation for unlink) */
|
||||
BsVenti = 1<<2, /* block has been stored on Venti */
|
||||
BsClosed = 1<<3,/* block has been unlinked on disk from active file system */
|
||||
BsMask = BsAlloc|BsCopied|BsVenti|BsClosed,
|
||||
};
|
||||
|
||||
/*
|
||||
* block types
|
||||
* more regular than Venti block types
|
||||
* bit 3 -> block or data block
|
||||
* bits 2-0 -> level of block
|
||||
*/
|
||||
enum {
|
||||
BtData,
|
||||
BtDir = 1<<3,
|
||||
BtLevelMask = 7,
|
||||
BtMax = 1<<4,
|
||||
};
|
||||
|
||||
/* io states */
|
||||
enum {
|
||||
BioEmpty, /* label & data are not valid */
|
||||
BioLabel, /* label is good */
|
||||
BioClean, /* data is on the disk */
|
||||
BioDirty, /* data is not yet on the disk */
|
||||
BioReading, /* in process of reading data */
|
||||
BioWriting, /* in process of writing data */
|
||||
BioReadError, /* error reading: assume disk always handles write errors */
|
||||
BioVentiError, /* error reading from venti (probably disconnected) */
|
||||
BioMax
|
||||
};
|
||||
|
||||
struct Label {
|
||||
uchar type;
|
||||
uchar state;
|
||||
u32int tag;
|
||||
u32int epoch;
|
||||
u32int epochClose;
|
||||
};
|
||||
|
||||
struct Block {
|
||||
Cache *c;
|
||||
int ref;
|
||||
int nlock;
|
||||
uintptr pc; /* pc that fetched this block from the cache */
|
||||
|
||||
VtLock *lk;
|
||||
|
||||
int part;
|
||||
u32int addr;
|
||||
uchar score[VtScoreSize]; /* score */
|
||||
Label l;
|
||||
|
||||
uchar *dmap;
|
||||
|
||||
uchar *data;
|
||||
|
||||
/* the following is private; used by cache */
|
||||
|
||||
Block *next; /* doubly linked hash chains */
|
||||
Block **prev;
|
||||
u32int heap; /* index in heap table */
|
||||
u32int used; /* last reference times */
|
||||
|
||||
u32int vers; /* version of dirty flag */
|
||||
|
||||
BList *uhead; /* blocks to unlink when this block is written */
|
||||
BList *utail;
|
||||
|
||||
/* block ordering for cache -> disk */
|
||||
BList *prior; /* list of blocks before this one */
|
||||
|
||||
Block *ionext;
|
||||
int iostate;
|
||||
VtRendez *ioready;
|
||||
};
|
||||
|
||||
/* tree walker, for gc and archiver */
|
||||
struct WalkPtr
|
||||
{
|
||||
uchar *data;
|
||||
int isEntry;
|
||||
int n;
|
||||
int m;
|
||||
Entry e;
|
||||
uchar type;
|
||||
u32int tag;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DoClose = 1<<0,
|
||||
DoClre = 1<<1,
|
||||
DoClri = 1<<2,
|
||||
DoClrp = 1<<3,
|
||||
};
|
||||
|
||||
struct Fsck
|
||||
{
|
||||
/* filled in by caller */
|
||||
int printblocks;
|
||||
int useventi;
|
||||
int flags;
|
||||
int printdirs;
|
||||
int printfiles;
|
||||
int walksnapshots;
|
||||
int walkfs;
|
||||
Fs *fs;
|
||||
int (*print)(char*, ...);
|
||||
void (*clre)(Fsck*, Block*, int);
|
||||
void (*clrp)(Fsck*, Block*, int);
|
||||
void (*close)(Fsck*, Block*, u32int);
|
||||
void (*clri)(Fsck*, char*, MetaBlock*, int, Block*);
|
||||
|
||||
/* used internally */
|
||||
Cache *cache;
|
||||
uchar *amap; /* all blocks seen so far */
|
||||
uchar *emap; /* all blocks seen in this epoch */
|
||||
uchar *xmap; /* all blocks in this epoch with parents in this epoch */
|
||||
uchar *errmap; /* blocks with errors */
|
||||
uchar *smap; /* walked sources */
|
||||
int nblocks;
|
||||
int bsize;
|
||||
int walkdepth;
|
||||
u32int hint; /* where the next root probably is */
|
||||
int nseen;
|
||||
int quantum;
|
||||
int nclre;
|
||||
int nclrp;
|
||||
int nclose;
|
||||
int nclri;
|
||||
};
|
||||
|
||||
/* disk partitions; keep in sync with partname[] in disk.c */
|
||||
enum {
|
||||
PartError,
|
||||
PartSuper,
|
||||
PartLabel,
|
||||
PartData,
|
||||
PartVenti, /* fake partition */
|
||||
};
|
||||
|
||||
extern vtType[BtMax];
|
||||
406
src/cmd/fossil/disk.c
Normal file
406
src/cmd/fossil/disk.c
Normal file
@ -0,0 +1,406 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
static void diskThread(void *a);
|
||||
|
||||
enum {
|
||||
/*
|
||||
* disable measurement since it gets alignment faults on BG
|
||||
* and the guts used to be commented out.
|
||||
*/
|
||||
Timing = 0, /* flag */
|
||||
QueueSize = 100, /* maximum block to queue */
|
||||
};
|
||||
|
||||
struct Disk {
|
||||
VtLock *lk;
|
||||
int ref;
|
||||
|
||||
int fd;
|
||||
Header h;
|
||||
|
||||
VtRendez *flow;
|
||||
VtRendez *starve;
|
||||
VtRendez *flush;
|
||||
VtRendez *die;
|
||||
|
||||
int nqueue;
|
||||
|
||||
Block *cur; /* block to do on current scan */
|
||||
Block *next; /* blocks to do next scan */
|
||||
};
|
||||
|
||||
/* keep in sync with Part* enum in dat.h */
|
||||
static char *partname[] = {
|
||||
[PartError] "error",
|
||||
[PartSuper] "super",
|
||||
[PartLabel] "label",
|
||||
[PartData] "data",
|
||||
[PartVenti] "venti",
|
||||
};
|
||||
|
||||
Disk *
|
||||
diskAlloc(int fd)
|
||||
{
|
||||
u8int buf[HeaderSize];
|
||||
Header h;
|
||||
Disk *disk;
|
||||
|
||||
if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize){
|
||||
vtSetError("short read: %r");
|
||||
vtOSError();
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(!headerUnpack(&h, buf)){
|
||||
vtSetError("bad disk header");
|
||||
return nil;
|
||||
}
|
||||
disk = vtMemAllocZ(sizeof(Disk));
|
||||
disk->lk = vtLockAlloc();
|
||||
disk->starve = vtRendezAlloc(disk->lk);
|
||||
disk->flow = vtRendezAlloc(disk->lk);
|
||||
disk->flush = vtRendezAlloc(disk->lk);
|
||||
disk->fd = fd;
|
||||
disk->h = h;
|
||||
|
||||
disk->ref = 2;
|
||||
vtThread(diskThread, disk);
|
||||
|
||||
return disk;
|
||||
}
|
||||
|
||||
void
|
||||
diskFree(Disk *disk)
|
||||
{
|
||||
diskFlush(disk);
|
||||
|
||||
/* kill slave */
|
||||
vtLock(disk->lk);
|
||||
disk->die = vtRendezAlloc(disk->lk);
|
||||
vtWakeup(disk->starve);
|
||||
while(disk->ref > 1)
|
||||
vtSleep(disk->die);
|
||||
vtUnlock(disk->lk);
|
||||
vtRendezFree(disk->flow);
|
||||
vtRendezFree(disk->starve);
|
||||
vtRendezFree(disk->die);
|
||||
vtLockFree(disk->lk);
|
||||
close(disk->fd);
|
||||
vtMemFree(disk);
|
||||
}
|
||||
|
||||
static u32int
|
||||
partStart(Disk *disk, int part)
|
||||
{
|
||||
switch(part){
|
||||
default:
|
||||
assert(0);
|
||||
case PartSuper:
|
||||
return disk->h.super;
|
||||
case PartLabel:
|
||||
return disk->h.label;
|
||||
case PartData:
|
||||
return disk->h.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static u32int
|
||||
partEnd(Disk *disk, int part)
|
||||
{
|
||||
switch(part){
|
||||
default:
|
||||
assert(0);
|
||||
case PartSuper:
|
||||
return disk->h.super+1;
|
||||
case PartLabel:
|
||||
return disk->h.data;
|
||||
case PartData:
|
||||
return disk->h.end;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
diskReadRaw(Disk *disk, int part, u32int addr, uchar *buf)
|
||||
{
|
||||
ulong start, end;
|
||||
u64int offset;
|
||||
int n, nn;
|
||||
|
||||
start = partStart(disk, part);
|
||||
end = partEnd(disk, part);
|
||||
|
||||
if(addr >= end-start){
|
||||
vtSetError(EBadAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = ((u64int)(addr + start))*disk->h.blockSize;
|
||||
n = disk->h.blockSize;
|
||||
while(n > 0){
|
||||
nn = pread(disk->fd, buf, n, offset);
|
||||
if(nn < 0){
|
||||
vtOSError();
|
||||
return 0;
|
||||
}
|
||||
if(nn == 0){
|
||||
vtSetError("eof reading disk");
|
||||
return 0;
|
||||
}
|
||||
n -= nn;
|
||||
offset += nn;
|
||||
buf += nn;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
diskWriteRaw(Disk *disk, int part, u32int addr, uchar *buf)
|
||||
{
|
||||
ulong start, end;
|
||||
u64int offset;
|
||||
int n;
|
||||
|
||||
start = partStart(disk, part);
|
||||
end = partEnd(disk, part);
|
||||
|
||||
if(addr >= end - start){
|
||||
vtSetError(EBadAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = ((u64int)(addr + start))*disk->h.blockSize;
|
||||
n = pwrite(disk->fd, buf, disk->h.blockSize, offset);
|
||||
if(n < 0){
|
||||
vtOSError();
|
||||
return 0;
|
||||
}
|
||||
if(n < disk->h.blockSize) {
|
||||
vtSetError("short write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
diskQueue(Disk *disk, Block *b)
|
||||
{
|
||||
Block **bp, *bb;
|
||||
|
||||
vtLock(disk->lk);
|
||||
while(disk->nqueue >= QueueSize)
|
||||
vtSleep(disk->flow);
|
||||
if(disk->cur == nil || b->addr > disk->cur->addr)
|
||||
bp = &disk->cur;
|
||||
else
|
||||
bp = &disk->next;
|
||||
|
||||
for(bb=*bp; bb; bb=*bp){
|
||||
if(b->addr < bb->addr)
|
||||
break;
|
||||
bp = &bb->ionext;
|
||||
}
|
||||
b->ionext = bb;
|
||||
*bp = b;
|
||||
if(disk->nqueue == 0)
|
||||
vtWakeup(disk->starve);
|
||||
disk->nqueue++;
|
||||
vtUnlock(disk->lk);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
diskRead(Disk *disk, Block *b)
|
||||
{
|
||||
assert(b->iostate == BioEmpty || b->iostate == BioLabel);
|
||||
blockSetIOState(b, BioReading);
|
||||
diskQueue(disk, b);
|
||||
}
|
||||
|
||||
void
|
||||
diskWrite(Disk *disk, Block *b)
|
||||
{
|
||||
assert(b->nlock == 1);
|
||||
assert(b->iostate == BioDirty);
|
||||
blockSetIOState(b, BioWriting);
|
||||
diskQueue(disk, b);
|
||||
}
|
||||
|
||||
void
|
||||
diskWriteAndWait(Disk *disk, Block *b)
|
||||
{
|
||||
int nlock;
|
||||
|
||||
/*
|
||||
* If b->nlock > 1, the block is aliased within
|
||||
* a single thread. That thread is us.
|
||||
* DiskWrite does some funny stuff with VtLock
|
||||
* and blockPut that basically assumes b->nlock==1.
|
||||
* We humor diskWrite by temporarily setting
|
||||
* nlock to 1. This needs to be revisited.
|
||||
*/
|
||||
nlock = b->nlock;
|
||||
if(nlock > 1)
|
||||
b->nlock = 1;
|
||||
diskWrite(disk, b);
|
||||
while(b->iostate != BioClean)
|
||||
vtSleep(b->ioready);
|
||||
b->nlock = nlock;
|
||||
}
|
||||
|
||||
int
|
||||
diskBlockSize(Disk *disk)
|
||||
{
|
||||
return disk->h.blockSize; /* immuttable */
|
||||
}
|
||||
|
||||
int
|
||||
diskFlush(Disk *disk)
|
||||
{
|
||||
Dir dir;
|
||||
|
||||
vtLock(disk->lk);
|
||||
while(disk->nqueue > 0)
|
||||
vtSleep(disk->flush);
|
||||
vtUnlock(disk->lk);
|
||||
|
||||
/* there really should be a cleaner interface to flush an fd */
|
||||
nulldir(&dir);
|
||||
if(dirfwstat(disk->fd, &dir) < 0){
|
||||
vtOSError();
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32int
|
||||
diskSize(Disk *disk, int part)
|
||||
{
|
||||
return partEnd(disk, part) - partStart(disk, part);
|
||||
}
|
||||
|
||||
static uintptr
|
||||
mypc(int x)
|
||||
{
|
||||
return getcallerpc(&x);
|
||||
}
|
||||
|
||||
static char *
|
||||
disk2file(Disk *disk)
|
||||
{
|
||||
static char buf[256];
|
||||
|
||||
if (fd2path(disk->fd, buf, sizeof buf) < 0)
|
||||
strncpy(buf, "GOK", sizeof buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
diskThread(void *a)
|
||||
{
|
||||
Disk *disk = a;
|
||||
Block *b;
|
||||
uchar *buf, *p;
|
||||
double t;
|
||||
int nio;
|
||||
|
||||
vtThreadSetName("disk");
|
||||
|
||||
//fprint(2, "diskThread %d\n", getpid());
|
||||
|
||||
buf = vtMemAlloc(disk->h.blockSize);
|
||||
|
||||
vtLock(disk->lk);
|
||||
if (Timing) {
|
||||
nio = 0;
|
||||
t = -nsec();
|
||||
}
|
||||
for(;;){
|
||||
while(disk->nqueue == 0){
|
||||
if (Timing) {
|
||||
t += nsec();
|
||||
if(nio >= 10000){
|
||||
fprint(2, "disk: io=%d at %.3fms\n",
|
||||
nio, t*1e-6/nio);
|
||||
nio = 0;
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
if(disk->die != nil)
|
||||
goto Done;
|
||||
vtSleep(disk->starve);
|
||||
if (Timing)
|
||||
t -= nsec();
|
||||
}
|
||||
assert(disk->cur != nil || disk->next != nil);
|
||||
|
||||
if(disk->cur == nil){
|
||||
disk->cur = disk->next;
|
||||
disk->next = nil;
|
||||
}
|
||||
b = disk->cur;
|
||||
disk->cur = b->ionext;
|
||||
vtUnlock(disk->lk);
|
||||
|
||||
/*
|
||||
* no one should hold onto blocking in the
|
||||
* reading or writing state, so this lock should
|
||||
* not cause deadlock.
|
||||
*/
|
||||
if(0)fprint(2, "fossil: diskThread: %d:%d %x\n", getpid(), b->part, b->addr);
|
||||
bwatchLock(b);
|
||||
vtLock(b->lk);
|
||||
b->pc = mypc(0);
|
||||
assert(b->nlock == 1);
|
||||
switch(b->iostate){
|
||||
default:
|
||||
abort();
|
||||
case BioReading:
|
||||
if(!diskReadRaw(disk, b->part, b->addr, b->data)){
|
||||
fprint(2, "fossil: diskReadRaw failed: %s: "
|
||||
"score %V: part=%s block %ud: %r\n",
|
||||
disk2file(disk), b->score,
|
||||
partname[b->part], b->addr);
|
||||
blockSetIOState(b, BioReadError);
|
||||
}else
|
||||
blockSetIOState(b, BioClean);
|
||||
break;
|
||||
case BioWriting:
|
||||
p = blockRollback(b, buf);
|
||||
/* NB: ctime result ends with a newline */
|
||||
if(!diskWriteRaw(disk, b->part, b->addr, p)){
|
||||
fprint(2, "fossil: diskWriteRaw failed: %s: "
|
||||
"score %V: date %s part=%s block %ud: %r\n",
|
||||
disk2file(disk), b->score,
|
||||
ctime(time(0)),
|
||||
partname[b->part], b->addr);
|
||||
break;
|
||||
}
|
||||
if(p != buf)
|
||||
blockSetIOState(b, BioClean);
|
||||
else
|
||||
blockSetIOState(b, BioDirty);
|
||||
break;
|
||||
}
|
||||
|
||||
blockPut(b); /* remove extra reference, unlock */
|
||||
vtLock(disk->lk);
|
||||
disk->nqueue--;
|
||||
if(disk->nqueue == QueueSize-1)
|
||||
vtWakeup(disk->flow);
|
||||
if(disk->nqueue == 0)
|
||||
vtWakeup(disk->flush);
|
||||
if(Timing)
|
||||
nio++;
|
||||
}
|
||||
Done:
|
||||
//fprint(2, "diskThread done\n");
|
||||
disk->ref--;
|
||||
vtWakeup(disk->die);
|
||||
vtUnlock(disk->lk);
|
||||
vtMemFree(buf);
|
||||
}
|
||||
86
src/cmd/fossil/dump.c
Normal file
86
src/cmd/fossil/dump.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Clumsy hack to take snapshots and dumps.
|
||||
*/
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: fossil/dump [-i snap-interval] [-n name] fscons /n/fossil\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
char*
|
||||
snapnow(void)
|
||||
{
|
||||
Tm t;
|
||||
static char buf[100];
|
||||
|
||||
t = *localtime(time(0)-5*60*60); /* take dumps at 5:00 am */
|
||||
|
||||
sprint(buf, "archive/%d/%02d%02d", t.year+1900, t.mon+1, t.mday);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int onlyarchive, cons, s;
|
||||
ulong t, i;
|
||||
char *name;
|
||||
|
||||
name = "main";
|
||||
s = 0;
|
||||
onlyarchive = 0;
|
||||
i = 60*60; /* one hour */
|
||||
ARGBEGIN{
|
||||
case 'i':
|
||||
i = atoi(EARGF(usage()));
|
||||
if(i == 0){
|
||||
onlyarchive = 1;
|
||||
i = 60*60;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
name = EARGF(usage());
|
||||
break;
|
||||
case 's':
|
||||
s = atoi(EARGF(usage()));
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(argc != 2)
|
||||
usage();
|
||||
|
||||
if((cons = open(argv[0], OWRITE)) < 0)
|
||||
sysfatal("open %s: %r", argv[0]);
|
||||
|
||||
if(chdir(argv[1]) < 0)
|
||||
sysfatal("chdir %s: %r", argv[1]);
|
||||
|
||||
rfork(RFNOTEG);
|
||||
switch(fork()){
|
||||
case -1:
|
||||
sysfatal("fork: %r");
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
exits(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* pause at boot time to let clock stabilize.
|
||||
*/
|
||||
if(s)
|
||||
sleep(s*1000);
|
||||
|
||||
for(;;){
|
||||
if(access(snapnow(), AEXIST) < 0)
|
||||
fprint(cons, "\nfsys %s snap -a\n", name);
|
||||
t = time(0);
|
||||
sleep((i - t%i)*1000+200);
|
||||
if(!onlyarchive)
|
||||
fprint(cons, "\nfsys %s snap\n", name);
|
||||
}
|
||||
}
|
||||
51
src/cmd/fossil/epoch.c
Normal file
51
src/cmd/fossil/epoch.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
uchar buf[65536];
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: fossil/epoch fs [new-low-epoch]\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
Header h;
|
||||
Super s;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc == 0 || argc > 2)
|
||||
usage();
|
||||
|
||||
if((fd = open(argv[0], argc==2 ? ORDWR : OREAD)) < 0)
|
||||
sysfatal("open %s: %r", argv[0]);
|
||||
|
||||
if(pread(fd, buf, HeaderSize, HeaderOffset) != HeaderSize)
|
||||
sysfatal("reading header: %r");
|
||||
if(!headerUnpack(&h, buf))
|
||||
sysfatal("unpacking header: %r");
|
||||
|
||||
if(pread(fd, buf, h.blockSize, (vlong)h.super*h.blockSize) != h.blockSize)
|
||||
sysfatal("reading super block: %r");
|
||||
|
||||
if(!superUnpack(&s, buf))
|
||||
sysfatal("unpacking super block: %r");
|
||||
|
||||
print("epoch %d\n", s.epochLow);
|
||||
if(argc == 2){
|
||||
s.epochLow = strtoul(argv[1], 0, 0);
|
||||
superPack(&s, buf);
|
||||
if(pwrite(fd, buf, h.blockSize, (vlong)h.super*h.blockSize) != h.blockSize)
|
||||
sysfatal("writing super block: %r");
|
||||
}
|
||||
exits(0);
|
||||
}
|
||||
38
src/cmd/fossil/error.c
Normal file
38
src/cmd/fossil/error.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
char EBadAddr[] = "illegal block address";
|
||||
char EBadDir[] = "corrupted directory entry";
|
||||
char EBadEntry[] = "corrupted file entry";
|
||||
char EBadLabel[] = "corrupted block label";
|
||||
char EBadMeta[] = "corrupted meta data";
|
||||
char EBadMode[] = "illegal mode";
|
||||
char EBadOffset[] = "illegal offset";
|
||||
char EBadPath[] = "illegal path element";
|
||||
char EBadRoot[] = "root of file system is corrupted";
|
||||
char EBadSuper[] = "corrupted super block";
|
||||
char EBlockTooBig[] = "block too big";
|
||||
char ECacheFull[] = "no free blocks in memory cache";
|
||||
char EConvert[] = "protocol botch";
|
||||
char EExists[] = "file already exists";
|
||||
char EFsFill[] = "file system is full";
|
||||
char EIO[] = "i/o error";
|
||||
char EInUse[] = "file is in use";
|
||||
char ELabelMismatch[] = "block label mismatch";
|
||||
char ENilBlock[] = "illegal block address";
|
||||
char ENoDir[] = "directory entry is not allocated";
|
||||
char ENoFile[] = "file does not exist";
|
||||
char ENotDir[] = "not a directory";
|
||||
char ENotEmpty[] = "directory not empty";
|
||||
char ENotFile[] = "not a file";
|
||||
char EReadOnly[] = "file is read only";
|
||||
char ERemoved[] = "file has been removed";
|
||||
char ENotArchived[] = "file is not archived";
|
||||
char EResize[] = "only support truncation to zero length";
|
||||
char ERoot[] = "cannot remove root";
|
||||
char ESnapOld[] = "snapshot has been deleted";
|
||||
char ESnapRO[] = "snapshot is read only";
|
||||
char ETooBig[] = "file too big";
|
||||
char EVentiIO[] = "venti i/o error";
|
||||
33
src/cmd/fossil/error.h
Normal file
33
src/cmd/fossil/error.h
Normal file
@ -0,0 +1,33 @@
|
||||
extern char EBadAddr[];
|
||||
extern char EBadDir[];
|
||||
extern char EBadEntry[];
|
||||
extern char EBadLabel[];
|
||||
extern char EBadMeta[];
|
||||
extern char EBadMode[];
|
||||
extern char EBadOffset[];
|
||||
extern char EBadPath[];
|
||||
extern char EBadRoot[];
|
||||
extern char EBadSuper[];
|
||||
extern char EBlockTooBig[];
|
||||
extern char ECacheFull[];
|
||||
extern char EConvert[];
|
||||
extern char EExists[];
|
||||
extern char EFsFill[];
|
||||
extern char EIO[];
|
||||
extern char EInUse[];
|
||||
extern char ELabelMismatch[];
|
||||
extern char ENilBlock[];
|
||||
extern char ENoDir[];
|
||||
extern char ENoFile[];
|
||||
extern char ENotDir[];
|
||||
extern char ENotEmpty[];
|
||||
extern char ENotFile[];
|
||||
extern char EReadOnly[];
|
||||
extern char ERemoved[];
|
||||
extern char ENotArchived[];
|
||||
extern char EResize[];
|
||||
extern char ERoot[];
|
||||
extern char ESnapOld[];
|
||||
extern char ESnapRO[];
|
||||
extern char ETooBig[];
|
||||
extern char EVentiIO[];
|
||||
1860
src/cmd/fossil/file.c
Normal file
1860
src/cmd/fossil/file.c
Normal file
File diff suppressed because it is too large
Load Diff
118
src/cmd/fossil/flchk.c
Normal file
118
src/cmd/fossil/flchk.c
Normal file
@ -0,0 +1,118 @@
|
||||
#include "stdinc.h"
|
||||
#include <bio.h>
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
Biobuf bout;
|
||||
Fsck fsck;
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s [-c cachesize] [-h host] file\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
#pragma varargck argpos flprint 1
|
||||
|
||||
static int
|
||||
flprint(char *fmt, ...)
|
||||
{
|
||||
int n;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
n = Bvprint(&bout, fmt, arg);
|
||||
va_end(arg);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
flclre(Fsck*, Block *b, int o)
|
||||
{
|
||||
Bprint(&bout, "# clre 0x%ux %d\n", b->addr, o);
|
||||
}
|
||||
|
||||
static void
|
||||
flclrp(Fsck*, Block *b, int o)
|
||||
{
|
||||
Bprint(&bout, "# clrp 0x%ux %d\n", b->addr, o);
|
||||
}
|
||||
|
||||
static void
|
||||
flclri(Fsck*, char *name, MetaBlock*, int, Block*)
|
||||
{
|
||||
Bprint(&bout, "# clri %s\n", name);
|
||||
}
|
||||
|
||||
static void
|
||||
flclose(Fsck*, Block *b, u32int epoch)
|
||||
{
|
||||
Bprint(&bout, "# bclose 0x%ux %ud\n", b->addr, epoch);
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int csize = 1000;
|
||||
VtSession *z;
|
||||
char *host = nil;
|
||||
|
||||
fsck.useventi = 1;
|
||||
Binit(&bout, 1, OWRITE);
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
case 'c':
|
||||
csize = atoi(ARGF());
|
||||
if(csize <= 0)
|
||||
usage();
|
||||
break;
|
||||
case 'f':
|
||||
fsck.useventi = 0;
|
||||
break;
|
||||
case 'h':
|
||||
host = ARGF();
|
||||
break;
|
||||
case 'v':
|
||||
fsck.printdirs = 1;
|
||||
break;
|
||||
}ARGEND;
|
||||
|
||||
if(argc != 1)
|
||||
usage();
|
||||
|
||||
vtAttach();
|
||||
|
||||
fmtinstall('L', labelFmt);
|
||||
fmtinstall('V', scoreFmt);
|
||||
fmtinstall('R', vtErrFmt);
|
||||
|
||||
/*
|
||||
* Connect to Venti.
|
||||
*/
|
||||
z = vtDial(host, 0);
|
||||
if(z == nil){
|
||||
if(fsck.useventi)
|
||||
vtFatal("could not connect to server: %s", vtGetError());
|
||||
}else if(!vtConnect(z, 0))
|
||||
vtFatal("vtConnect: %s", vtGetError());
|
||||
|
||||
/*
|
||||
* Initialize file system.
|
||||
*/
|
||||
fsck.fs = fsOpen(argv[0], z, csize, OReadOnly);
|
||||
if(fsck.fs == nil)
|
||||
vtFatal("could not open file system: %R");
|
||||
|
||||
fsck.print = flprint;
|
||||
fsck.clre = flclre;
|
||||
fsck.clrp = flclrp;
|
||||
fsck.close = flclose;
|
||||
fsck.clri = flclri;
|
||||
|
||||
fsCheck(&fsck);
|
||||
|
||||
exits(0);
|
||||
}
|
||||
|
||||
571
src/cmd/fossil/flfmt.c
Normal file
571
src/cmd/fossil/flfmt.c
Normal file
@ -0,0 +1,571 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "flfmt9660.h"
|
||||
|
||||
#define blockWrite _blockWrite /* hack */
|
||||
|
||||
static void usage(void);
|
||||
static u64int fdsize(int fd);
|
||||
static void partition(int fd, int bsize, Header *h);
|
||||
static u64int unittoull(char *s);
|
||||
static u32int blockAlloc(int type, u32int tag);
|
||||
static void blockRead(int part, u32int addr);
|
||||
static void blockWrite(int part, u32int addr);
|
||||
static void superInit(char *label, u32int root, uchar[VtScoreSize]);
|
||||
static void rootMetaInit(Entry *e);
|
||||
static u32int rootInit(Entry *e);
|
||||
static void topLevel(char *name);
|
||||
static int parseScore(uchar[VtScoreSize], char*);
|
||||
static u32int ventiRoot(char*, char*);
|
||||
static VtSession *z;
|
||||
|
||||
#define TWID64 ((u64int)~(u64int)0)
|
||||
|
||||
Disk *disk;
|
||||
Fs *fs;
|
||||
uchar *buf;
|
||||
int bsize = 8*1024;
|
||||
u64int qid = 1;
|
||||
int iso9660off;
|
||||
char *iso9660file;
|
||||
|
||||
int
|
||||
confirm(char *msg)
|
||||
{
|
||||
char buf[100];
|
||||
int n;
|
||||
|
||||
fprint(2, "%s [y/n]: ", msg);
|
||||
n = read(0, buf, sizeof buf - 1);
|
||||
if(n <= 0)
|
||||
return 0;
|
||||
if(buf[0] == 'y')
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd, force;
|
||||
Header h;
|
||||
ulong bn;
|
||||
Entry e;
|
||||
char *label = "vfs";
|
||||
char *host = nil;
|
||||
char *score = nil;
|
||||
u32int root;
|
||||
Dir *d;
|
||||
|
||||
force = 0;
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
case 'b':
|
||||
bsize = unittoull(EARGF(usage()));
|
||||
if(bsize == ~0)
|
||||
usage();
|
||||
break;
|
||||
case 'h':
|
||||
host = EARGF(usage());
|
||||
break;
|
||||
case 'i':
|
||||
iso9660file = EARGF(usage());
|
||||
iso9660off = atoi(EARGF(usage()));
|
||||
break;
|
||||
case 'l':
|
||||
label = EARGF(usage());
|
||||
break;
|
||||
case 'v':
|
||||
score = EARGF(usage());
|
||||
break;
|
||||
|
||||
/*
|
||||
* This is -y instead of -f because flchk has a
|
||||
* (frequently used) -f option. I type flfmt instead
|
||||
* of flchk all the time, and want to make it hard
|
||||
* to reformat my file system accidentally.
|
||||
*/
|
||||
case 'y':
|
||||
force = 1;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(argc != 1)
|
||||
usage();
|
||||
|
||||
if(iso9660file && score)
|
||||
vtFatal("cannot use -i with -v");
|
||||
|
||||
vtAttach();
|
||||
|
||||
fmtinstall('V', scoreFmt);
|
||||
fmtinstall('R', vtErrFmt);
|
||||
fmtinstall('L', labelFmt);
|
||||
|
||||
fd = open(argv[0], ORDWR);
|
||||
if(fd < 0)
|
||||
vtFatal("could not open file: %s: %r", argv[0]);
|
||||
|
||||
buf = vtMemAllocZ(bsize);
|
||||
if(pread(fd, buf, bsize, HeaderOffset) != bsize)
|
||||
vtFatal("could not read fs header block: %r");
|
||||
|
||||
if(headerUnpack(&h, buf) && !force
|
||||
&& !confirm("fs header block already exists; are you sure?"))
|
||||
goto Out;
|
||||
|
||||
if((d = dirfstat(fd)) == nil)
|
||||
vtFatal("dirfstat: %r");
|
||||
|
||||
if(d->type == 'M' && !force
|
||||
&& !confirm("fs file is mounted via devmnt (is not a kernel device); are you sure?"))
|
||||
goto Out;
|
||||
|
||||
partition(fd, bsize, &h);
|
||||
headerPack(&h, buf);
|
||||
if(pwrite(fd, buf, bsize, HeaderOffset) < bsize)
|
||||
vtFatal("could not write fs header: %r");
|
||||
|
||||
disk = diskAlloc(fd);
|
||||
if(disk == nil)
|
||||
vtFatal("could not open disk: %r");
|
||||
|
||||
if(iso9660file)
|
||||
iso9660init(fd, &h, iso9660file, iso9660off);
|
||||
|
||||
/* zero labels */
|
||||
memset(buf, 0, bsize);
|
||||
for(bn = 0; bn < diskSize(disk, PartLabel); bn++)
|
||||
blockWrite(PartLabel, bn);
|
||||
|
||||
if(iso9660file)
|
||||
iso9660labels(disk, buf, blockWrite);
|
||||
|
||||
if(score)
|
||||
root = ventiRoot(host, score);
|
||||
else{
|
||||
rootMetaInit(&e);
|
||||
root = rootInit(&e);
|
||||
}
|
||||
|
||||
superInit(label, root, vtZeroScore);
|
||||
diskFree(disk);
|
||||
|
||||
if(score == nil)
|
||||
topLevel(argv[0]);
|
||||
|
||||
Out:
|
||||
vtDetach();
|
||||
exits(0);
|
||||
}
|
||||
|
||||
static u64int
|
||||
fdsize(int fd)
|
||||
{
|
||||
Dir *dir;
|
||||
u64int size;
|
||||
|
||||
dir = dirfstat(fd);
|
||||
if(dir == nil)
|
||||
vtFatal("could not stat file: %r");
|
||||
size = dir->length;
|
||||
free(dir);
|
||||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s [-b blocksize] [-h host] [-i file offset] "
|
||||
"[-l label] [-v score] [-y] file\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
static void
|
||||
partition(int fd, int bsize, Header *h)
|
||||
{
|
||||
ulong nblock, ndata, nlabel;
|
||||
ulong lpb;
|
||||
|
||||
if(bsize % 512 != 0)
|
||||
sysfatal("block size must be a multiple of 512 bytes");
|
||||
if(bsize > VtMaxLumpSize)
|
||||
sysfatal("block size must be less than %d", VtMaxLumpSize);
|
||||
|
||||
memset(h, 0, sizeof(*h));
|
||||
h->blockSize = bsize;
|
||||
|
||||
lpb = bsize/LabelSize;
|
||||
|
||||
nblock = fdsize(fd)/bsize;
|
||||
|
||||
/* sanity check */
|
||||
if(nblock < (HeaderOffset*10)/bsize)
|
||||
vtFatal("file too small");
|
||||
|
||||
h->super = (HeaderOffset + 2*bsize)/bsize;
|
||||
h->label = h->super + 1;
|
||||
ndata = ((u64int)lpb)*(nblock - h->label)/(lpb+1);
|
||||
nlabel = (ndata + lpb - 1)/lpb;
|
||||
h->data = h->label + nlabel;
|
||||
h->end = h->data + ndata;
|
||||
|
||||
}
|
||||
|
||||
static u32int
|
||||
tagGen(void)
|
||||
{
|
||||
u32int tag;
|
||||
|
||||
for(;;){
|
||||
tag = lrand();
|
||||
if(tag > RootTag)
|
||||
break;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void
|
||||
entryInit(Entry *e)
|
||||
{
|
||||
e->gen = 0;
|
||||
e->dsize = bsize;
|
||||
e->psize = bsize/VtEntrySize*VtEntrySize;
|
||||
e->flags = VtEntryActive;
|
||||
e->depth = 0;
|
||||
e->size = 0;
|
||||
memmove(e->score, vtZeroScore, VtScoreSize);
|
||||
e->tag = tagGen();
|
||||
e->snap = 0;
|
||||
e->archive = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rootMetaInit(Entry *e)
|
||||
{
|
||||
u32int addr;
|
||||
u32int tag;
|
||||
DirEntry de;
|
||||
MetaBlock mb;
|
||||
MetaEntry me;
|
||||
|
||||
memset(&de, 0, sizeof(de));
|
||||
de.elem = vtStrDup("root");
|
||||
de.entry = 0;
|
||||
de.gen = 0;
|
||||
de.mentry = 1;
|
||||
de.mgen = 0;
|
||||
de.size = 0;
|
||||
de.qid = qid++;
|
||||
de.uid = vtStrDup("adm");
|
||||
de.gid = vtStrDup("adm");
|
||||
de.mid = vtStrDup("adm");
|
||||
de.mtime = time(0);
|
||||
de.mcount = 0;
|
||||
de.ctime = time(0);
|
||||
de.atime = time(0);
|
||||
de.mode = ModeDir | 0555;
|
||||
|
||||
tag = tagGen();
|
||||
addr = blockAlloc(BtData, tag);
|
||||
|
||||
/* build up meta block */
|
||||
memset(buf, 0, bsize);
|
||||
mbInit(&mb, buf, bsize, bsize/100);
|
||||
me.size = deSize(&de);
|
||||
me.p = mbAlloc(&mb, me.size);
|
||||
assert(me.p != nil);
|
||||
dePack(&de, &me);
|
||||
mbInsert(&mb, 0, &me);
|
||||
mbPack(&mb);
|
||||
blockWrite(PartData, addr);
|
||||
deCleanup(&de);
|
||||
|
||||
/* build up entry for meta block */
|
||||
entryInit(e);
|
||||
e->flags |= VtEntryLocal;
|
||||
e->size = bsize;
|
||||
e->tag = tag;
|
||||
localToGlobal(addr, e->score);
|
||||
}
|
||||
|
||||
static u32int
|
||||
rootInit(Entry *e)
|
||||
{
|
||||
ulong addr;
|
||||
u32int tag;
|
||||
|
||||
tag = tagGen();
|
||||
|
||||
addr = blockAlloc(BtDir, tag);
|
||||
memset(buf, 0, bsize);
|
||||
|
||||
/* root meta data is in the third entry */
|
||||
entryPack(e, buf, 2);
|
||||
|
||||
entryInit(e);
|
||||
e->flags |= VtEntryDir;
|
||||
entryPack(e, buf, 0);
|
||||
|
||||
entryInit(e);
|
||||
entryPack(e, buf, 1);
|
||||
|
||||
blockWrite(PartData, addr);
|
||||
|
||||
entryInit(e);
|
||||
e->flags |= VtEntryLocal|VtEntryDir;
|
||||
e->size = VtEntrySize*3;
|
||||
e->tag = tag;
|
||||
localToGlobal(addr, e->score);
|
||||
|
||||
addr = blockAlloc(BtDir, RootTag);
|
||||
memset(buf, 0, bsize);
|
||||
entryPack(e, buf, 0);
|
||||
|
||||
blockWrite(PartData, addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
static u32int
|
||||
blockAlloc(int type, u32int tag)
|
||||
{
|
||||
static u32int addr;
|
||||
Label l;
|
||||
int lpb;
|
||||
|
||||
lpb = bsize/LabelSize;
|
||||
|
||||
blockRead(PartLabel, addr/lpb);
|
||||
if(!labelUnpack(&l, buf, addr % lpb))
|
||||
vtFatal("bad label: %r");
|
||||
if(l.state != BsFree)
|
||||
vtFatal("want to allocate block already in use");
|
||||
l.epoch = 1;
|
||||
l.epochClose = ~(u32int)0;
|
||||
l.type = type;
|
||||
l.state = BsAlloc;
|
||||
l.tag = tag;
|
||||
labelPack(&l, buf, addr % lpb);
|
||||
blockWrite(PartLabel, addr/lpb);
|
||||
return addr++;
|
||||
}
|
||||
|
||||
static void
|
||||
superInit(char *label, u32int root, uchar score[VtScoreSize])
|
||||
{
|
||||
Super s;
|
||||
|
||||
memset(buf, 0, bsize);
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.version = SuperVersion;
|
||||
s.epochLow = 1;
|
||||
s.epochHigh = 1;
|
||||
s.qid = qid;
|
||||
s.active = root;
|
||||
s.next = NilBlock;
|
||||
s.current = NilBlock;
|
||||
strecpy(s.name, s.name+sizeof(s.name), label);
|
||||
memmove(s.last, score, VtScoreSize);
|
||||
|
||||
superPack(&s, buf);
|
||||
blockWrite(PartSuper, 0);
|
||||
}
|
||||
|
||||
static u64int
|
||||
unittoull(char *s)
|
||||
{
|
||||
char *es;
|
||||
u64int n;
|
||||
|
||||
if(s == nil)
|
||||
return TWID64;
|
||||
n = strtoul(s, &es, 0);
|
||||
if(*es == 'k' || *es == 'K'){
|
||||
n *= 1024;
|
||||
es++;
|
||||
}else if(*es == 'm' || *es == 'M'){
|
||||
n *= 1024*1024;
|
||||
es++;
|
||||
}else if(*es == 'g' || *es == 'G'){
|
||||
n *= 1024*1024*1024;
|
||||
es++;
|
||||
}
|
||||
if(*es != '\0')
|
||||
return TWID64;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
blockRead(int part, u32int addr)
|
||||
{
|
||||
if(!diskReadRaw(disk, part, addr, buf))
|
||||
vtFatal("read failed: %r");
|
||||
}
|
||||
|
||||
static void
|
||||
blockWrite(int part, u32int addr)
|
||||
{
|
||||
if(!diskWriteRaw(disk, part, addr, buf))
|
||||
vtFatal("write failed: %r");
|
||||
}
|
||||
|
||||
static void
|
||||
addFile(File *root, char *name, uint mode)
|
||||
{
|
||||
File *f;
|
||||
|
||||
f = fileCreate(root, name, mode | ModeDir, "adm");
|
||||
if(f == nil)
|
||||
vtFatal("could not create file: %s: %r", name);
|
||||
fileDecRef(f);
|
||||
}
|
||||
|
||||
static void
|
||||
topLevel(char *name)
|
||||
{
|
||||
Fs *fs;
|
||||
File *root;
|
||||
|
||||
/* ok, now we can open as a fs */
|
||||
fs = fsOpen(name, z, 100, OReadWrite);
|
||||
if(fs == nil)
|
||||
vtFatal("could not open file system: %r");
|
||||
vtRLock(fs->elk);
|
||||
root = fsGetRoot(fs);
|
||||
if(root == nil)
|
||||
vtFatal("could not open root: %r");
|
||||
addFile(root, "active", 0555);
|
||||
addFile(root, "archive", 0555);
|
||||
addFile(root, "snapshot", 0555);
|
||||
fileDecRef(root);
|
||||
if(iso9660file)
|
||||
iso9660copy(fs);
|
||||
vtRUnlock(fs->elk);
|
||||
fsClose(fs);
|
||||
}
|
||||
|
||||
static int
|
||||
ventiRead(uchar score[VtScoreSize], int type)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = vtRead(z, score, type, buf, bsize);
|
||||
if(n < 0)
|
||||
vtFatal("ventiRead %V (%d) failed: %R", score, type);
|
||||
vtZeroExtend(type, buf, n, bsize);
|
||||
return n;
|
||||
}
|
||||
|
||||
static u32int
|
||||
ventiRoot(char *host, char *s)
|
||||
{
|
||||
int i, n;
|
||||
uchar score[VtScoreSize];
|
||||
u32int addr, tag;
|
||||
DirEntry de;
|
||||
MetaBlock mb;
|
||||
MetaEntry me;
|
||||
Entry e;
|
||||
VtRoot root;
|
||||
|
||||
if(!parseScore(score, s))
|
||||
vtFatal("bad score '%s'", s);
|
||||
|
||||
if((z = vtDial(host, 0)) == nil
|
||||
|| !vtConnect(z, nil))
|
||||
vtFatal("connect to venti: %R");
|
||||
|
||||
tag = tagGen();
|
||||
addr = blockAlloc(BtDir, tag);
|
||||
|
||||
ventiRead(score, VtRootType);
|
||||
if(!vtRootUnpack(&root, buf))
|
||||
vtFatal("corrupted root: vtRootUnpack");
|
||||
n = ventiRead(root.score, VtDirType);
|
||||
|
||||
/*
|
||||
* Fossil's vac archives start with an extra layer of source,
|
||||
* but vac's don't.
|
||||
*/
|
||||
if(n <= 2*VtEntrySize){
|
||||
if(!entryUnpack(&e, buf, 0))
|
||||
vtFatal("bad root: top entry");
|
||||
n = ventiRead(e.score, VtDirType);
|
||||
}
|
||||
|
||||
/*
|
||||
* There should be three root sources (and nothing else) here.
|
||||
*/
|
||||
for(i=0; i<3; i++){
|
||||
if(!entryUnpack(&e, buf, i)
|
||||
|| !(e.flags&VtEntryActive)
|
||||
|| e.psize < 256
|
||||
|| e.dsize < 256)
|
||||
vtFatal("bad root: entry %d", i);
|
||||
fprint(2, "%V\n", e.score);
|
||||
}
|
||||
if(n > 3*VtEntrySize)
|
||||
vtFatal("bad root: entry count");
|
||||
|
||||
blockWrite(PartData, addr);
|
||||
|
||||
/*
|
||||
* Maximum qid is recorded in root's msource, entry #2 (conveniently in e).
|
||||
*/
|
||||
ventiRead(e.score, VtDataType);
|
||||
if(!mbUnpack(&mb, buf, bsize))
|
||||
vtFatal("bad root: mbUnpack");
|
||||
meUnpack(&me, &mb, 0);
|
||||
if(!deUnpack(&de, &me))
|
||||
vtFatal("bad root: dirUnpack");
|
||||
if(!de.qidSpace)
|
||||
vtFatal("bad root: no qidSpace");
|
||||
qid = de.qidMax;
|
||||
|
||||
/*
|
||||
* Recreate the top layer of source.
|
||||
*/
|
||||
entryInit(&e);
|
||||
e.flags |= VtEntryLocal|VtEntryDir;
|
||||
e.size = VtEntrySize*3;
|
||||
e.tag = tag;
|
||||
localToGlobal(addr, e.score);
|
||||
|
||||
addr = blockAlloc(BtDir, RootTag);
|
||||
memset(buf, 0, bsize);
|
||||
entryPack(&e, buf, 0);
|
||||
blockWrite(PartData, addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int
|
||||
parseScore(uchar *score, char *buf)
|
||||
{
|
||||
int i, c;
|
||||
|
||||
memset(score, 0, VtScoreSize);
|
||||
|
||||
if(strlen(buf) < VtScoreSize*2)
|
||||
return 0;
|
||||
for(i=0; i<VtScoreSize*2; i++){
|
||||
if(buf[i] >= '0' && buf[i] <= '9')
|
||||
c = buf[i] - '0';
|
||||
else if(buf[i] >= 'a' && buf[i] <= 'f')
|
||||
c = buf[i] - 'a' + 10;
|
||||
else if(buf[i] >= 'A' && buf[i] <= 'F')
|
||||
c = buf[i] - 'A' + 10;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if((i & 1) == 0)
|
||||
c <<= 4;
|
||||
|
||||
score[i>>1] |= c;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
565
src/cmd/fossil/flfmt9660.c
Normal file
565
src/cmd/fossil/flfmt9660.c
Normal file
@ -0,0 +1,565 @@
|
||||
/*
|
||||
* Initialize a fossil file system from an ISO9660 image already in the
|
||||
* file system. This is a fairly bizarre thing to do, but it lets us generate
|
||||
* installation CDs that double as valid Plan 9 disk partitions.
|
||||
* People having trouble booting the CD can just copy it into a disk
|
||||
* partition and you've got a working Plan 9 system.
|
||||
*
|
||||
* I've tried hard to keep all the associated cruft in this file.
|
||||
* If you deleted this file and cut out the three calls into it from flfmt.c,
|
||||
* no traces would remain.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "flfmt9660.h"
|
||||
#include <bio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static Biobuf *b;
|
||||
|
||||
enum{
|
||||
Tag = 0x96609660,
|
||||
Blocksize = 2048,
|
||||
};
|
||||
|
||||
#pragma varargck type "s" uchar*
|
||||
#pragma varargck type "L" uchar*
|
||||
#pragma varargck type "B" uchar*
|
||||
#pragma varargck type "N" uchar*
|
||||
#pragma varargck type "T" uchar*
|
||||
#pragma varargck type "D" uchar*
|
||||
|
||||
typedef struct Voldesc Voldesc;
|
||||
struct Voldesc {
|
||||
uchar magic[8]; /* 0x01, "CD001", 0x01, 0x00 */
|
||||
uchar systemid[32]; /* system identifier */
|
||||
uchar volumeid[32]; /* volume identifier */
|
||||
uchar unused[8]; /* character set in secondary desc */
|
||||
uchar volsize[8]; /* volume size */
|
||||
uchar charset[32];
|
||||
uchar volsetsize[4]; /* volume set size = 1 */
|
||||
uchar volseqnum[4]; /* volume sequence number = 1 */
|
||||
uchar blocksize[4]; /* logical block size */
|
||||
uchar pathsize[8]; /* path table size */
|
||||
uchar lpathloc[4]; /* Lpath */
|
||||
uchar olpathloc[4]; /* optional Lpath */
|
||||
uchar mpathloc[4]; /* Mpath */
|
||||
uchar ompathloc[4]; /* optional Mpath */
|
||||
uchar rootdir[34]; /* root directory */
|
||||
uchar volsetid[128]; /* volume set identifier */
|
||||
uchar publisher[128];
|
||||
uchar prepid[128]; /* data preparer identifier */
|
||||
uchar applid[128]; /* application identifier */
|
||||
uchar notice[37]; /* copyright notice file */
|
||||
uchar abstract[37]; /* abstract file */
|
||||
uchar biblio[37]; /* bibliographic file */
|
||||
uchar cdate[17]; /* creation date */
|
||||
uchar mdate[17]; /* modification date */
|
||||
uchar xdate[17]; /* expiration date */
|
||||
uchar edate[17]; /* effective date */
|
||||
uchar fsvers; /* file system version = 1 */
|
||||
};
|
||||
|
||||
static void
|
||||
dumpbootvol(void *a)
|
||||
{
|
||||
Voldesc *v;
|
||||
|
||||
v = a;
|
||||
print("magic %.2ux %.5s %.2ux %2ux\n",
|
||||
v->magic[0], v->magic+1, v->magic[6], v->magic[7]);
|
||||
if(v->magic[0] == 0xFF)
|
||||
return;
|
||||
|
||||
print("system %.32T\n", v->systemid);
|
||||
print("volume %.32T\n", v->volumeid);
|
||||
print("volume size %.4N\n", v->volsize);
|
||||
print("charset %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n",
|
||||
v->charset[0], v->charset[1], v->charset[2], v->charset[3],
|
||||
v->charset[4], v->charset[5], v->charset[6], v->charset[7]);
|
||||
print("volume set size %.2N\n", v->volsetsize);
|
||||
print("volume sequence number %.2N\n", v->volseqnum);
|
||||
print("logical block size %.2N\n", v->blocksize);
|
||||
print("path size %.4L\n", v->pathsize);
|
||||
print("lpath loc %.4L\n", v->lpathloc);
|
||||
print("opt lpath loc %.4L\n", v->olpathloc);
|
||||
print("mpath loc %.4B\n", v->mpathloc);
|
||||
print("opt mpath loc %.4B\n", v->ompathloc);
|
||||
print("rootdir %D\n", v->rootdir);
|
||||
print("volume set identifier %.128T\n", v->volsetid);
|
||||
print("publisher %.128T\n", v->publisher);
|
||||
print("preparer %.128T\n", v->prepid);
|
||||
print("application %.128T\n", v->applid);
|
||||
print("notice %.37T\n", v->notice);
|
||||
print("abstract %.37T\n", v->abstract);
|
||||
print("biblio %.37T\n", v->biblio);
|
||||
print("creation date %.17s\n", v->cdate);
|
||||
print("modification date %.17s\n", v->mdate);
|
||||
print("expiration date %.17s\n", v->xdate);
|
||||
print("effective date %.17s\n", v->edate);
|
||||
print("fs version %d\n", v->fsvers);
|
||||
}
|
||||
|
||||
typedef struct Cdir Cdir;
|
||||
struct Cdir {
|
||||
uchar len;
|
||||
uchar xlen;
|
||||
uchar dloc[8];
|
||||
uchar dlen[8];
|
||||
uchar date[7];
|
||||
uchar flags;
|
||||
uchar unitsize;
|
||||
uchar gapsize;
|
||||
uchar volseqnum[4];
|
||||
uchar namelen;
|
||||
uchar name[1]; /* chumminess */
|
||||
};
|
||||
#pragma varargck type "D" Cdir*
|
||||
|
||||
static int
|
||||
Dfmt(Fmt *fmt)
|
||||
{
|
||||
char buf[128];
|
||||
Cdir *c;
|
||||
|
||||
c = va_arg(fmt->args, Cdir*);
|
||||
if(c->namelen == 1 && c->name[0] == '\0' || c->name[0] == '\001') {
|
||||
snprint(buf, sizeof buf, ".%s dloc %.4N dlen %.4N",
|
||||
c->name[0] ? "." : "", c->dloc, c->dlen);
|
||||
} else {
|
||||
snprint(buf, sizeof buf, "%.*T dloc %.4N dlen %.4N", c->namelen, c->name,
|
||||
c->dloc, c->dlen);
|
||||
}
|
||||
fmtstrcpy(fmt, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char longc, shortc;
|
||||
static void
|
||||
bigend(void)
|
||||
{
|
||||
longc = 'B';
|
||||
}
|
||||
|
||||
static void
|
||||
littleend(void)
|
||||
{
|
||||
longc = 'L';
|
||||
}
|
||||
|
||||
static ulong
|
||||
big(void *a, int n)
|
||||
{
|
||||
uchar *p;
|
||||
ulong v;
|
||||
int i;
|
||||
|
||||
p = a;
|
||||
v = 0;
|
||||
for(i=0; i<n; i++)
|
||||
v = (v<<8) | *p++;
|
||||
return v;
|
||||
}
|
||||
|
||||
static ulong
|
||||
little(void *a, int n)
|
||||
{
|
||||
uchar *p;
|
||||
ulong v;
|
||||
int i;
|
||||
|
||||
p = a;
|
||||
v = 0;
|
||||
for(i=0; i<n; i++)
|
||||
v |= (*p++<<(i*8));
|
||||
return v;
|
||||
}
|
||||
|
||||
/* numbers in big or little endian. */
|
||||
static int
|
||||
BLfmt(Fmt *fmt)
|
||||
{
|
||||
ulong v;
|
||||
uchar *p;
|
||||
char buf[20];
|
||||
|
||||
p = va_arg(fmt->args, uchar*);
|
||||
|
||||
if(!(fmt->flags&FmtPrec)) {
|
||||
fmtstrcpy(fmt, "*BL*");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(fmt->r == 'B')
|
||||
v = big(p, fmt->prec);
|
||||
else
|
||||
v = little(p, fmt->prec);
|
||||
|
||||
sprint(buf, "0x%.*lux", fmt->prec*2, v);
|
||||
fmt->flags &= ~FmtPrec;
|
||||
fmtstrcpy(fmt, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* numbers in both little and big endian */
|
||||
static int
|
||||
Nfmt(Fmt *fmt)
|
||||
{
|
||||
char buf[100];
|
||||
uchar *p;
|
||||
|
||||
p = va_arg(fmt->args, uchar*);
|
||||
|
||||
sprint(buf, "%.*L %.*B", fmt->prec, p, fmt->prec, p+fmt->prec);
|
||||
fmt->flags &= ~FmtPrec;
|
||||
fmtstrcpy(fmt, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asciiTfmt(Fmt *fmt)
|
||||
{
|
||||
char *p, buf[256];
|
||||
int i;
|
||||
|
||||
p = va_arg(fmt->args, char*);
|
||||
for(i=0; i<fmt->prec; i++)
|
||||
buf[i] = *p++;
|
||||
buf[i] = '\0';
|
||||
for(p=buf+strlen(buf); p>buf && p[-1]==' '; p--)
|
||||
;
|
||||
p[0] = '\0';
|
||||
fmt->flags &= ~FmtPrec;
|
||||
fmtstrcpy(fmt, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ascii(void)
|
||||
{
|
||||
fmtinstall('T', asciiTfmt);
|
||||
}
|
||||
|
||||
static int
|
||||
runeTfmt(Fmt *fmt)
|
||||
{
|
||||
Rune buf[256], *r;
|
||||
int i;
|
||||
uchar *p;
|
||||
|
||||
p = va_arg(fmt->args, uchar*);
|
||||
for(i=0; i*2+2<=fmt->prec; i++, p+=2)
|
||||
buf[i] = (p[0]<<8)|p[1];
|
||||
buf[i] = L'\0';
|
||||
for(r=buf+i; r>buf && r[-1]==L' '; r--)
|
||||
;
|
||||
r[0] = L'\0';
|
||||
fmt->flags &= ~FmtPrec;
|
||||
return fmtprint(fmt, "%S", buf);
|
||||
}
|
||||
|
||||
static void
|
||||
getsect(uchar *buf, int n)
|
||||
{
|
||||
if(Bseek(b, n*2048, 0) != n*2048 || Bread(b, buf, 2048) != 2048)
|
||||
{
|
||||
abort();
|
||||
sysfatal("reading block at %,d: %r", n*2048);
|
||||
}
|
||||
}
|
||||
|
||||
static Header *h;
|
||||
static int fd;
|
||||
static char *file9660;
|
||||
static int off9660;
|
||||
static ulong startoff;
|
||||
static ulong endoff;
|
||||
static ulong fsoff;
|
||||
static uchar root[2048];
|
||||
static Voldesc *v;
|
||||
static ulong iso9660start(Cdir*);
|
||||
static void iso9660copydir(Fs*, File*, Cdir*);
|
||||
static void iso9660copyfile(Fs*, File*, Cdir*);
|
||||
|
||||
void
|
||||
iso9660init(int xfd, Header *xh, char *xfile9660, int xoff9660)
|
||||
{
|
||||
uchar sect[2048], sect2[2048];
|
||||
|
||||
fmtinstall('L', BLfmt);
|
||||
fmtinstall('B', BLfmt);
|
||||
fmtinstall('N', Nfmt);
|
||||
fmtinstall('D', Dfmt);
|
||||
|
||||
fd = xfd;
|
||||
h = xh;
|
||||
file9660 = xfile9660;
|
||||
off9660 = xoff9660;
|
||||
|
||||
if((b = Bopen(file9660, OREAD)) == nil)
|
||||
vtFatal("Bopen %s: %r", file9660);
|
||||
|
||||
getsect(root, 16);
|
||||
ascii();
|
||||
|
||||
v = (Voldesc*)root;
|
||||
if(memcmp(v->magic, "\x01CD001\x01\x00", 8) != 0)
|
||||
vtFatal("%s not a cd image", file9660);
|
||||
|
||||
startoff = iso9660start((Cdir*)v->rootdir)*Blocksize;
|
||||
endoff = little(v->volsize, 4); /* already in bytes */
|
||||
|
||||
fsoff = off9660 + h->data*h->blockSize;
|
||||
if(fsoff > startoff)
|
||||
vtFatal("fossil data starts after cd data");
|
||||
if(off9660 + (vlong)h->end*h->blockSize < endoff)
|
||||
vtFatal("fossil data ends before cd data");
|
||||
if(fsoff%h->blockSize)
|
||||
vtFatal("cd offset not a multiple of fossil block size");
|
||||
|
||||
/* Read "same" block via CD image and via Fossil image */
|
||||
getsect(sect, startoff/Blocksize);
|
||||
if(seek(fd, startoff-off9660, 0) < 0)
|
||||
vtFatal("cannot seek to first data sector on cd via fossil");
|
||||
fprint(2, "look for %lud at %lud\n", startoff, startoff-off9660);
|
||||
if(readn(fd, sect2, Blocksize) != Blocksize)
|
||||
vtFatal("cannot read first data sector on cd via fossil");
|
||||
if(memcmp(sect, sect2, Blocksize) != 0)
|
||||
vtFatal("iso9660 offset is a lie %08ux %08ux", *(long*)sect, *(long*)sect2);
|
||||
}
|
||||
|
||||
void
|
||||
iso9660labels(Disk *disk, uchar *buf, void (*write)(int, u32int))
|
||||
{
|
||||
ulong sb, eb, bn, lb, llb;
|
||||
Label l;
|
||||
int lpb;
|
||||
uchar sect[Blocksize];
|
||||
|
||||
if(!diskReadRaw(disk, PartData, (startoff-fsoff)/h->blockSize, buf))
|
||||
vtFatal("disk read failed: %r");
|
||||
getsect(sect, startoff/Blocksize);
|
||||
if(memcmp(buf, sect, Blocksize) != 0)
|
||||
vtFatal("fsoff is wrong");
|
||||
|
||||
sb = (startoff-fsoff)/h->blockSize;
|
||||
eb = (endoff-fsoff+h->blockSize-1)/h->blockSize;
|
||||
|
||||
lpb = h->blockSize/LabelSize;
|
||||
|
||||
/* for each reserved block, mark label */
|
||||
llb = ~0;
|
||||
l.type = BtData;
|
||||
l.state = BsAlloc;
|
||||
l.tag = Tag;
|
||||
l.epoch = 1;
|
||||
l.epochClose = ~(u32int)0;
|
||||
for(bn=sb; bn<eb; bn++){
|
||||
lb = bn/lpb;
|
||||
if(lb != llb){
|
||||
if(llb != ~0)
|
||||
(*write)(PartLabel, llb);
|
||||
memset(buf, 0, h->blockSize);
|
||||
}
|
||||
llb = lb;
|
||||
labelPack(&l, buf, bn%lpb);
|
||||
}
|
||||
if(llb != ~0)
|
||||
(*write)(PartLabel, llb);
|
||||
}
|
||||
|
||||
void
|
||||
iso9660copy(Fs *fs)
|
||||
{
|
||||
File *root;
|
||||
|
||||
root = fileOpen(fs, "/active");
|
||||
iso9660copydir(fs, root, (Cdir*)v->rootdir);
|
||||
fileDecRef(root);
|
||||
vtRUnlock(fs->elk);
|
||||
if(!fsSnapshot(fs, nil, nil, 0))
|
||||
vtFatal("snapshot failed: %R");
|
||||
vtRLock(fs->elk);
|
||||
}
|
||||
|
||||
/*
|
||||
* The first block used is the first data block of the leftmost file in the tree.
|
||||
* (Just an artifact of how mk9660 works.)
|
||||
*/
|
||||
static ulong
|
||||
iso9660start(Cdir *c)
|
||||
{
|
||||
uchar sect[Blocksize];
|
||||
|
||||
while(c->flags&2){
|
||||
getsect(sect, little(c->dloc, 4));
|
||||
c = (Cdir*)sect;
|
||||
c = (Cdir*)((uchar*)c+c->len); /* skip dot */
|
||||
c = (Cdir*)((uchar*)c+c->len); /* skip dotdot */
|
||||
/* oops: might happen if leftmost directory is empty or leftmost file is zero length! */
|
||||
if(little(c->dloc, 4) == 0)
|
||||
vtFatal("error parsing cd image or unfortunate cd image");
|
||||
}
|
||||
return little(c->dloc, 4);
|
||||
}
|
||||
|
||||
static void
|
||||
iso9660copydir(Fs *fs, File *dir, Cdir *cd)
|
||||
{
|
||||
ulong off, end, len;
|
||||
uchar sect[Blocksize], *esect, *p;
|
||||
Cdir *c;
|
||||
|
||||
len = little(cd->dlen, 4);
|
||||
off = little(cd->dloc, 4)*Blocksize;
|
||||
end = off+len;
|
||||
esect = sect+Blocksize;
|
||||
|
||||
for(; off<end; off+=Blocksize){
|
||||
getsect(sect, off/Blocksize);
|
||||
p = sect;
|
||||
while(p < esect){
|
||||
c = (Cdir*)p;
|
||||
if(c->len <= 0)
|
||||
break;
|
||||
if(c->namelen!=1 || c->name[0]>1)
|
||||
iso9660copyfile(fs, dir, c);
|
||||
p += c->len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char*
|
||||
getname(uchar **pp)
|
||||
{
|
||||
uchar *p;
|
||||
int l;
|
||||
|
||||
p = *pp;
|
||||
l = *p;
|
||||
*pp = p+1+l;
|
||||
if(l == 0)
|
||||
return "";
|
||||
memmove(p, p+1, l);
|
||||
p[l] = 0;
|
||||
return (char*)p;
|
||||
}
|
||||
|
||||
static char*
|
||||
getcname(Cdir *c)
|
||||
{
|
||||
uchar *up;
|
||||
char *p, *q;
|
||||
|
||||
up = &c->namelen;
|
||||
p = getname(&up);
|
||||
for(q=p; *q; q++)
|
||||
*q = tolower(*q);
|
||||
return p;
|
||||
}
|
||||
|
||||
static char
|
||||
dmsize[12] =
|
||||
{
|
||||
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
|
||||
};
|
||||
|
||||
static ulong
|
||||
getcdate(uchar *p) /* yMdhmsz */
|
||||
{
|
||||
Tm tm;
|
||||
int y, M, d, h, m, s, tz;
|
||||
|
||||
y=p[0]; M=p[1]; d=p[2];
|
||||
h=p[3]; m=p[4]; s=p[5]; tz=p[6];
|
||||
USED(tz);
|
||||
if (y < 70)
|
||||
return 0;
|
||||
if (M < 1 || M > 12)
|
||||
return 0;
|
||||
if (d < 1 || d > dmsize[M-1])
|
||||
return 0;
|
||||
if (h > 23)
|
||||
return 0;
|
||||
if (m > 59)
|
||||
return 0;
|
||||
if (s > 59)
|
||||
return 0;
|
||||
|
||||
memset(&tm, 0, sizeof tm);
|
||||
tm.sec = s;
|
||||
tm.min = m;
|
||||
tm.hour = h;
|
||||
tm.mday = d;
|
||||
tm.mon = M-1;
|
||||
tm.year = 1900+y;
|
||||
tm.zone[0] = 0;
|
||||
return tm2sec(&tm);
|
||||
}
|
||||
|
||||
static int ind;
|
||||
|
||||
static void
|
||||
iso9660copyfile(Fs *fs, File *dir, Cdir *c)
|
||||
{
|
||||
Dir d;
|
||||
DirEntry de;
|
||||
int sysl;
|
||||
uchar score[VtScoreSize];
|
||||
ulong off, foff, len, mode;
|
||||
uchar *p;
|
||||
File *f;
|
||||
|
||||
ind++;
|
||||
memset(&d, 0, sizeof d);
|
||||
p = c->name + c->namelen;
|
||||
if(((uintptr)p) & 1)
|
||||
p++;
|
||||
sysl = (uchar*)c + c->len - p;
|
||||
if(sysl <= 0)
|
||||
vtFatal("missing plan9 directory entry on %d/%d/%.*s", c->namelen, c->name[0], c->namelen, c->name);
|
||||
d.name = getname(&p);
|
||||
d.uid = getname(&p);
|
||||
d.gid = getname(&p);
|
||||
if((uintptr)p & 1)
|
||||
p++;
|
||||
d.mode = little(p, 4);
|
||||
if(d.name[0] == 0)
|
||||
d.name = getcname(c);
|
||||
d.mtime = getcdate(c->date);
|
||||
d.atime = d.mtime;
|
||||
|
||||
if(d.mode&DMDIR) print("%*scopy %s %s %s %luo\n", ind*2, "", d.name, d.uid, d.gid, d.mode);
|
||||
|
||||
mode = d.mode&0777;
|
||||
if(d.mode&DMDIR)
|
||||
mode |= ModeDir;
|
||||
if((f = fileCreate(dir, d.name, mode, d.uid)) == nil)
|
||||
vtFatal("could not create file '%s': %r", d.name);
|
||||
if(d.mode&DMDIR)
|
||||
iso9660copydir(fs, f, c);
|
||||
else{
|
||||
len = little(c->dlen, 4);
|
||||
off = little(c->dloc, 4)*Blocksize;
|
||||
for(foff=0; foff<len; foff+=h->blockSize){
|
||||
localToGlobal((off+foff-fsoff)/h->blockSize, score);
|
||||
if(!fileMapBlock(f, foff/h->blockSize, score, Tag))
|
||||
vtFatal("fileMapBlock: %R");
|
||||
}
|
||||
if(!fileSetSize(f, len))
|
||||
vtFatal("fileSetSize: %R");
|
||||
}
|
||||
if(!fileGetDir(f, &de))
|
||||
vtFatal("fileGetDir: %R");
|
||||
de.uid = d.uid;
|
||||
de.gid = d.gid;
|
||||
de.mtime = d.mtime;
|
||||
de.atime = d.atime;
|
||||
de.mode = d.mode&0777;
|
||||
if(!fileSetDir(f, &de, "sys"))
|
||||
vtFatal("fileSetDir: %R");
|
||||
fileDecRef(f);
|
||||
ind--;
|
||||
}
|
||||
3
src/cmd/fossil/flfmt9660.h
Normal file
3
src/cmd/fossil/flfmt9660.h
Normal file
@ -0,0 +1,3 @@
|
||||
void iso9660init(int fd, Header *h, char*, int);
|
||||
void iso9660labels(Disk*, uchar*, void(*write)(int, u32int));
|
||||
void iso9660copy(Fs*);
|
||||
106
src/cmd/fossil/fns.h
Normal file
106
src/cmd/fossil/fns.h
Normal file
@ -0,0 +1,106 @@
|
||||
Block* sourceBlock(Source*, ulong, int);
|
||||
Block* _sourceBlock(Source*, ulong, int, int, ulong);
|
||||
void sourceClose(Source*);
|
||||
Source* sourceCreate(Source*, int, int, u32int);
|
||||
ulong sourceGetDirSize(Source*);
|
||||
int sourceGetEntry(Source*, Entry*);
|
||||
uvlong sourceGetSize(Source*);
|
||||
int sourceLock2(Source*, Source*, int);
|
||||
int sourceLock(Source*, int);
|
||||
char *sourceName(Source *s);
|
||||
Source* sourceOpen(Source*, ulong, int, int);
|
||||
int sourceRemove(Source*);
|
||||
Source* sourceRoot(Fs*, u32int, int);
|
||||
int sourceSetDirSize(Source*, ulong);
|
||||
int sourceSetEntry(Source*, Entry*);
|
||||
int sourceSetSize(Source*, uvlong);
|
||||
int sourceTruncate(Source*);
|
||||
void sourceUnlock(Source*);
|
||||
|
||||
Block* cacheAllocBlock(Cache*, int, u32int, u32int, u32int);
|
||||
Cache* cacheAlloc(Disk*, VtSession*, ulong, int);
|
||||
void cacheCountUsed(Cache*, u32int, u32int*, u32int*, u32int*);
|
||||
int cacheDirty(Cache*);
|
||||
void cacheFlush(Cache*, int);
|
||||
void cacheFree(Cache*);
|
||||
Block* cacheGlobal(Cache*, uchar[VtScoreSize], int, u32int, int);
|
||||
Block* cacheLocal(Cache*, int, u32int, int);
|
||||
Block* cacheLocalData(Cache*, u32int, int, u32int, int, u32int);
|
||||
u32int cacheLocalSize(Cache*, int);
|
||||
int readLabel(Cache*, Label*, u32int addr);
|
||||
|
||||
Block* blockCopy(Block*, u32int, u32int, u32int);
|
||||
void blockDependency(Block*, Block*, int, uchar*, Entry*);
|
||||
int blockDirty(Block*);
|
||||
void blockDupLock(Block*);
|
||||
void blockPut(Block*);
|
||||
void blockRemoveLink(Block*, u32int, int, u32int, int);
|
||||
uchar* blockRollback(Block*, uchar*);
|
||||
void blockSetIOState(Block*, int);
|
||||
Block* _blockSetLabel(Block*, Label*);
|
||||
int blockSetLabel(Block*, Label*, int);
|
||||
int blockWrite(Block*, int);
|
||||
|
||||
Disk* diskAlloc(int);
|
||||
int diskBlockSize(Disk*);
|
||||
int diskFlush(Disk*);
|
||||
void diskFree(Disk*);
|
||||
void diskRead(Disk*, Block*);
|
||||
int diskReadRaw(Disk*, int, u32int, uchar*);
|
||||
u32int diskSize(Disk*, int);
|
||||
void diskWriteAndWait(Disk*, Block*);
|
||||
void diskWrite(Disk*, Block*);
|
||||
int diskWriteRaw(Disk*, int, u32int, uchar*);
|
||||
|
||||
char* bioStr(int);
|
||||
char* bsStr(int);
|
||||
char* btStr(int);
|
||||
u32int globalToLocal(uchar[VtScoreSize]);
|
||||
void localToGlobal(u32int, uchar[VtScoreSize]);
|
||||
|
||||
void headerPack(Header*, uchar*);
|
||||
int headerUnpack(Header*, uchar*);
|
||||
|
||||
int labelFmt(Fmt*);
|
||||
void labelPack(Label*, uchar*, int);
|
||||
int labelUnpack(Label*, uchar*, int);
|
||||
|
||||
int scoreFmt(Fmt*);
|
||||
|
||||
void superPack(Super*, uchar*);
|
||||
int superUnpack(Super*, uchar*);
|
||||
|
||||
void entryPack(Entry*, uchar*, int);
|
||||
int entryType(Entry*);
|
||||
int entryUnpack(Entry*, uchar*, int);
|
||||
|
||||
Periodic* periodicAlloc(void (*)(void*), void*, int);
|
||||
void periodicKill(Periodic*);
|
||||
|
||||
int fileGetSources(File*, Entry*, Entry*);
|
||||
File* fileRoot(Source*);
|
||||
int fileSnapshot(File*, File*, u32int, int);
|
||||
int fsNextQid(Fs*, u64int*);
|
||||
int mkVac(VtSession*, uint, Entry*, Entry*, DirEntry*, uchar[VtScoreSize]);
|
||||
Block* superGet(Cache*, Super*);
|
||||
|
||||
void archFree(Arch*);
|
||||
Arch* archInit(Cache*, Disk*, Fs*, VtSession*);
|
||||
void archKick(Arch*);
|
||||
|
||||
void bwatchDependency(Block*);
|
||||
void bwatchInit(void);
|
||||
void bwatchLock(Block*);
|
||||
void bwatchReset(uchar[VtScoreSize]);
|
||||
void bwatchSetBlockSize(uint);
|
||||
void bwatchUnlock(Block*);
|
||||
|
||||
void initWalk(WalkPtr*, Block*, uint);
|
||||
int nextWalk(WalkPtr*, uchar[VtScoreSize], uchar*, u32int*, Entry**);
|
||||
|
||||
void snapGetTimes(Snap*, u32int*, u32int*, u32int*);
|
||||
void snapSetTimes(Snap*, u32int, u32int, u32int);
|
||||
|
||||
void fsCheck(Fsck*);
|
||||
|
||||
#pragma varargck type "L" Label*
|
||||
143
src/cmd/fossil/fossil.c
Normal file
143
src/cmd/fossil/fossil.c
Normal file
@ -0,0 +1,143 @@
|
||||
#include "stdinc.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#include "9.h"
|
||||
|
||||
int Dflag;
|
||||
int mempcnt; /* for 9fsys.c */
|
||||
char* none = "none";
|
||||
char* foptname = "/none/such";
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s [-Dt] [-c cmd] [-f partition] [-m %%]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
static void
|
||||
readCmdPart(char *file, char ***pcmd, int *pncmd)
|
||||
{
|
||||
char buf[1024+1], *f[1024];
|
||||
char tbuf[1024];
|
||||
int nf;
|
||||
int i, fd, n;
|
||||
char **cmd, *p;
|
||||
int ncmd;
|
||||
|
||||
cmd = *pcmd;
|
||||
ncmd = *pncmd;
|
||||
|
||||
if((fd = open(file, OREAD)) < 0)
|
||||
sysfatal("open %s: %r", file);
|
||||
if(seek(fd, 127*1024, 0) != 127*1024)
|
||||
sysfatal("seek %s 127kB: %r", file);
|
||||
n = readn(fd, buf, sizeof buf-1);
|
||||
if(n == 0)
|
||||
sysfatal("short read of %s at 127kB", file);
|
||||
if(n < 0)
|
||||
sysfatal("read %s: %r", file);
|
||||
buf[n] = 0;
|
||||
if(memcmp(buf, "fossil config\n", 6+1+6+1) != 0)
|
||||
sysfatal("bad config magic in %s", file);
|
||||
nf = getfields(buf+6+1+6+1, f, nelem(f), 1, "\n");
|
||||
for(i=0; i<nf; i++){
|
||||
if(f[i][0] == '#')
|
||||
continue;
|
||||
cmd = vtMemRealloc(cmd, (ncmd+1)*sizeof(char*));
|
||||
/* expand argument '*' to mean current file */
|
||||
if((p = strchr(f[i], '*')) && (p==f[i]||isspace(p[-1])) && (p[1]==0||isspace(p[1]))){
|
||||
memmove(tbuf, f[i], p-f[i]);
|
||||
strecpy(tbuf+(p-f[i]), tbuf+sizeof tbuf, file);
|
||||
strecpy(tbuf+strlen(tbuf), tbuf+sizeof tbuf, p+1);
|
||||
f[i] = tbuf;
|
||||
}
|
||||
cmd[ncmd++] = vtStrDup(f[i]);
|
||||
}
|
||||
close(fd);
|
||||
*pcmd = cmd;
|
||||
*pncmd = ncmd;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
char **cmd, *p;
|
||||
int i, ncmd, tflag;
|
||||
|
||||
fmtinstall('D', dirfmt);
|
||||
fmtinstall('F', fcallfmt);
|
||||
fmtinstall('M', dirmodefmt);
|
||||
quotefmtinstall();
|
||||
|
||||
/*
|
||||
* Insulate from the invoker's environment.
|
||||
*/
|
||||
if(rfork(RFREND|RFNOTEG|RFNAMEG) < 0)
|
||||
sysfatal("rfork: %r");
|
||||
|
||||
close(0);
|
||||
open("/dev/null", OREAD);
|
||||
close(1);
|
||||
open("/dev/null", OWRITE);
|
||||
|
||||
cmd = nil;
|
||||
ncmd = tflag = 0;
|
||||
|
||||
vtAttach();
|
||||
|
||||
ARGBEGIN{
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
case 'c':
|
||||
p = EARGF(usage());
|
||||
currfsysname = p;
|
||||
cmd = vtMemRealloc(cmd, (ncmd+1)*sizeof(char*));
|
||||
cmd[ncmd++] = p;
|
||||
break;
|
||||
case 'D':
|
||||
Dflag ^= 1;
|
||||
break;
|
||||
case 'f':
|
||||
p = EARGF(usage());
|
||||
currfsysname = foptname = p;
|
||||
readCmdPart(p, &cmd, &ncmd);
|
||||
break;
|
||||
case 'm':
|
||||
mempcnt = atoi(EARGF(usage()));
|
||||
if(mempcnt <= 0 || mempcnt >= 100)
|
||||
usage();
|
||||
break;
|
||||
case 't':
|
||||
tflag = 1;
|
||||
break;
|
||||
}ARGEND
|
||||
if(argc != 0)
|
||||
usage();
|
||||
|
||||
consInit();
|
||||
cliInit();
|
||||
msgInit();
|
||||
conInit();
|
||||
cmdInit();
|
||||
fsysInit();
|
||||
exclInit();
|
||||
fidInit();
|
||||
|
||||
srvInit();
|
||||
lstnInit();
|
||||
usersInit();
|
||||
|
||||
for(i = 0; i < ncmd; i++)
|
||||
if(cliExec(cmd[i]) == 0)
|
||||
fprint(2, "%s: %R\n", cmd[i]);
|
||||
vtMemFree(cmd);
|
||||
|
||||
if(tflag && consTTY() == 0)
|
||||
consPrint("%s\n", vtGetError());
|
||||
|
||||
vtDetach();
|
||||
exits(0);
|
||||
}
|
||||
1099
src/cmd/fossil/fs.c
Normal file
1099
src/cmd/fossil/fs.c
Normal file
File diff suppressed because it is too large
Load Diff
67
src/cmd/fossil/fs.h
Normal file
67
src/cmd/fossil/fs.h
Normal file
@ -0,0 +1,67 @@
|
||||
typedef struct Fs Fs;
|
||||
typedef struct File File;
|
||||
typedef struct DirEntryEnum DirEntryEnum;
|
||||
|
||||
#pragma incomplete Fs
|
||||
#pragma incomplete File
|
||||
#pragma incomplete DirEntryEnum
|
||||
|
||||
/* modes */
|
||||
|
||||
enum {
|
||||
OReadOnly,
|
||||
OReadWrite,
|
||||
OOverWrite,
|
||||
};
|
||||
|
||||
extern char *currfsysname;
|
||||
extern char *foptname;
|
||||
|
||||
void fsClose(Fs*);
|
||||
int fsEpochLow(Fs*, u32int);
|
||||
File *fsGetRoot(Fs*);
|
||||
int fsHalt(Fs*);
|
||||
Fs *fsOpen(char*, VtSession*, long, int);
|
||||
int fsRedial(Fs*, char*);
|
||||
void fsSnapshotCleanup(Fs*, u32int);
|
||||
int fsSnapshot(Fs*, char*, char*, int);
|
||||
void fsSnapshotRemove(Fs*);
|
||||
int fsSync(Fs*);
|
||||
int fsUnhalt(Fs*);
|
||||
int fsVac(Fs*, char*, uchar[VtScoreSize]);
|
||||
|
||||
void deeClose(DirEntryEnum*);
|
||||
DirEntryEnum *deeOpen(File*);
|
||||
int deeRead(DirEntryEnum*, DirEntry*);
|
||||
int fileClri(File*, char*, char*);
|
||||
int fileClriPath(Fs*, char*, char*);
|
||||
File *fileCreate(File*, char*, ulong, char*);
|
||||
int fileDecRef(File*);
|
||||
int fileGetDir(File*, DirEntry*);
|
||||
uvlong fileGetId(File*);
|
||||
ulong fileGetMcount(File*);
|
||||
ulong fileGetMode(File*);
|
||||
File *fileGetParent(File*);
|
||||
int fileGetSize(File*, uvlong*);
|
||||
File *fileIncRef(File*);
|
||||
int fileIsDir(File*);
|
||||
int fileIsTemporary(File*);
|
||||
int fileIsAppend(File*);
|
||||
int fileIsExclusive(File*);
|
||||
int fileIsRoFs(File*);
|
||||
int fileIsRoot(File*);
|
||||
int fileMapBlock(File*, ulong, uchar[VtScoreSize], ulong);
|
||||
int fileMetaFlush(File*, int);
|
||||
char *fileName(File *f);
|
||||
File *fileOpen(Fs*, char*);
|
||||
int fileRead(File*, void *, int, vlong);
|
||||
int fileRemove(File*, char*);
|
||||
int fileSetDir(File*, DirEntry*, char*);
|
||||
int fileSetQidSpace(File*, u64int, u64int);
|
||||
int fileSetSize(File*, uvlong);
|
||||
int fileSync(File*);
|
||||
int fileTruncate(File*, char*);
|
||||
File *fileWalk(File*, char*);
|
||||
File *_fileWalk(File*, char*, int);
|
||||
int fileWalkSources(File*);
|
||||
int fileWrite(File*, void *, int, vlong, char*);
|
||||
40
src/cmd/fossil/last.c
Normal file
40
src/cmd/fossil/last.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: fossil/last disk\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int fd, bs, addr;
|
||||
char buf[20];
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc != 1)
|
||||
usage();
|
||||
|
||||
if((fd = open(argv[0], OREAD)) < 0)
|
||||
sysfatal("open %s: %r", argv[0]);
|
||||
|
||||
werrstr("end of file");
|
||||
if(seek(fd, 131072, 0) < 0 || readn(fd, buf, 20) != 20)
|
||||
sysfatal("error reading %s: %r", argv[0]);
|
||||
fmtinstall('H', encodefmt);
|
||||
if(memcmp(buf, "\x37\x76\xAE\x89", 4) != 0)
|
||||
sysfatal("bad magic %.4H != 3776AE89", buf);
|
||||
bs = buf[7]|(buf[6]<<8);
|
||||
addr = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11];
|
||||
if(seek(fd, (vlong)bs*addr+34, 0) < 0 || readn(fd, buf, 20) != 20)
|
||||
sysfatal("error reading %s: %r", argv[0]);
|
||||
print("vac:%.20lH\n", buf);
|
||||
exits(0);
|
||||
}
|
||||
136
src/cmd/fossil/mkfile
Normal file
136
src/cmd/fossil/mkfile
Normal file
@ -0,0 +1,136 @@
|
||||
</$objtype/mkfile
|
||||
BIN=/$objtype/bin/fossil
|
||||
|
||||
TARG=fossil flchk flfmt conf last
|
||||
|
||||
LIBFILES=\
|
||||
9p\
|
||||
9auth\
|
||||
9dir\
|
||||
9excl\
|
||||
9fid\
|
||||
9fsys\
|
||||
9lstn\
|
||||
9proc\
|
||||
9srv\
|
||||
9user\
|
||||
Ccmd\
|
||||
Ccli\
|
||||
Ccons\
|
||||
Clog\
|
||||
archive\
|
||||
nobwatch\
|
||||
cache\
|
||||
check\
|
||||
disk\
|
||||
error\
|
||||
file\
|
||||
fs\
|
||||
pack\
|
||||
periodic\
|
||||
source\
|
||||
vac\
|
||||
walk\
|
||||
|
||||
LIBCFILES=${LIBFILES:%=%.c}
|
||||
LIBOFILES=${LIBFILES:%=%.$O}
|
||||
LIB=libfs.a$O
|
||||
|
||||
HFILES=\
|
||||
/sys/include/oventi.h\
|
||||
stdinc.h\
|
||||
vac.h\
|
||||
dat.h\
|
||||
fns.h\
|
||||
fs.h\
|
||||
error.h\
|
||||
9.h\
|
||||
flfmt9660.h\
|
||||
|
||||
CFILES=${TARG:%=%.c} $LIBCFILES flfmt9660.c
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$CFILES\
|
||||
$HFILES\
|
||||
|
||||
default:V: all
|
||||
|
||||
test:V: all
|
||||
rm -f /srv/test.fossil /srv/test.fscons
|
||||
slay 8.flfmt | rc
|
||||
slay 8.fossil | rc
|
||||
unmount /n/fossil || status=''
|
||||
{syscall seek 1 6400000000 0; echo} >>/tmp/fossil
|
||||
8.flfmt -y /tmp/fossil
|
||||
8.conf -w /tmp/fossil flproto
|
||||
8.fossil -f /tmp/fossil
|
||||
cat /srv/test.fscons &
|
||||
echo fsys main >>/srv/test.fscons
|
||||
mount /srv/test.fossil /n/fossil
|
||||
cd /n/fossil/tmp
|
||||
dd -bs 1048576 -count 256 -if /dev/zero -of a
|
||||
rm a
|
||||
echo sync >>/srv/test.fscons
|
||||
echo sync >>/srv/test.fscons
|
||||
echo sync >>/srv/test.fscons
|
||||
sleep 1
|
||||
echo sync >>/srv/test.fscons
|
||||
sleep 1
|
||||
echo sync >>/srv/test.fscons
|
||||
sleep 1
|
||||
echo sync >>/srv/test.fscons
|
||||
echo check >>/srv/test.fscons
|
||||
echo check >>/srv/test.fscons
|
||||
echo check >>/srv/test.fscons
|
||||
|
||||
# cp /env/timezone /n/fossil/tmp
|
||||
# cp /lib/words /n/fossil/tmp
|
||||
# dircp /n/sources/plan9/sys/src/cmd/aux /n/fossil/tmp
|
||||
# >/n/fossil/tmp/lis
|
||||
# chmod +t /n/fossil/tmp/lis
|
||||
# echo SHOULD NOT SEE THIS >>/n/fossil/tmp/lis
|
||||
# echo snap >>/srv/test.fscons
|
||||
# sleep 2
|
||||
# mount /srv/test.fossil /n/dump main/archive
|
||||
# cat /n/dump/*/*/tmp/lis
|
||||
# @{cd /n/fossil/tmp && time tar xTf /sys/src/cmd/fossil/test.tar}
|
||||
# unmount /n/fossil
|
||||
# rm /srv/fossil
|
||||
|
||||
</sys/src/cmd/mkmany
|
||||
|
||||
$LIB(%.$O):N: %.$O
|
||||
$LIB: ${LIBOFILES:%=$LIB(%)}
|
||||
names = `{echo $newprereq |sed 's/ /\n/g' |sed -n 's/'$LIB'\(([^)]+)\)/\1/gp'}
|
||||
ar vu $LIB $names
|
||||
# rm $names
|
||||
|
||||
$O.flfmt: flfmt9660.$O
|
||||
|
||||
flfmt%.$O: flfmt9660.h
|
||||
|
||||
%.page:V: %.ps
|
||||
page -w $stem.ps
|
||||
|
||||
%.ps:D: %.ms
|
||||
tbl $stem.ms | pic | eqn | troff -ms | lp -dstdout >$target
|
||||
|
||||
bundle:V:
|
||||
rfork n
|
||||
ramfs -m /n/kremvax >[2]/dev/null
|
||||
bind -a /n/kremvax .
|
||||
cp /sys/doc/fossil.ms /sys/doc/fossil.ps /n/kremvax
|
||||
cp /sys/man/4/fossil /n/kremvax/fossil.4.man
|
||||
cp /sys/man/8/fossilcons /n/kremvax/fossilcons.8.man
|
||||
x=`{ls |grep -v 'TODO|test.tar|fossil.tar.gz'}
|
||||
tar c $x | gzip > fossil.tar.gz
|
||||
|
||||
$O.conf:D: conf.rc
|
||||
{
|
||||
echo '#!/bin/rc'
|
||||
echo '# THIS FILE IS AUTOMATICALLY GENERATED'
|
||||
echo '# FROM /sys/src/cmd/fossil/conf.rc. DO NOT EDIT.'
|
||||
echo
|
||||
sed 1d conf.rc
|
||||
} >$target && chmod +x $target
|
||||
39
src/cmd/fossil/nobwatch.c
Normal file
39
src/cmd/fossil/nobwatch.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
void
|
||||
bwatchReset(uchar score[VtScoreSize])
|
||||
{
|
||||
USED(score);
|
||||
}
|
||||
|
||||
void
|
||||
bwatchInit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
bwatchSetBlockSize(uint)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
bwatchDependency(Block *b)
|
||||
{
|
||||
USED(b);
|
||||
}
|
||||
|
||||
void
|
||||
bwatchLock(Block *b)
|
||||
{
|
||||
USED(b);
|
||||
}
|
||||
|
||||
void
|
||||
bwatchUnlock(Block *b)
|
||||
{
|
||||
USED(b);
|
||||
}
|
||||
|
||||
225
src/cmd/fossil/pack.c
Normal file
225
src/cmd/fossil/pack.c
Normal file
@ -0,0 +1,225 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
/*
|
||||
* integer conversion routines
|
||||
*/
|
||||
#define U8GET(p) ((p)[0])
|
||||
#define U16GET(p) (((p)[0]<<8)|(p)[1])
|
||||
#define U32GET(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3])
|
||||
#define U48GET(p) (((uvlong)U16GET(p)<<32)|(uvlong)U32GET((p)+2))
|
||||
#define U64GET(p) (((uvlong)U32GET(p)<<32)|(uvlong)U32GET((p)+4))
|
||||
|
||||
#define U8PUT(p,v) (p)[0]=(v)
|
||||
#define U16PUT(p,v) (p)[0]=(v)>>8;(p)[1]=(v)
|
||||
#define U32PUT(p,v) (p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v)
|
||||
#define U48PUT(p,v,t32) t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32)
|
||||
#define U64PUT(p,v,t32) t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32)
|
||||
|
||||
void
|
||||
headerPack(Header *h, uchar *p)
|
||||
{
|
||||
memset(p, 0, HeaderSize);
|
||||
U32PUT(p, HeaderMagic);
|
||||
U16PUT(p+4, HeaderVersion);
|
||||
U16PUT(p+6, h->blockSize);
|
||||
U32PUT(p+8, h->super);
|
||||
U32PUT(p+12, h->label);
|
||||
U32PUT(p+16, h->data);
|
||||
U32PUT(p+20, h->end);
|
||||
}
|
||||
|
||||
int
|
||||
headerUnpack(Header *h, uchar *p)
|
||||
{
|
||||
if(U32GET(p) != HeaderMagic){
|
||||
vtSetError("vac header bad magic");
|
||||
return 0;
|
||||
}
|
||||
h->version = U16GET(p+4);
|
||||
if(h->version != HeaderVersion){
|
||||
vtSetError("vac header bad version");
|
||||
return 0;
|
||||
}
|
||||
h->blockSize = U16GET(p+6);
|
||||
h->super = U32GET(p+8);
|
||||
h->label = U32GET(p+12);
|
||||
h->data = U32GET(p+16);
|
||||
h->end = U32GET(p+20);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
labelPack(Label *l, uchar *p, int i)
|
||||
{
|
||||
p += i*LabelSize;
|
||||
U8PUT(p, l->state);
|
||||
U8PUT(p+1, l->type);
|
||||
U32PUT(p+2, l->epoch);
|
||||
U32PUT(p+6, l->epochClose);
|
||||
U32PUT(p+10, l->tag);
|
||||
}
|
||||
|
||||
int
|
||||
labelUnpack(Label *l, uchar *p, int i)
|
||||
{
|
||||
p += i*LabelSize;
|
||||
l->state = p[0];
|
||||
l->type = p[1];
|
||||
l->epoch = U32GET(p+2);
|
||||
l->epochClose = U32GET(p+6);
|
||||
l->tag = U32GET(p+10);
|
||||
|
||||
if(l->type > BtMax){
|
||||
Bad:
|
||||
vtSetError(EBadLabel);
|
||||
fprint(2, "%s: labelUnpack: bad label: 0x%.2ux 0x%.2ux 0x%.8ux "
|
||||
"0x%.8ux 0x%.8ux\n", argv0, l->state, l->type, l->epoch,
|
||||
l->epochClose, l->tag);
|
||||
return 0;
|
||||
}
|
||||
if(l->state != BsBad && l->state != BsFree){
|
||||
if(!(l->state&BsAlloc) || l->state & ~BsMask)
|
||||
goto Bad;
|
||||
if(l->state&BsClosed){
|
||||
if(l->epochClose == ~(u32int)0)
|
||||
goto Bad;
|
||||
}else{
|
||||
if(l->epochClose != ~(u32int)0)
|
||||
goto Bad;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32int
|
||||
globalToLocal(uchar score[VtScoreSize])
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<VtScoreSize-4; i++)
|
||||
if(score[i] != 0)
|
||||
return NilBlock;
|
||||
|
||||
return U32GET(score+VtScoreSize-4);
|
||||
}
|
||||
|
||||
void
|
||||
localToGlobal(u32int addr, uchar score[VtScoreSize])
|
||||
{
|
||||
memset(score, 0, VtScoreSize-4);
|
||||
U32PUT(score+VtScoreSize-4, addr);
|
||||
}
|
||||
|
||||
void
|
||||
entryPack(Entry *e, uchar *p, int index)
|
||||
{
|
||||
ulong t32;
|
||||
int flags;
|
||||
|
||||
p += index * VtEntrySize;
|
||||
|
||||
U32PUT(p, e->gen);
|
||||
U16PUT(p+4, e->psize);
|
||||
U16PUT(p+6, e->dsize);
|
||||
flags = e->flags | ((e->depth << VtEntryDepthShift) & VtEntryDepthMask);
|
||||
U8PUT(p+8, flags);
|
||||
memset(p+9, 0, 5);
|
||||
U48PUT(p+14, e->size, t32);
|
||||
|
||||
if(flags & VtEntryLocal){
|
||||
if(globalToLocal(e->score) == NilBlock)
|
||||
abort();
|
||||
memset(p+20, 0, 7);
|
||||
U8PUT(p+27, e->archive);
|
||||
U32PUT(p+28, e->snap);
|
||||
U32PUT(p+32, e->tag);
|
||||
memmove(p+36, e->score+16, 4);
|
||||
}else
|
||||
memmove(p+20, e->score, VtScoreSize);
|
||||
}
|
||||
|
||||
int
|
||||
entryUnpack(Entry *e, uchar *p, int index)
|
||||
{
|
||||
p += index * VtEntrySize;
|
||||
|
||||
e->gen = U32GET(p);
|
||||
e->psize = U16GET(p+4);
|
||||
e->dsize = U16GET(p+6);
|
||||
e->flags = U8GET(p+8);
|
||||
e->depth = (e->flags & VtEntryDepthMask) >> VtEntryDepthShift;
|
||||
e->flags &= ~VtEntryDepthMask;
|
||||
e->size = U48GET(p+14);
|
||||
|
||||
if(e->flags & VtEntryLocal){
|
||||
e->archive = p[27];
|
||||
e->snap = U32GET(p+28);
|
||||
e->tag = U32GET(p+32);
|
||||
memset(e->score, 0, 16);
|
||||
memmove(e->score+16, p+36, 4);
|
||||
}else{
|
||||
e->archive = 0;
|
||||
e->snap = 0;
|
||||
e->tag = 0;
|
||||
memmove(e->score, p+20, VtScoreSize);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
entryType(Entry *e)
|
||||
{
|
||||
return (((e->flags & VtEntryDir) != 0) << 3) | e->depth;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
superPack(Super *s, uchar *p)
|
||||
{
|
||||
u32int t32;
|
||||
|
||||
memset(p, 0, SuperSize);
|
||||
U32PUT(p, SuperMagic);
|
||||
assert(s->version == SuperVersion);
|
||||
U16PUT(p+4, s->version);
|
||||
U32PUT(p+6, s->epochLow);
|
||||
U32PUT(p+10, s->epochHigh);
|
||||
U64PUT(p+14, s->qid, t32);
|
||||
U32PUT(p+22, s->active);
|
||||
U32PUT(p+26, s->next);
|
||||
U32PUT(p+30, s->current);
|
||||
memmove(p+34, s->last, VtScoreSize);
|
||||
memmove(p+54, s->name, sizeof(s->name));
|
||||
}
|
||||
|
||||
int
|
||||
superUnpack(Super *s, uchar *p)
|
||||
{
|
||||
memset(s, 0, sizeof(*s));
|
||||
if(U32GET(p) != SuperMagic)
|
||||
goto Err;
|
||||
s->version = U16GET(p+4);
|
||||
if(s->version != SuperVersion)
|
||||
goto Err;
|
||||
s->epochLow = U32GET(p+6);
|
||||
s->epochHigh = U32GET(p+10);
|
||||
s->qid = U64GET(p+14);
|
||||
if(s->epochLow == 0 || s->epochLow > s->epochHigh || s->qid == 0)
|
||||
goto Err;
|
||||
s->active = U32GET(p+22);
|
||||
s->next = U32GET(p+26);
|
||||
s->current = U32GET(p+30);
|
||||
memmove(s->last, p+34, VtScoreSize);
|
||||
memmove(s->name, p+54, sizeof(s->name));
|
||||
s->name[sizeof(s->name)-1] = 0;
|
||||
return 1;
|
||||
Err:
|
||||
memset(s, 0, sizeof(*s));
|
||||
vtSetError(EBadSuper);
|
||||
return 0;
|
||||
}
|
||||
|
||||
84
src/cmd/fossil/periodic.c
Normal file
84
src/cmd/fossil/periodic.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
struct Periodic {
|
||||
VtLock *lk;
|
||||
int die; /* flag: quit if set */
|
||||
void (*f)(void*); /* call this each period */
|
||||
void *a; /* argument to f */
|
||||
int msec; /* period */
|
||||
};
|
||||
|
||||
static void periodicThread(void *a);
|
||||
|
||||
Periodic *
|
||||
periodicAlloc(void (*f)(void*), void *a, int msec)
|
||||
{
|
||||
Periodic *p;
|
||||
|
||||
p = vtMemAllocZ(sizeof(Periodic));
|
||||
p->lk = vtLockAlloc();
|
||||
p->f = f;
|
||||
p->a = a;
|
||||
p->msec = msec;
|
||||
if(p->msec < 10)
|
||||
p->msec = 10;
|
||||
|
||||
vtThread(periodicThread, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
periodicKill(Periodic *p)
|
||||
{
|
||||
if(p == nil)
|
||||
return;
|
||||
vtLock(p->lk);
|
||||
p->die = 1;
|
||||
vtUnlock(p->lk);
|
||||
}
|
||||
|
||||
static void
|
||||
periodicFree(Periodic *p)
|
||||
{
|
||||
vtLockFree(p->lk);
|
||||
vtMemFree(p);
|
||||
}
|
||||
|
||||
static void
|
||||
periodicThread(void *a)
|
||||
{
|
||||
Periodic *p = a;
|
||||
vlong t, ct, ts; /* times in ms. */
|
||||
|
||||
vtThreadSetName("periodic");
|
||||
|
||||
ct = nsec() / 1000000;
|
||||
t = ct + p->msec; /* call p->f at or after this time */
|
||||
|
||||
for(;;){
|
||||
ts = t - ct; /* ms. to next cycle's start */
|
||||
if(ts > 1000)
|
||||
ts = 1000; /* bound sleep duration */
|
||||
if(ts > 0)
|
||||
sleep(ts); /* wait for cycle's start */
|
||||
|
||||
vtLock(p->lk);
|
||||
if(p->die){
|
||||
vtUnlock(p->lk);
|
||||
break;
|
||||
}
|
||||
ct = nsec() / 1000000;
|
||||
if(t <= ct){ /* due to call p->f? */
|
||||
p->f(p->a);
|
||||
ct = nsec() / 1000000;
|
||||
while(t <= ct) /* advance t to future cycle start */
|
||||
t += p->msec;
|
||||
}
|
||||
vtUnlock(p->lk);
|
||||
}
|
||||
periodicFree(p);
|
||||
}
|
||||
|
||||
1068
src/cmd/fossil/source.c
Normal file
1068
src/cmd/fossil/source.c
Normal file
File diff suppressed because it is too large
Load Diff
270
src/cmd/fossil/srcload.c
Normal file
270
src/cmd/fossil/srcload.c
Normal file
@ -0,0 +1,270 @@
|
||||
#include "stdinc.h"
|
||||
#include <bio.h>
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "error.h"
|
||||
|
||||
int num = 100;
|
||||
int length = 20*1024;
|
||||
int block= 1024;
|
||||
int bush = 4;
|
||||
int iter = 100;
|
||||
Biobuf *bout;
|
||||
int maxdepth;
|
||||
|
||||
Source *mkroot(Cache*);
|
||||
void new(Source*, int trace, int);
|
||||
int delete(Source*);
|
||||
int count(Source *s, int);
|
||||
void stats(Source *s);
|
||||
void dump(Source *s, int ident, ulong entry);
|
||||
static void bench(Source *r);
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
Fs *fs;
|
||||
int csize = 1000;
|
||||
ulong t;
|
||||
Source *r;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'i':
|
||||
iter = atoi(ARGF());
|
||||
break;
|
||||
case 'n':
|
||||
num = atoi(ARGF());
|
||||
break;
|
||||
case 'l':
|
||||
length = atoi(ARGF());
|
||||
break;
|
||||
case 'b':
|
||||
block = atoi(ARGF());
|
||||
break;
|
||||
case 'u':
|
||||
bush = atoi(ARGF());
|
||||
break;
|
||||
case 'c':
|
||||
csize = atoi(ARGF());
|
||||
break;
|
||||
}ARGEND;
|
||||
|
||||
vtAttach();
|
||||
|
||||
bout = vtMemAllocZ(sizeof(Biobuf));
|
||||
Binit(bout, 1, OWRITE);
|
||||
|
||||
fmtinstall('V', vtScoreFmt);
|
||||
fmtinstall('R', vtErrFmt);
|
||||
|
||||
fs = fsOpen(argv[0], nil, csize, OReadWrite);
|
||||
if(fs == nil)
|
||||
sysfatal("could not open fs: %r");
|
||||
|
||||
t = time(0);
|
||||
|
||||
srand(0);
|
||||
|
||||
r = fs->source;
|
||||
dump(r, 0, 0);
|
||||
|
||||
fprint(2, "count = %d\n", count(r, 1));
|
||||
for(i=0; i<num; i++)
|
||||
new(r, 0, 0);
|
||||
|
||||
for(i=0; i<iter; i++){
|
||||
if(i % 10000 == 0)
|
||||
stats(r);
|
||||
new(r, 0, 0);
|
||||
delete(r);
|
||||
}
|
||||
|
||||
// dump(r, 0, 0);
|
||||
|
||||
fprint(2, "count = %d\n", count(r, 1));
|
||||
// cacheCheck(c);
|
||||
|
||||
fprint(2, "deleting\n");
|
||||
for(i=0; i<num; i++)
|
||||
delete(r);
|
||||
// dump(r, 0, 0);
|
||||
|
||||
fprint(2, "count = %d\n", count(r, 1));
|
||||
fprint(2, "total time = %ld\n", time(0)-t);
|
||||
|
||||
fsClose(fs);
|
||||
vtDetach();
|
||||
exits(0);
|
||||
}
|
||||
|
||||
static void
|
||||
bench(Source *r)
|
||||
{
|
||||
vlong t;
|
||||
Entry e;
|
||||
int i;
|
||||
|
||||
t = nsec();
|
||||
|
||||
for(i=0; i<1000000; i++)
|
||||
sourceGetEntry(r, &e);
|
||||
|
||||
fprint(2, "%f\n", 1e-9*(nsec() - t));
|
||||
}
|
||||
|
||||
void
|
||||
new(Source *s, int trace, int depth)
|
||||
{
|
||||
int i, n;
|
||||
Source *ss;
|
||||
Entry e;
|
||||
|
||||
if(depth > maxdepth)
|
||||
maxdepth = depth;
|
||||
|
||||
Bflush(bout);
|
||||
|
||||
n = sourceGetDirSize(s);
|
||||
for(i=0; i<n; i++){
|
||||
ss = sourceOpen(s, nrand(n), OReadWrite);
|
||||
if(ss == nil || !sourceGetEntry(ss, &e))
|
||||
continue;
|
||||
if((e.flags & VtEntryDir) && frand() < 1./bush){
|
||||
if(trace){
|
||||
int j;
|
||||
for(j=0; j<trace; j++)
|
||||
Bprint(bout, " ");
|
||||
Bprint(bout, "decend %d\n", i);
|
||||
}
|
||||
new(ss, trace?trace+1:0, depth+1);
|
||||
sourceClose(ss);
|
||||
return;
|
||||
}
|
||||
sourceClose(ss);
|
||||
}
|
||||
ss = sourceCreate(s, s->dsize, 1+frand()>.5, 0);
|
||||
if(ss == nil){
|
||||
Bprint(bout, "could not create directory: %R\n");
|
||||
return;
|
||||
}
|
||||
if(trace){
|
||||
int j;
|
||||
for(j=1; j<trace; j++)
|
||||
Bprint(bout, " ");
|
||||
Bprint(bout, "create %d\n", ss->offset);
|
||||
}
|
||||
sourceClose(ss);
|
||||
}
|
||||
|
||||
int
|
||||
delete(Source *s)
|
||||
{
|
||||
int i, n;
|
||||
Source *ss;
|
||||
|
||||
n = sourceGetDirSize(s);
|
||||
/* check if empty */
|
||||
for(i=0; i<n; i++){
|
||||
ss = sourceOpen(s, i, OReadWrite);
|
||||
if(ss != nil){
|
||||
sourceClose(ss);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == n)
|
||||
return 0;
|
||||
|
||||
for(;;){
|
||||
ss = sourceOpen(s, nrand(n), OReadWrite);
|
||||
if(ss == nil)
|
||||
continue;
|
||||
if(s->dir && delete(ss)){
|
||||
sourceClose(ss);
|
||||
return 1;
|
||||
}
|
||||
if(1)
|
||||
break;
|
||||
sourceClose(ss);
|
||||
}
|
||||
|
||||
|
||||
sourceRemove(ss);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
dump(Source *s, int ident, ulong entry)
|
||||
{
|
||||
ulong i, n;
|
||||
Source *ss;
|
||||
Entry e;
|
||||
|
||||
for(i=0; i<ident; i++)
|
||||
Bprint(bout, " ");
|
||||
|
||||
if(!sourceGetEntry(s, &e)){
|
||||
fprint(2, "sourceGetEntry failed: %r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Bprint(bout, "%4lud: gen %4ud depth %d tag=%x score=%V",
|
||||
entry, e.gen, e.depth, e.tag, e.score);
|
||||
if(!s->dir){
|
||||
Bprint(bout, " data size: %llud\n", e.size);
|
||||
return;
|
||||
}
|
||||
n = sourceGetDirSize(s);
|
||||
Bprint(bout, " dir size: %lud\n", n);
|
||||
for(i=0; i<n; i++){
|
||||
ss = sourceOpen(s, i, 1);
|
||||
if(ss == nil)
|
||||
continue;
|
||||
dump(ss, ident+1, i);
|
||||
sourceClose(ss);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
count(Source *s, int rec)
|
||||
{
|
||||
ulong i, n;
|
||||
int c;
|
||||
Source *ss;
|
||||
|
||||
n = sourceGetDirSize(s);
|
||||
c = 0;
|
||||
for(i=0; i<n; i++){
|
||||
ss = sourceOpen(s, i, OReadOnly);
|
||||
if(ss == nil)
|
||||
continue;
|
||||
if(rec)
|
||||
c += count(ss, rec);
|
||||
c++;
|
||||
sourceClose(ss);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
stats(Source *s)
|
||||
{
|
||||
int n, i, c, cc, max;
|
||||
Source *ss;
|
||||
|
||||
cc = 0;
|
||||
max = 0;
|
||||
n = sourceGetDirSize(s);
|
||||
for(i=0; i<n; i++){
|
||||
ss = sourceOpen(s, i, 1);
|
||||
if(ss == nil)
|
||||
continue;
|
||||
cc++;
|
||||
c = count(ss, 1);
|
||||
if(c > max)
|
||||
max = c;
|
||||
sourceClose(ss);
|
||||
}
|
||||
fprint(2, "count = %d top = %d depth=%d maxcount %d\n", cc, n, maxdepth, max);
|
||||
}
|
||||
11
src/cmd/fossil/stdinc.h
Normal file
11
src/cmd/fossil/stdinc.h
Normal file
@ -0,0 +1,11 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
typedef uvlong u64int;
|
||||
typedef uchar u8int;
|
||||
typedef ushort u16int;
|
||||
|
||||
#include "oventi.h"
|
||||
#include "vac.h"
|
||||
#include "fs.h"
|
||||
|
||||
19
src/cmd/fossil/trunc.c
Normal file
19
src/cmd/fossil/trunc.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Dir d;
|
||||
|
||||
if(argc != 3){
|
||||
fprint(2, "usage: trunc file size\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
nulldir(&d);
|
||||
d.length = strtoull(argv[2], 0, 0);
|
||||
if(dirwstat(argv[1], &d) < 0)
|
||||
sysfatal("dirwstat: %r");
|
||||
exits(0);
|
||||
}
|
||||
746
src/cmd/fossil/vac.c
Normal file
746
src/cmd/fossil/vac.c
Normal file
@ -0,0 +1,746 @@
|
||||
#include "stdinc.h"
|
||||
|
||||
typedef struct MetaChunk MetaChunk;
|
||||
|
||||
struct MetaChunk {
|
||||
ushort offset;
|
||||
ushort size;
|
||||
ushort index;
|
||||
};
|
||||
|
||||
static int stringUnpack(char **s, uchar **p, int *n);
|
||||
static int meCmp(MetaEntry*, char *s);
|
||||
static int meCmpOld(MetaEntry*, char *s);
|
||||
|
||||
|
||||
|
||||
static char EBadMeta[] = "corrupted meta data";
|
||||
static char ENoFile[] = "file does not exist";
|
||||
|
||||
/*
|
||||
* integer conversion routines
|
||||
*/
|
||||
#define U8GET(p) ((p)[0])
|
||||
#define U16GET(p) (((p)[0]<<8)|(p)[1])
|
||||
#define U32GET(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3])
|
||||
#define U48GET(p) (((uvlong)U16GET(p)<<32)|(uvlong)U32GET((p)+2))
|
||||
#define U64GET(p) (((uvlong)U32GET(p)<<32)|(uvlong)U32GET((p)+4))
|
||||
|
||||
#define U8PUT(p,v) (p)[0]=(v)
|
||||
#define U16PUT(p,v) (p)[0]=(v)>>8;(p)[1]=(v)
|
||||
#define U32PUT(p,v) (p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v)
|
||||
#define U48PUT(p,v,t32) t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32)
|
||||
#define U64PUT(p,v,t32) t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32)
|
||||
|
||||
static int
|
||||
stringUnpack(char **s, uchar **p, int *n)
|
||||
{
|
||||
int nn;
|
||||
|
||||
if(*n < 2)
|
||||
return 0;
|
||||
|
||||
nn = U16GET(*p);
|
||||
*p += 2;
|
||||
*n -= 2;
|
||||
if(nn > *n)
|
||||
return 0;
|
||||
*s = vtMemAlloc(nn+1);
|
||||
memmove(*s, *p, nn);
|
||||
(*s)[nn] = 0;
|
||||
*p += nn;
|
||||
*n -= nn;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
stringPack(char *s, uchar *p)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = strlen(s);
|
||||
U16PUT(p, n);
|
||||
memmove(p+2, s, n);
|
||||
return n+2;
|
||||
}
|
||||
|
||||
int
|
||||
mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
|
||||
{
|
||||
int i;
|
||||
int b, t, x;
|
||||
if(0)fprint(2, "mbSearch %s\n", elem);
|
||||
|
||||
/* binary search within block */
|
||||
b = 0;
|
||||
t = mb->nindex;
|
||||
while(b < t){
|
||||
i = (b+t)>>1;
|
||||
meUnpack(me, mb, i);
|
||||
|
||||
if(mb->botch)
|
||||
x = meCmpOld(me, elem);
|
||||
else
|
||||
x = meCmp(me, elem);
|
||||
|
||||
if(x == 0){
|
||||
*ri = i;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(x < 0)
|
||||
b = i+1;
|
||||
else /* x > 0 */
|
||||
t = i;
|
||||
}
|
||||
|
||||
assert(b == t);
|
||||
|
||||
*ri = b; /* b is the index to insert this entry */
|
||||
memset(me, 0, sizeof(*me));
|
||||
|
||||
vtSetError(ENoFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
mbInit(MetaBlock *mb, uchar *p, int n, int ne)
|
||||
{
|
||||
memset(p, 0, n);
|
||||
mb->maxsize = n;
|
||||
mb->maxindex = ne;
|
||||
mb->nindex = 0;
|
||||
mb->free = 0;
|
||||
mb->size = MetaHeaderSize + ne*MetaIndexSize;
|
||||
mb->buf = p;
|
||||
mb->botch = 0;
|
||||
}
|
||||
|
||||
int
|
||||
mbUnpack(MetaBlock *mb, uchar *p, int n)
|
||||
{
|
||||
u32int magic;
|
||||
int i;
|
||||
int eo, en, omin;
|
||||
uchar *q;
|
||||
|
||||
mb->maxsize = n;
|
||||
mb->buf = p;
|
||||
|
||||
if(n == 0){
|
||||
memset(mb, 0, sizeof(MetaBlock));
|
||||
return 1;
|
||||
}
|
||||
|
||||
magic = U32GET(p);
|
||||
if(magic != MetaMagic && magic != MetaMagic-1)
|
||||
goto Err;
|
||||
mb->size = U16GET(p+4);
|
||||
mb->free = U16GET(p+6);
|
||||
mb->maxindex = U16GET(p+8);
|
||||
mb->nindex = U16GET(p+10);
|
||||
mb->botch = magic != MetaMagic;
|
||||
if(mb->size > n)
|
||||
goto Err;
|
||||
|
||||
omin = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
||||
if(n < omin)
|
||||
goto Err;
|
||||
|
||||
|
||||
p += MetaHeaderSize;
|
||||
|
||||
/* check the index table - ensures that meUnpack and meCmp never fail */
|
||||
for(i=0; i<mb->nindex; i++){
|
||||
eo = U16GET(p);
|
||||
en = U16GET(p+2);
|
||||
if(eo < omin || eo+en > mb->size || en < 8)
|
||||
goto Err;
|
||||
q = mb->buf + eo;
|
||||
if(U32GET(q) != DirMagic)
|
||||
goto Err;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
return 1;
|
||||
Err:
|
||||
vtSetError(EBadMeta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mbPack(MetaBlock *mb)
|
||||
{
|
||||
uchar *p;
|
||||
|
||||
p = mb->buf;
|
||||
|
||||
assert(!mb->botch);
|
||||
|
||||
U32PUT(p, MetaMagic);
|
||||
U16PUT(p+4, mb->size);
|
||||
U16PUT(p+6, mb->free);
|
||||
U16PUT(p+8, mb->maxindex);
|
||||
U16PUT(p+10, mb->nindex);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mbDelete(MetaBlock *mb, int i)
|
||||
{
|
||||
uchar *p;
|
||||
int n;
|
||||
MetaEntry me;
|
||||
|
||||
assert(i < mb->nindex);
|
||||
meUnpack(&me, mb, i);
|
||||
memset(me.p, 0, me.size);
|
||||
|
||||
if(me.p - mb->buf + me.size == mb->size)
|
||||
mb->size -= me.size;
|
||||
else
|
||||
mb->free += me.size;
|
||||
|
||||
p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
|
||||
n = (mb->nindex-i-1)*MetaIndexSize;
|
||||
memmove(p, p+MetaIndexSize, n);
|
||||
memset(p+n, 0, MetaIndexSize);
|
||||
mb->nindex--;
|
||||
}
|
||||
|
||||
void
|
||||
mbInsert(MetaBlock *mb, int i, MetaEntry *me)
|
||||
{
|
||||
uchar *p;
|
||||
int o, n;
|
||||
|
||||
assert(mb->nindex < mb->maxindex);
|
||||
|
||||
o = me->p - mb->buf;
|
||||
n = me->size;
|
||||
if(o+n > mb->size){
|
||||
mb->free -= mb->size - o;
|
||||
mb->size = o + n;
|
||||
}else
|
||||
mb->free -= n;
|
||||
|
||||
p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
|
||||
n = (mb->nindex-i)*MetaIndexSize;
|
||||
memmove(p+MetaIndexSize, p, n);
|
||||
U16PUT(p, me->p - mb->buf);
|
||||
U16PUT(p+2, me->size);
|
||||
mb->nindex++;
|
||||
}
|
||||
|
||||
int
|
||||
mbResize(MetaBlock *mb, MetaEntry *me, int n)
|
||||
{
|
||||
uchar *p, *ep;
|
||||
|
||||
/* easy case */
|
||||
if(n <= me->size){
|
||||
me->size = n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* try and expand entry */
|
||||
|
||||
p = me->p + me->size;
|
||||
ep = mb->buf + mb->maxsize;
|
||||
while(p < ep && *p == 0)
|
||||
p++;
|
||||
if(n <= p - me->p){
|
||||
me->size = n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = mbAlloc(mb, n);
|
||||
if(p != nil){
|
||||
me->p = p;
|
||||
me->size = n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
meUnpack(MetaEntry *me, MetaBlock *mb, int i)
|
||||
{
|
||||
uchar *p;
|
||||
int eo, en;
|
||||
|
||||
assert(i >= 0 && i < mb->nindex);
|
||||
|
||||
p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
|
||||
eo = U16GET(p);
|
||||
en = U16GET(p+2);
|
||||
|
||||
me->p = mb->buf + eo;
|
||||
me->size = en;
|
||||
|
||||
/* checked by mbUnpack */
|
||||
assert(me->size >= 8);
|
||||
}
|
||||
|
||||
/* assumes a small amount of checking has been done in mbEntry */
|
||||
static int
|
||||
meCmp(MetaEntry *me, char *s)
|
||||
{
|
||||
int n;
|
||||
uchar *p;
|
||||
|
||||
p = me->p;
|
||||
|
||||
/* skip magic & version */
|
||||
p += 6;
|
||||
n = U16GET(p);
|
||||
p += 2;
|
||||
|
||||
if(n > me->size - 8)
|
||||
n = me->size - 8;
|
||||
|
||||
while(n > 0){
|
||||
if(*s == 0)
|
||||
return 1;
|
||||
if(*p < (uchar)*s)
|
||||
return -1;
|
||||
if(*p > (uchar)*s)
|
||||
return 1;
|
||||
p++;
|
||||
s++;
|
||||
n--;
|
||||
}
|
||||
return -(*s != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the old and broken meCmp.
|
||||
* This cmp routine reverse the sense of the comparison
|
||||
* when one string is a prefix of the other.
|
||||
* In other words, it put "ab" after "abc" rather
|
||||
* than before. This behaviour is ok; binary search
|
||||
* and sort still work. However, it is goes against
|
||||
* the usual convention.
|
||||
*/
|
||||
static int
|
||||
meCmpOld(MetaEntry *me, char *s)
|
||||
{
|
||||
int n;
|
||||
uchar *p;
|
||||
|
||||
p = me->p;
|
||||
|
||||
/* skip magic & version */
|
||||
p += 6;
|
||||
n = U16GET(p);
|
||||
p += 2;
|
||||
|
||||
if(n > me->size - 8)
|
||||
n = me->size - 8;
|
||||
|
||||
while(n > 0){
|
||||
if(*s == 0)
|
||||
return -1;
|
||||
if(*p < (uchar)*s)
|
||||
return -1;
|
||||
if(*p > (uchar)*s)
|
||||
return 1;
|
||||
p++;
|
||||
s++;
|
||||
n--;
|
||||
}
|
||||
return *s != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
offsetCmp(void *s0, void *s1)
|
||||
{
|
||||
MetaChunk *mc0, *mc1;
|
||||
|
||||
mc0 = s0;
|
||||
mc1 = s1;
|
||||
if(mc0->offset < mc1->offset)
|
||||
return -1;
|
||||
if(mc0->offset > mc1->offset)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MetaChunk *
|
||||
metaChunks(MetaBlock *mb)
|
||||
{
|
||||
MetaChunk *mc;
|
||||
int oo, o, n, i;
|
||||
uchar *p;
|
||||
|
||||
mc = vtMemAlloc(mb->nindex*sizeof(MetaChunk));
|
||||
p = mb->buf + MetaHeaderSize;
|
||||
for(i = 0; i<mb->nindex; i++){
|
||||
mc[i].offset = U16GET(p);
|
||||
mc[i].size = U16GET(p+2);
|
||||
mc[i].index = i;
|
||||
p += MetaIndexSize;
|
||||
}
|
||||
|
||||
qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
|
||||
|
||||
/* check block looks ok */
|
||||
oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
||||
o = oo;
|
||||
n = 0;
|
||||
for(i=0; i<mb->nindex; i++){
|
||||
o = mc[i].offset;
|
||||
n = mc[i].size;
|
||||
if(o < oo)
|
||||
goto Err;
|
||||
oo += n;
|
||||
}
|
||||
if(o+n > mb->size)
|
||||
goto Err;
|
||||
if(mb->size - oo != mb->free)
|
||||
goto Err;
|
||||
|
||||
return mc;
|
||||
Err:
|
||||
fprint(2, "metaChunks failed!\n");
|
||||
oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
||||
for(i=0; i<mb->nindex; i++){
|
||||
fprint(2, "\t%d: %d %d\n", i, mc[i].offset, mc[i].offset + mc[i].size);
|
||||
oo += mc[i].size;
|
||||
}
|
||||
fprint(2, "\tused=%d size=%d free=%d free2=%d\n", oo, mb->size, mb->free, mb->size - oo);
|
||||
vtSetError(EBadMeta);
|
||||
vtMemFree(mc);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
mbCompact(MetaBlock *mb, MetaChunk *mc)
|
||||
{
|
||||
int oo, o, n, i;
|
||||
|
||||
oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
||||
|
||||
for(i=0; i<mb->nindex; i++){
|
||||
o = mc[i].offset;
|
||||
n = mc[i].size;
|
||||
if(o != oo){
|
||||
memmove(mb->buf + oo, mb->buf + o, n);
|
||||
U16PUT(mb->buf + MetaHeaderSize + mc[i].index*MetaIndexSize, oo);
|
||||
}
|
||||
oo += n;
|
||||
}
|
||||
|
||||
mb->size = oo;
|
||||
mb->free = 0;
|
||||
}
|
||||
|
||||
uchar *
|
||||
mbAlloc(MetaBlock *mb, int n)
|
||||
{
|
||||
int i, o;
|
||||
MetaChunk *mc;
|
||||
|
||||
/* off the end */
|
||||
if(mb->maxsize - mb->size >= n)
|
||||
return mb->buf + mb->size;
|
||||
|
||||
/* check if possible */
|
||||
if(mb->maxsize - mb->size + mb->free < n)
|
||||
return nil;
|
||||
|
||||
mc = metaChunks(mb);
|
||||
if(mc == nil){
|
||||
fprint(2, "mbAlloc: metaChunks failed: %r\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* look for hole */
|
||||
o = MetaHeaderSize + mb->maxindex*MetaIndexSize;
|
||||
for(i=0; i<mb->nindex; i++){
|
||||
if(mc[i].offset - o >= n){
|
||||
vtMemFree(mc);
|
||||
return mb->buf + o;
|
||||
}
|
||||
o = mc[i].offset + mc[i].size;
|
||||
}
|
||||
|
||||
if(mb->maxsize - o >= n){
|
||||
vtMemFree(mc);
|
||||
return mb->buf + o;
|
||||
}
|
||||
|
||||
/* compact and return off the end */
|
||||
mbCompact(mb, mc);
|
||||
vtMemFree(mc);
|
||||
|
||||
if(mb->maxsize - mb->size < n){
|
||||
vtSetError(EBadMeta);
|
||||
return nil;
|
||||
}
|
||||
return mb->buf + mb->size;
|
||||
}
|
||||
|
||||
int
|
||||
deSize(DirEntry *dir)
|
||||
{
|
||||
int n;
|
||||
|
||||
/* constant part */
|
||||
|
||||
n = 4 + /* magic */
|
||||
2 + /* version */
|
||||
4 + /* entry */
|
||||
4 + /* guid */
|
||||
4 + /* mentry */
|
||||
4 + /* mgen */
|
||||
8 + /* qid */
|
||||
4 + /* mtime */
|
||||
4 + /* mcount */
|
||||
4 + /* ctime */
|
||||
4 + /* atime */
|
||||
4 + /* mode */
|
||||
0;
|
||||
|
||||
/* strings */
|
||||
n += 2 + strlen(dir->elem);
|
||||
n += 2 + strlen(dir->uid);
|
||||
n += 2 + strlen(dir->gid);
|
||||
n += 2 + strlen(dir->mid);
|
||||
|
||||
/* optional sections */
|
||||
if(dir->qidSpace){
|
||||
n += 3 + /* option header */
|
||||
8 + /* qidOffset */
|
||||
8; /* qid Max */
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
dePack(DirEntry *dir, MetaEntry *me)
|
||||
{
|
||||
uchar *p;
|
||||
ulong t32;
|
||||
|
||||
p = me->p;
|
||||
|
||||
U32PUT(p, DirMagic);
|
||||
U16PUT(p+4, 9); /* version */
|
||||
p += 6;
|
||||
|
||||
p += stringPack(dir->elem, p);
|
||||
|
||||
U32PUT(p, dir->entry);
|
||||
U32PUT(p+4, dir->gen);
|
||||
U32PUT(p+8, dir->mentry);
|
||||
U32PUT(p+12, dir->mgen);
|
||||
U64PUT(p+16, dir->qid, t32);
|
||||
p += 24;
|
||||
|
||||
p += stringPack(dir->uid, p);
|
||||
p += stringPack(dir->gid, p);
|
||||
p += stringPack(dir->mid, p);
|
||||
|
||||
U32PUT(p, dir->mtime);
|
||||
U32PUT(p+4, dir->mcount);
|
||||
U32PUT(p+8, dir->ctime);
|
||||
U32PUT(p+12, dir->atime);
|
||||
U32PUT(p+16, dir->mode);
|
||||
p += 5*4;
|
||||
|
||||
if(dir->qidSpace){
|
||||
U8PUT(p, DeQidSpace);
|
||||
U16PUT(p+1, 2*8);
|
||||
p += 3;
|
||||
U64PUT(p, dir->qidOffset, t32);
|
||||
U64PUT(p+8, dir->qidMax, t32);
|
||||
p += 16;
|
||||
}
|
||||
|
||||
assert(p == me->p + me->size);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
deUnpack(DirEntry *dir, MetaEntry *me)
|
||||
{
|
||||
int t, nn, n, version;
|
||||
uchar *p;
|
||||
|
||||
p = me->p;
|
||||
n = me->size;
|
||||
|
||||
memset(dir, 0, sizeof(DirEntry));
|
||||
|
||||
if(0)print("deUnpack\n");
|
||||
/* magic */
|
||||
if(n < 4 || U32GET(p) != DirMagic)
|
||||
goto Err;
|
||||
p += 4;
|
||||
n -= 4;
|
||||
|
||||
if(0)print("deUnpack: got magic\n");
|
||||
/* version */
|
||||
if(n < 2)
|
||||
goto Err;
|
||||
version = U16GET(p);
|
||||
if(version < 7 || version > 9)
|
||||
goto Err;
|
||||
p += 2;
|
||||
n -= 2;
|
||||
|
||||
if(0)print("deUnpack: got version\n");
|
||||
|
||||
/* elem */
|
||||
if(!stringUnpack(&dir->elem, &p, &n))
|
||||
goto Err;
|
||||
|
||||
if(0)print("deUnpack: got elem\n");
|
||||
|
||||
/* entry */
|
||||
if(n < 4)
|
||||
goto Err;
|
||||
dir->entry = U32GET(p);
|
||||
p += 4;
|
||||
n -= 4;
|
||||
|
||||
if(0)print("deUnpack: got entry\n");
|
||||
|
||||
if(version < 9){
|
||||
dir->gen = 0;
|
||||
dir->mentry = dir->entry+1;
|
||||
dir->mgen = 0;
|
||||
}else{
|
||||
if(n < 3*4)
|
||||
goto Err;
|
||||
dir->gen = U32GET(p);
|
||||
dir->mentry = U32GET(p+4);
|
||||
dir->mgen = U32GET(p+8);
|
||||
p += 3*4;
|
||||
n -= 3*4;
|
||||
}
|
||||
|
||||
if(0)print("deUnpack: got gen etc\n");
|
||||
|
||||
/* size is gotten from VtEntry */
|
||||
dir->size = 0;
|
||||
|
||||
/* qid */
|
||||
if(n < 8)
|
||||
goto Err;
|
||||
dir->qid = U64GET(p);
|
||||
p += 8;
|
||||
n -= 8;
|
||||
|
||||
if(0)print("deUnpack: got qid\n");
|
||||
/* skip replacement */
|
||||
if(version == 7){
|
||||
if(n < VtScoreSize)
|
||||
goto Err;
|
||||
p += VtScoreSize;
|
||||
n -= VtScoreSize;
|
||||
}
|
||||
|
||||
/* uid */
|
||||
if(!stringUnpack(&dir->uid, &p, &n))
|
||||
goto Err;
|
||||
|
||||
/* gid */
|
||||
if(!stringUnpack(&dir->gid, &p, &n))
|
||||
goto Err;
|
||||
|
||||
/* mid */
|
||||
if(!stringUnpack(&dir->mid, &p, &n))
|
||||
goto Err;
|
||||
|
||||
if(0)print("deUnpack: got ids\n");
|
||||
if(n < 5*4)
|
||||
goto Err;
|
||||
dir->mtime = U32GET(p);
|
||||
dir->mcount = U32GET(p+4);
|
||||
dir->ctime = U32GET(p+8);
|
||||
dir->atime = U32GET(p+12);
|
||||
dir->mode = U32GET(p+16);
|
||||
p += 5*4;
|
||||
n -= 5*4;
|
||||
|
||||
if(0)print("deUnpack: got times\n");
|
||||
/* optional meta data */
|
||||
while(n > 0){
|
||||
if(n < 3)
|
||||
goto Err;
|
||||
t = p[0];
|
||||
nn = U16GET(p+1);
|
||||
p += 3;
|
||||
n -= 3;
|
||||
if(n < nn)
|
||||
goto Err;
|
||||
switch(t){
|
||||
case DePlan9:
|
||||
/* not valid in version >= 9 */
|
||||
if(version >= 9)
|
||||
break;
|
||||
if(dir->plan9 || nn != 12)
|
||||
goto Err;
|
||||
dir->plan9 = 1;
|
||||
dir->p9path = U64GET(p);
|
||||
dir->p9version = U32GET(p+8);
|
||||
if(dir->mcount == 0)
|
||||
dir->mcount = dir->p9version;
|
||||
break;
|
||||
case DeGen:
|
||||
/* not valid in version >= 9 */
|
||||
if(version >= 9)
|
||||
break;
|
||||
break;
|
||||
case DeQidSpace:
|
||||
if(dir->qidSpace || nn != 16)
|
||||
goto Err;
|
||||
dir->qidSpace = 1;
|
||||
dir->qidOffset = U64GET(p);
|
||||
dir->qidMax = U64GET(p+8);
|
||||
break;
|
||||
}
|
||||
p += nn;
|
||||
n -= nn;
|
||||
}
|
||||
if(0)print("deUnpack: got options\n");
|
||||
|
||||
if(p != me->p + me->size)
|
||||
goto Err;
|
||||
|
||||
if(0)print("deUnpack: correct size\n");
|
||||
return 1;
|
||||
Err:
|
||||
if(0)print("deUnpack: XXXXXXXXXXXX EBadMeta\n");
|
||||
vtSetError(EBadMeta);
|
||||
deCleanup(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
deCleanup(DirEntry *dir)
|
||||
{
|
||||
vtMemFree(dir->elem);
|
||||
dir->elem = nil;
|
||||
vtMemFree(dir->uid);
|
||||
dir->uid = nil;
|
||||
vtMemFree(dir->gid);
|
||||
dir->gid = nil;
|
||||
vtMemFree(dir->mid);
|
||||
dir->mid = nil;
|
||||
}
|
||||
|
||||
void
|
||||
deCopy(DirEntry *dst, DirEntry *src)
|
||||
{
|
||||
*dst = *src;
|
||||
dst->elem = vtStrDup(src->elem);
|
||||
dst->uid = vtStrDup(src->uid);
|
||||
dst->gid = vtStrDup(src->gid);
|
||||
dst->mid = vtStrDup(src->mid);
|
||||
}
|
||||
107
src/cmd/fossil/vac.h
Normal file
107
src/cmd/fossil/vac.h
Normal file
@ -0,0 +1,107 @@
|
||||
typedef struct DirEntry DirEntry;
|
||||
typedef struct MetaBlock MetaBlock;
|
||||
typedef struct MetaEntry MetaEntry;
|
||||
|
||||
enum {
|
||||
MetaMagic = 0x5656fc7a,
|
||||
MetaHeaderSize = 12,
|
||||
MetaIndexSize = 4,
|
||||
IndexEntrySize = 8,
|
||||
DirMagic = 0x1c4d9072,
|
||||
};
|
||||
|
||||
/*
|
||||
* Mode bits
|
||||
*/
|
||||
enum {
|
||||
ModeOtherExec = (1<<0),
|
||||
ModeOtherWrite = (1<<1),
|
||||
ModeOtherRead = (1<<2),
|
||||
ModeGroupExec = (1<<3),
|
||||
ModeGroupWrite = (1<<4),
|
||||
ModeGroupRead = (1<<5),
|
||||
ModeOwnerExec = (1<<6),
|
||||
ModeOwnerWrite = (1<<7),
|
||||
ModeOwnerRead = (1<<8),
|
||||
ModeSticky = (1<<9),
|
||||
ModeSetUid = (1<<10),
|
||||
ModeSetGid = (1<<11),
|
||||
ModeAppend = (1<<12), /* append only file */
|
||||
ModeExclusive = (1<<13), /* lock file - plan 9 */
|
||||
ModeLink = (1<<14), /* sym link */
|
||||
ModeDir = (1<<15), /* duplicate of DirEntry */
|
||||
ModeHidden = (1<<16), /* MS-DOS */
|
||||
ModeSystem = (1<<17), /* MS-DOS */
|
||||
ModeArchive = (1<<18), /* MS-DOS */
|
||||
ModeTemporary = (1<<19), /* MS-DOS */
|
||||
ModeSnapshot = (1<<20), /* read only snapshot */
|
||||
};
|
||||
|
||||
/* optional directory entry fields */
|
||||
enum {
|
||||
DePlan9 = 1, /* not valid in version >= 9 */
|
||||
DeNT, /* not valid in version >= 9 */
|
||||
DeQidSpace,
|
||||
DeGen, /* not valid in version >= 9 */
|
||||
};
|
||||
|
||||
struct DirEntry {
|
||||
char *elem; /* path element */
|
||||
ulong entry; /* entry in directory for data */
|
||||
ulong gen; /* generation of data entry */
|
||||
ulong mentry; /* entry in directory for meta */
|
||||
ulong mgen; /* generation of meta entry */
|
||||
uvlong size; /* size of file */
|
||||
uvlong qid; /* unique file id */
|
||||
|
||||
char *uid; /* owner id */
|
||||
char *gid; /* group id */
|
||||
char *mid; /* last modified by */
|
||||
ulong mtime; /* last modified time */
|
||||
ulong mcount; /* number of modifications: can wrap! */
|
||||
ulong ctime; /* directory entry last changed */
|
||||
ulong atime; /* last time accessed */
|
||||
ulong mode; /* various mode bits */
|
||||
|
||||
/* plan 9 */
|
||||
int plan9;
|
||||
uvlong p9path;
|
||||
ulong p9version;
|
||||
|
||||
/* sub space of qid */
|
||||
int qidSpace;
|
||||
uvlong qidOffset; /* qid offset */
|
||||
uvlong qidMax; /* qid maximum */
|
||||
};
|
||||
|
||||
struct MetaEntry {
|
||||
uchar *p;
|
||||
ushort size;
|
||||
};
|
||||
|
||||
struct MetaBlock {
|
||||
int maxsize; /* size of block */
|
||||
int size; /* size used */
|
||||
int free; /* free space within used size */
|
||||
int maxindex; /* entries allocated for table */
|
||||
int nindex; /* amount of table used */
|
||||
int botch; /* compensate for my stupidity */
|
||||
uchar *buf;
|
||||
};
|
||||
|
||||
void deCleanup(DirEntry*);
|
||||
void deCopy(DirEntry*, DirEntry*);
|
||||
int deSize(DirEntry*);
|
||||
void dePack(DirEntry*, MetaEntry*);
|
||||
int deUnpack(DirEntry*, MetaEntry*);
|
||||
|
||||
void mbInit(MetaBlock*, uchar*, int, int);
|
||||
int mbUnpack(MetaBlock*, uchar*, int);
|
||||
void mbInsert(MetaBlock*, int, MetaEntry*);
|
||||
void mbDelete(MetaBlock*, int);
|
||||
void mbPack(MetaBlock*);
|
||||
uchar *mbAlloc(MetaBlock*, int);
|
||||
int mbResize(MetaBlock*, MetaEntry*, int);
|
||||
int mbSearch(MetaBlock*, char*, int*, MetaEntry*);
|
||||
|
||||
void meUnpack(MetaEntry*, MetaBlock*, int);
|
||||
1127
src/cmd/fossil/view.c
Normal file
1127
src/cmd/fossil/view.c
Normal file
File diff suppressed because it is too large
Load Diff
65
src/cmd/fossil/walk.c
Normal file
65
src/cmd/fossil/walk.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Generic traversal routines.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
static uint
|
||||
etype(Entry *e)
|
||||
{
|
||||
uint t;
|
||||
|
||||
if(e->flags&VtEntryDir)
|
||||
t = BtDir;
|
||||
else
|
||||
t = BtData;
|
||||
return t+e->depth;
|
||||
}
|
||||
|
||||
void
|
||||
initWalk(WalkPtr *w, Block *b, uint size)
|
||||
{
|
||||
memset(w, 0, sizeof *w);
|
||||
switch(b->l.type){
|
||||
case BtData:
|
||||
return;
|
||||
|
||||
case BtDir:
|
||||
w->data = b->data;
|
||||
w->m = size / VtEntrySize;
|
||||
w->isEntry = 1;
|
||||
return;
|
||||
|
||||
default:
|
||||
w->data = b->data;
|
||||
w->m = size / VtScoreSize;
|
||||
w->type = b->l.type;
|
||||
w->tag = b->l.tag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nextWalk(WalkPtr *w, uchar score[VtScoreSize], uchar *type, u32int *tag, Entry **e)
|
||||
{
|
||||
if(w->n >= w->m)
|
||||
return 0;
|
||||
|
||||
if(w->isEntry){
|
||||
*e = &w->e;
|
||||
entryUnpack(&w->e, w->data, w->n);
|
||||
memmove(score, w->e.score, VtScoreSize);
|
||||
*type = etype(&w->e);
|
||||
*tag = w->e.tag;
|
||||
}else{
|
||||
*e = nil;
|
||||
memmove(score, w->data+w->n*VtScoreSize, VtScoreSize);
|
||||
*type = w->type-1;
|
||||
*tag = w->tag;
|
||||
}
|
||||
w->n++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user