mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-12 11:10:07 +00:00
Dump-like file system backup for Unix, built on Venti.
This commit is contained in:
parent
0c98da8bf8
commit
004aa293f3
27 changed files with 4437 additions and 0 deletions
78
man/man1/hist.1
Normal file
78
man/man1/hist.1
Normal file
|
@ -0,0 +1,78 @@
|
|||
.TH HIST 1
|
||||
.SH NAME
|
||||
hist \- print file names from the dump
|
||||
.SH SYNOPSIS
|
||||
.B hist
|
||||
[
|
||||
.B -vdu
|
||||
] [
|
||||
.B -s
|
||||
.I yyyymmdd
|
||||
]
|
||||
.I files ...
|
||||
.SH DESCRIPTION
|
||||
.I Hist
|
||||
prints the names, dates, and sizes of all versions of the named
|
||||
.IR files ,
|
||||
looking backwards in time,
|
||||
stored in the dump file system.
|
||||
If the file exists in the main tree, the first line of output will be its current state.
|
||||
For example,
|
||||
.IP
|
||||
.EX
|
||||
hist ~rsc/.bash_history
|
||||
.EE
|
||||
.PP
|
||||
produces
|
||||
.IP
|
||||
.EX
|
||||
.nf
|
||||
May 19 16:11:37 EDT 2005 /home/am3/rsc/.bash_history 6175
|
||||
May 18 23:32:16 EDT 2005 /dump/am/2005/0519/home/am3/rsc/.bash_history 5156
|
||||
May 17 23:32:31 EDT 2005 /dump/am/2005/0518/home/am3/rsc/.bash_history 5075
|
||||
May 16 07:53:47 EDT 2005 /dump/am/2005/0517/home/am3/rsc/.bash_history 5065
|
||||
.fi
|
||||
.EE
|
||||
.PP
|
||||
The
|
||||
.B -v
|
||||
option enables verbose debugging printout.
|
||||
.PP
|
||||
The
|
||||
.B -d
|
||||
option causes
|
||||
.IR diff (1)
|
||||
.B -c
|
||||
to be run for each adjacent pair of dump files, while
|
||||
.B -b
|
||||
runs
|
||||
.IR diff
|
||||
.BR -cb .
|
||||
.PP
|
||||
The
|
||||
.B -u
|
||||
option causes times to be printed in GMT (UT) rather than local time.
|
||||
.PP
|
||||
Finally, the
|
||||
.B -s
|
||||
option
|
||||
sets the starting (most recent) date for the output.
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
Examine changes in block.c:
|
||||
.IP
|
||||
.EX
|
||||
hist -d block.c
|
||||
.EE
|
||||
.SH FILES
|
||||
.B /dump
|
||||
.SH SOURCE
|
||||
.B /home/am3/rsc/src/backup/cmd/history.c
|
||||
.SH SEE ALSO
|
||||
.IR yesterday (1)
|
||||
.SH BUGS
|
||||
Should be called
|
||||
.IR history ,
|
||||
but
|
||||
that name is taken by
|
||||
.IR sh (1).
|
263
man/man1/vbackup.1
Normal file
263
man/man1/vbackup.1
Normal file
|
@ -0,0 +1,263 @@
|
|||
.TH VBACKUP 8
|
||||
.SH NAME
|
||||
vbackup, vcat, vftp, vmount, vmount0, vnfs \-
|
||||
back up Unix file systems to Venti
|
||||
.SH SYNOPSIS
|
||||
.B vbackup
|
||||
[
|
||||
.B -DVnv
|
||||
]
|
||||
[
|
||||
.B -s
|
||||
.I secs
|
||||
]
|
||||
[
|
||||
.B -w
|
||||
.I n
|
||||
]
|
||||
.I disk
|
||||
[
|
||||
.I score
|
||||
]
|
||||
.PP
|
||||
.B vcat
|
||||
[
|
||||
.B -z
|
||||
]
|
||||
.I disk
|
||||
|
|
||||
.I score
|
||||
.B >
|
||||
.I disk
|
||||
.PP
|
||||
.B vftp
|
||||
.I disk
|
||||
|
|
||||
.I score
|
||||
.PP
|
||||
.B vmount
|
||||
[
|
||||
.B -v
|
||||
]
|
||||
.I addr
|
||||
.I mtpt
|
||||
.PP
|
||||
.B vmount0
|
||||
[
|
||||
.B -v
|
||||
]
|
||||
[
|
||||
.B -h
|
||||
.I handle
|
||||
]
|
||||
.I addr
|
||||
.I mtpt
|
||||
.PP
|
||||
.B vnfs
|
||||
[
|
||||
.B -LLMRVr
|
||||
]
|
||||
[
|
||||
.B -a
|
||||
.I addr
|
||||
]
|
||||
[
|
||||
.B -m
|
||||
.I mntaddr
|
||||
]
|
||||
[
|
||||
.B -b
|
||||
.I blocksize
|
||||
]
|
||||
[
|
||||
.B -c
|
||||
.I cachesize
|
||||
]
|
||||
.I config
|
||||
.SH DESCRIPTION
|
||||
These programs back up and restore standard
|
||||
Unix file system images stored in
|
||||
.IR venti (8).
|
||||
Images stored in
|
||||
.I venti
|
||||
are named by
|
||||
.IR scores ,
|
||||
which consist of a file system type followed
|
||||
by a colon and forty hexadecimal digits, as in:
|
||||
.IP
|
||||
.EX
|
||||
ffs:0123456789abcdef0123456789abcdef01234567
|
||||
.EE
|
||||
.PP
|
||||
(The hexadecimal data is the SHA1 hash of the Venti
|
||||
root block representing the file system image.)
|
||||
.PP
|
||||
These programs expect the environment variable
|
||||
.B $venti
|
||||
to be set to the network address of the Venti server to use
|
||||
(for example,
|
||||
.B yourhost
|
||||
or
|
||||
.BR tcp!yourhost!venti ).
|
||||
.PP
|
||||
.I Vbackup
|
||||
copies the file system stored on
|
||||
.I disk
|
||||
to the Venti server and prints the
|
||||
score for the newly-stored image.
|
||||
The argument
|
||||
.I disk
|
||||
should be a disk or disk partition device
|
||||
that would be appropriate to pass to
|
||||
.IR mount (8).
|
||||
.PP
|
||||
The optional argument
|
||||
.I score
|
||||
is the score of a previous backup of the disk image.
|
||||
If
|
||||
.I score
|
||||
is given,
|
||||
.I vbackup
|
||||
will not write to Venti any blocks that have not changed
|
||||
since the previous backup.
|
||||
This is only a speed optimization: since the blocks are already
|
||||
stored on Venti they need not be sent to the Venti server again.
|
||||
.PP
|
||||
The options to
|
||||
.I vbackup
|
||||
are:
|
||||
.TP
|
||||
.B -D
|
||||
.TP
|
||||
.B -V
|
||||
.TP
|
||||
.B -n
|
||||
.TP
|
||||
.B -v
|
||||
.TP
|
||||
.B -w \fIn
|
||||
.TP
|
||||
.B -s \fIsecs
|
||||
.PP
|
||||
.I Vcat
|
||||
writes the named disk image to standard output.
|
||||
Unused file system blocks are printed zeroed regardless
|
||||
of their actual content.
|
||||
.PP
|
||||
If the
|
||||
.B -z
|
||||
flag is given,
|
||||
.I vcat
|
||||
will attempt to seek over unused blocks instead of writing to them.
|
||||
The
|
||||
.B -z
|
||||
flag should only be used when standard output is seekable
|
||||
.RI ( i.e. ,
|
||||
when it has been redirected to a file or disk).
|
||||
.PP
|
||||
.I Vftp
|
||||
presents the
|
||||
file system image named by
|
||||
.I disk
|
||||
or
|
||||
.I score
|
||||
in a shell-like
|
||||
interactive session.
|
||||
Type
|
||||
.B help
|
||||
at the
|
||||
.B vftp>
|
||||
prompt for details.
|
||||
.PP
|
||||
.I Vmount
|
||||
mounts the NFS service at the network connection
|
||||
.I address
|
||||
onto
|
||||
.IR mountpoint .
|
||||
On most operating systems,
|
||||
.I vmount
|
||||
must be run by the user
|
||||
.BR root .
|
||||
.PP
|
||||
.I Vmount0
|
||||
is a simple C program that
|
||||
.I vmount
|
||||
uses if
|
||||
.IR mount (8)
|
||||
does not suffice.
|
||||
.PP
|
||||
.I Vnfs
|
||||
serves, using the
|
||||
NFS version 3 protocol,
|
||||
one or more disk images in a synthetic tree defined
|
||||
by the configuration file
|
||||
.IR config .
|
||||
.I Vnfs
|
||||
announces NFS service at
|
||||
.IR addr
|
||||
(default
|
||||
.BR udp!*!nfs )
|
||||
and NFS mount service at
|
||||
.IR mntaddr
|
||||
(default
|
||||
.BR udp!*!\fI999 ),
|
||||
registering both with the port mapper.
|
||||
If no port mapper is found running (on port 111),
|
||||
.I vnfs
|
||||
starts its own port mapper.
|
||||
The options are:
|
||||
.TP
|
||||
.B -r
|
||||
Reply to all NFS requests with RPC rejections.
|
||||
.TP
|
||||
.B -M
|
||||
Do not announce an NFS mount service.
|
||||
.TP
|
||||
.B -P
|
||||
Do not register service with the port mapper.
|
||||
.TP
|
||||
.B -a
|
||||
|
||||
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
Back up the file system stored on
|
||||
.BR /dev/da0s1a :
|
||||
.IP
|
||||
.EX
|
||||
% vbackup /dev/da0s1a
|
||||
ffs:0123456789abcdef0123456789abcdef01234567
|
||||
%
|
||||
.EE
|
||||
.PP
|
||||
Serve that backup and a few others in a tree reminiscent
|
||||
of Plan 9's dump file system, but hide each day's contents of
|
||||
.B /tmp :
|
||||
.IP
|
||||
.EX
|
||||
% cat config
|
||||
mount /2005/0510 ffs:0123456789abcdef\fI...\fP
|
||||
mount /2005/0510/home ffs:0123456789abcdef\fI...\fP
|
||||
mount /2005/0510 ffs:0123456789abcdef\fI...\fP
|
||||
mount /2005/0510/home ffs:0123456789abcdef\fI...\fP
|
||||
hide /*/*/tmp
|
||||
% vnfs -m -b 16k -c 1k config
|
||||
%
|
||||
.EE
|
||||
.PP
|
||||
Mount the backups on a client machine using
|
||||
.IR vmount :
|
||||
.IP
|
||||
.EX
|
||||
# vmount udp!yourserver!nfs /dump
|
||||
# ls /dump
|
||||
2005
|
||||
#
|
||||
.EE
|
||||
.PP
|
||||
Mount the backups using the standard NFS mount program:
|
||||
.IP
|
||||
.EX
|
||||
# mount -t nfs -o soft,intr,ro,nfsv3,rsize=8192,timeo=100 \
|
||||
-o nfsvers=3,nolock,noatime,nodev,nosuid \
|
||||
.EE
|
99
man/man1/yesterday.1
Normal file
99
man/man1/yesterday.1
Normal file
|
@ -0,0 +1,99 @@
|
|||
.TH YESTERDAY 1
|
||||
.SH NAME
|
||||
yesterday \- print file names from the dump
|
||||
.SH SYNOPSIS
|
||||
.B yesterday
|
||||
[
|
||||
.B -cCd
|
||||
] [
|
||||
.B -n
|
||||
.I daysago
|
||||
] [
|
||||
.I \-date
|
||||
]
|
||||
.I files ...
|
||||
.SH DESCRIPTION
|
||||
.I Yesterday
|
||||
prints the names of the
|
||||
.I files
|
||||
from the most recent dump.
|
||||
Since dumps are done early in the morning,
|
||||
yesterday's files are really in today's dump.
|
||||
For example, if today is February 11, 2003,
|
||||
.IP
|
||||
.EX
|
||||
yesterday /home/am3/rsc/.profile
|
||||
.EE
|
||||
.PP
|
||||
prints
|
||||
.IP
|
||||
.EX
|
||||
/dump/am/2003/0211/home/am3/rsc/.profile
|
||||
.EE
|
||||
.PP
|
||||
In fact, the implementation is to select the most recent dump in
|
||||
the current year, so the dump selected may not be from today.
|
||||
.PP
|
||||
By default,
|
||||
.I yesterday
|
||||
prints the names of the dump files corresponding to the named files.
|
||||
The first set of options changes this behavior.
|
||||
.TP
|
||||
.B -c
|
||||
Copy the dump files over the named files.
|
||||
.TP
|
||||
.B -C
|
||||
Copy the dump files over the named files only when
|
||||
they differ.
|
||||
.TP
|
||||
.B -d
|
||||
Run
|
||||
.B diff
|
||||
to compare the dump files with the named files.
|
||||
.PP
|
||||
The
|
||||
.I date
|
||||
option selects other day's dumps, with a format of
|
||||
1, 2, 4, 6, or 8 digits of the form
|
||||
.IR d,
|
||||
.IR dd ,
|
||||
.IR mmdd ,
|
||||
.IR yymmdd ,
|
||||
or
|
||||
.IR yyyymmdd .
|
||||
.PP
|
||||
The
|
||||
.B -n
|
||||
option selects the dump
|
||||
.I daysago
|
||||
prior to the current day.
|
||||
.PP
|
||||
.I Yesterday
|
||||
does not guarantee that the string it prints represents an existing file.
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
See what's changed in the last week in your profile:
|
||||
.IP
|
||||
.EX
|
||||
yesterday -d -n 7 ~/.profile
|
||||
.EE
|
||||
.PP
|
||||
Restore your profile from yesterday:
|
||||
.IP
|
||||
.EX
|
||||
yesterday -c ~/.profile
|
||||
.EE
|
||||
.SH FILES
|
||||
.B /dump
|
||||
.SH SOURCE
|
||||
.B /usr/local/bin/yesterday
|
||||
.SH SEE ALSO
|
||||
.IR diff (1),
|
||||
.IR hist (1)
|
||||
.SH BUGS
|
||||
Backups are only available on
|
||||
.B amsterdam
|
||||
and
|
||||
.BR toil .
|
||||
.PP
|
||||
It's hard to use this command without singing.
|
27
src/cmd/vbackup/COPYRIGHT
Normal file
27
src/cmd/vbackup/COPYRIGHT
Normal file
|
@ -0,0 +1,27 @@
|
|||
This software was developed as part of a project at MIT:
|
||||
$PLAN9/src/libdiskfs/*
|
||||
$PLAN9/include/diskfs.h
|
||||
$PLAN9/src/cmd/vbackup/*
|
||||
|
||||
Copyright (c) 2005 Russ Cox,
|
||||
Massachusetts Institute of Technology
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
515
src/cmd/vbackup/config.c
Normal file
515
src/cmd/vbackup/config.c
Normal file
|
@ -0,0 +1,515 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <thread.h>
|
||||
#include <sunrpc.h>
|
||||
#include <nfs3.h>
|
||||
#include <diskfs.h>
|
||||
#include <venti.h>
|
||||
#include <libsec.h>
|
||||
|
||||
#undef stime
|
||||
#define stime configstime /* sometimes in <time.h> */
|
||||
typedef struct Entry Entry;
|
||||
struct Entry
|
||||
{
|
||||
Entry *parent;
|
||||
Entry *nextdir;
|
||||
Entry *nexthash;
|
||||
Entry *kids;
|
||||
int isfsys;
|
||||
Fsys *fsys;
|
||||
uchar score[VtScoreSize]; /* of fsys */
|
||||
char *name;
|
||||
uchar sha1[VtScoreSize]; /* of path to this entry */
|
||||
ulong time;
|
||||
};
|
||||
|
||||
typedef struct Config Config;
|
||||
struct Config
|
||||
{
|
||||
VtCache *vcache;
|
||||
Entry *root;
|
||||
Entry *hash[1024];
|
||||
Qid qid;
|
||||
};
|
||||
|
||||
Config *config;
|
||||
static ulong mtime; /* mod time */
|
||||
static ulong stime; /* sync time */
|
||||
static char* configfile;
|
||||
|
||||
static int addpath(Config*, char*, uchar[VtScoreSize], ulong);
|
||||
Fsys fsysconfig;
|
||||
|
||||
static void
|
||||
freeconfig(Config *c)
|
||||
{
|
||||
Entry *next, *e;
|
||||
int i;
|
||||
|
||||
for(i=0; i<nelem(c->hash); i++){
|
||||
for(e=c->hash[i]; e; e=next){
|
||||
next = e->nexthash;
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
free(c);
|
||||
}
|
||||
|
||||
static int
|
||||
namehash(uchar *s)
|
||||
{
|
||||
return (s[0]<<2)|(s[1]>>6);
|
||||
}
|
||||
|
||||
static Entry*
|
||||
entrybyhandle(Nfs3Handle *h)
|
||||
{
|
||||
int hh;
|
||||
Entry *e;
|
||||
|
||||
hh = namehash(h->h);
|
||||
for(e=config->hash[hh]; e; e=e->nexthash)
|
||||
if(memcmp(e->sha1, h->h, VtScoreSize) == 0)
|
||||
return e;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static Config*
|
||||
readconfigfile(char *name, VtCache *vcache)
|
||||
{
|
||||
char *p, *pref, *f[10];
|
||||
int ok;
|
||||
Config *c;
|
||||
uchar score[VtScoreSize];
|
||||
int h, nf, line;
|
||||
Biobuf *b;
|
||||
Dir *dir;
|
||||
|
||||
configfile = vtstrdup(name);
|
||||
|
||||
if((dir = dirstat(name)) == nil)
|
||||
return nil;
|
||||
|
||||
if((b = Bopen(name, OREAD)) == nil){
|
||||
free(dir);
|
||||
return nil;
|
||||
}
|
||||
|
||||
line = 0;
|
||||
ok = 1;
|
||||
c = emalloc(sizeof(Config));
|
||||
c->vcache = vcache;
|
||||
c->qid = dir->qid;
|
||||
free(dir);
|
||||
c->root = emalloc(sizeof(Entry));
|
||||
c->root->name = "/";
|
||||
c->root->parent = c->root;
|
||||
sha1((uchar*)"/", 1, c->root->sha1, nil);
|
||||
h = namehash(c->root->sha1);
|
||||
c->hash[h] = c->root;
|
||||
|
||||
for(; (p = Brdstr(b, '\n', 1)) != nil; free(p)){
|
||||
line++;
|
||||
if(p[0] == '#')
|
||||
continue;
|
||||
nf = tokenize(p, f, nelem(f));
|
||||
if(nf != 3){
|
||||
fprint(2, "%s:%d: syntax error\n", name, line);
|
||||
// ok = 0;
|
||||
continue;
|
||||
}
|
||||
if(vtparsescore(f[1], &pref, score) < 0){
|
||||
fprint(2, "%s:%d: bad score '%s'\n", name, line, f[1]);
|
||||
// ok = 0;
|
||||
continue;
|
||||
}
|
||||
if(f[0][0] != '/'){
|
||||
fprint(2, "%s:%d: unrooted path '%s'\n", name, line, f[0]);
|
||||
// ok = 0;
|
||||
continue;
|
||||
}
|
||||
if(addpath(c, f[0], score, strtoul(f[2], 0, 0)) < 0){
|
||||
fprint(2, "%s:%d: %s: %r\n", name, line, f[0]);
|
||||
// ok = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Bterm(b);
|
||||
|
||||
if(!ok){
|
||||
freeconfig(c);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static void
|
||||
refreshconfig(void)
|
||||
{
|
||||
ulong now;
|
||||
Config *c, *old;
|
||||
Dir *d;
|
||||
|
||||
now = time(0);
|
||||
if(now - stime < 60)
|
||||
return;
|
||||
if((d = dirstat(configfile)) == nil)
|
||||
return;
|
||||
if(d->mtime == mtime){
|
||||
free(d);
|
||||
stime = now;
|
||||
return;
|
||||
}
|
||||
|
||||
c = readconfigfile(configfile, config->vcache);
|
||||
if(c == nil){
|
||||
free(d);
|
||||
return;
|
||||
}
|
||||
|
||||
old = config;
|
||||
config = c;
|
||||
stime = now;
|
||||
mtime = d->mtime;
|
||||
free(d);
|
||||
freeconfig(old);
|
||||
}
|
||||
|
||||
static Entry*
|
||||
entrylookup(Entry *e, char *p, int np)
|
||||
{
|
||||
for(e=e->kids; e; e=e->nextdir)
|
||||
if(strlen(e->name) == np && memcmp(e->name, p, np) == 0)
|
||||
return e;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static Entry*
|
||||
walkpath(Config *c, char *name)
|
||||
{
|
||||
Entry *e, *ee;
|
||||
char *p, *nextp;
|
||||
int h;
|
||||
|
||||
e = c->root;
|
||||
p = name;
|
||||
for(; *p; p=nextp){
|
||||
assert(*p == '/');
|
||||
p++;
|
||||
nextp = strchr(p, '/');
|
||||
if(nextp == nil)
|
||||
nextp = p+strlen(p);
|
||||
if(e->fsys){
|
||||
werrstr("%.*s is already a mount point", utfnlen(name, nextp-name), name);
|
||||
return nil;
|
||||
}
|
||||
if((ee = entrylookup(e, p, nextp-p)) == nil){
|
||||
ee = emalloc(sizeof(Entry)+(nextp-p)+1);
|
||||
ee->parent = e;
|
||||
ee->nextdir = e->kids;
|
||||
e->kids = ee;
|
||||
ee->name = (char*)&ee[1];
|
||||
memmove(ee->name, p, nextp-p);
|
||||
ee->name[nextp-p] = 0;
|
||||
sha1((uchar*)name, nextp-name, ee->sha1, nil);
|
||||
h = namehash(ee->sha1);
|
||||
ee->nexthash = c->hash[h];
|
||||
c->hash[h] = ee;
|
||||
}
|
||||
e = ee;
|
||||
}
|
||||
if(e->kids){
|
||||
werrstr("%s already has children; cannot be mount point", name);
|
||||
return nil;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
static int
|
||||
addpath(Config *c, char *name, uchar score[VtScoreSize], ulong time)
|
||||
{
|
||||
Entry *e;
|
||||
|
||||
e = walkpath(c, name);
|
||||
if(e == nil)
|
||||
return -1;
|
||||
e->isfsys = 1;
|
||||
e->time = time;
|
||||
memmove(e->score, score, VtScoreSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mkhandle(Nfs3Handle *h, Entry *e)
|
||||
{
|
||||
memmove(h->h, e->sha1, VtScoreSize);
|
||||
h->len = VtScoreSize;
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
handleparse(Nfs3Handle *h, Fsys **pfsys, Nfs3Handle *nh, int isgetattr)
|
||||
{
|
||||
int hh;
|
||||
Entry *e;
|
||||
Disk *disk;
|
||||
Fsys *fsys;
|
||||
|
||||
refreshconfig();
|
||||
|
||||
if(h->len < VtScoreSize)
|
||||
return Nfs3ErrBadHandle;
|
||||
|
||||
hh = namehash(h->h);
|
||||
for(e=config->hash[hh]; e; e=e->nexthash)
|
||||
if(memcmp(e->sha1, h->h, VtScoreSize) == 0)
|
||||
break;
|
||||
if(e == nil)
|
||||
return Nfs3ErrBadHandle;
|
||||
|
||||
if(e->isfsys == 1 && e->fsys == nil && (h->len != VtScoreSize || !isgetattr)){
|
||||
if((disk = diskopenventi(config->vcache, e->score)) == nil){
|
||||
fprint(2, "cannot open disk %V: %r\n", e->score);
|
||||
return Nfs3ErrIo;
|
||||
}
|
||||
if((fsys = fsysopen(disk)) == nil){
|
||||
fprint(2, "cannot open fsys on %V: %r\n", e->score);
|
||||
diskclose(disk);
|
||||
return Nfs3ErrIo;
|
||||
}
|
||||
e->fsys = fsys;
|
||||
}
|
||||
|
||||
if(e->fsys == nil || (isgetattr && h->len == VtScoreSize)){
|
||||
if(h->len != VtScoreSize)
|
||||
return Nfs3ErrBadHandle;
|
||||
*pfsys = &fsysconfig;
|
||||
*nh = *h;
|
||||
return Nfs3Ok;
|
||||
}
|
||||
*pfsys = e->fsys;
|
||||
if(h->len == VtScoreSize)
|
||||
return fsysroot(*pfsys, nh);
|
||||
nh->len = h->len - VtScoreSize;
|
||||
memmove(nh->h, h->h+VtScoreSize, nh->len);
|
||||
return Nfs3Ok;
|
||||
}
|
||||
|
||||
void
|
||||
handleunparse(Fsys *fsys, Nfs3Handle *h, Nfs3Handle *nh, int dotdot)
|
||||
{
|
||||
Entry *e;
|
||||
int hh;
|
||||
|
||||
refreshconfig();
|
||||
|
||||
if(fsys == &fsysconfig)
|
||||
return;
|
||||
|
||||
if(dotdot && nh->len == h->len - VtScoreSize
|
||||
&& memcmp(h->h+VtScoreSize, nh->h, nh->len) == 0){
|
||||
/* walked .. but didn't go anywhere: must be at root */
|
||||
hh = namehash(h->h);
|
||||
for(e=config->hash[hh]; e; e=e->nexthash)
|
||||
if(memcmp(e->sha1, h->h, VtScoreSize) == 0)
|
||||
break;
|
||||
if(e == nil)
|
||||
return; /* cannot happen */
|
||||
|
||||
/* walk .. */
|
||||
e = e->parent;
|
||||
nh->len = VtScoreSize;
|
||||
memmove(nh->h, e->sha1, VtScoreSize);
|
||||
return;
|
||||
}
|
||||
|
||||
/* otherwise just insert the same prefix */
|
||||
memmove(nh->h+VtScoreSize, nh->h, VtScoreSize);
|
||||
nh->len += VtScoreSize;
|
||||
memmove(nh->h, h->h, VtScoreSize);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsysconfigroot(Fsys *fsys, Nfs3Handle *h)
|
||||
{
|
||||
USED(fsys);
|
||||
|
||||
mkhandle(h, config->root);
|
||||
return Nfs3Ok;
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsysconfiggetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
|
||||
{
|
||||
Entry *e;
|
||||
|
||||
USED(fsys);
|
||||
USED(au);
|
||||
|
||||
if(h->len != VtScoreSize)
|
||||
return Nfs3ErrBadHandle;
|
||||
|
||||
e = entrybyhandle(h);
|
||||
if(e == nil)
|
||||
return Nfs3ErrNoEnt;
|
||||
|
||||
memset(attr, 0, sizeof *attr);
|
||||
attr->type = Nfs3FileDir;
|
||||
attr->mode = 0555;
|
||||
attr->nlink = 2;
|
||||
attr->size = 1024;
|
||||
attr->fileid = *(u64int*)h->h;
|
||||
attr->atime.sec = e->time;
|
||||
attr->mtime.sec = e->time;
|
||||
attr->ctime.sec = e->time;
|
||||
return Nfs3Ok;
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsysconfigaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
|
||||
{
|
||||
want &= Nfs3AccessRead|Nfs3AccessLookup|Nfs3AccessExecute;
|
||||
*got = want;
|
||||
return fsysconfiggetattr(fsys, au, h, attr);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsysconfiglookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
|
||||
{
|
||||
Entry *e;
|
||||
|
||||
USED(fsys);
|
||||
USED(au);
|
||||
|
||||
if(h->len != VtScoreSize)
|
||||
return Nfs3ErrBadHandle;
|
||||
|
||||
e = entrybyhandle(h);
|
||||
if(e == nil)
|
||||
return Nfs3ErrNoEnt;
|
||||
|
||||
if(strcmp(name, "..") == 0)
|
||||
e = e->parent;
|
||||
else if(strcmp(name, ".") == 0){
|
||||
/* nothing */
|
||||
}else{
|
||||
if((e = entrylookup(e, name, strlen(name))) == nil)
|
||||
return Nfs3ErrNoEnt;
|
||||
}
|
||||
|
||||
mkhandle(nh, e);
|
||||
return Nfs3Ok;
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsysconfigreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
|
||||
{
|
||||
USED(h);
|
||||
USED(fsys);
|
||||
USED(au);
|
||||
|
||||
*link = 0;
|
||||
return Nfs3ErrNotSupp;
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsysconfigreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
|
||||
{
|
||||
USED(fsys);
|
||||
USED(h);
|
||||
USED(count);
|
||||
USED(offset);
|
||||
USED(pdata);
|
||||
USED(pcount);
|
||||
USED(peof);
|
||||
USED(au);
|
||||
|
||||
return Nfs3ErrNotSupp;
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsysconfigreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
|
||||
{
|
||||
uchar *data, *p, *ep, *np;
|
||||
u64int c;
|
||||
Entry *e;
|
||||
Nfs3Entry ne;
|
||||
|
||||
USED(fsys);
|
||||
USED(au);
|
||||
|
||||
if(h->len != VtScoreSize)
|
||||
return Nfs3ErrBadHandle;
|
||||
|
||||
e = entrybyhandle(h);
|
||||
if(e == nil)
|
||||
return Nfs3ErrNoEnt;
|
||||
|
||||
e = e->kids;
|
||||
c = cookie;
|
||||
for(; c && e; c--)
|
||||
e = e->nextdir;
|
||||
if(e == nil){
|
||||
*pdata = 0;
|
||||
*pcount = 0;
|
||||
*peof = 1;
|
||||
return Nfs3Ok;
|
||||
}
|
||||
|
||||
data = emalloc(count);
|
||||
p = data;
|
||||
ep = data+count;
|
||||
while(e && p < ep){
|
||||
ne.name = e->name;
|
||||
ne.cookie = ++cookie;
|
||||
ne.fileid = *(u64int*)e->sha1;
|
||||
if(nfs3entrypack(p, ep, &np, &ne) < 0)
|
||||
break;
|
||||
p = np;
|
||||
e = e->nextdir;
|
||||
}
|
||||
*pdata = data;
|
||||
*pcount = p - data;
|
||||
*peof = 0;
|
||||
return Nfs3Ok;
|
||||
}
|
||||
|
||||
void
|
||||
fsysconfigclose(Fsys *fsys)
|
||||
{
|
||||
USED(fsys);
|
||||
}
|
||||
|
||||
int
|
||||
readconfig(char *name, VtCache *vcache, Nfs3Handle *h)
|
||||
{
|
||||
Config *c;
|
||||
Dir *d;
|
||||
|
||||
if((d = dirstat(name)) == nil)
|
||||
return -1;
|
||||
|
||||
c = readconfigfile(name, vcache);
|
||||
if(c == nil){
|
||||
free(d);
|
||||
return -1;
|
||||
}
|
||||
|
||||
config = c;
|
||||
mtime = d->mtime;
|
||||
stime = time(0);
|
||||
free(d);
|
||||
|
||||
mkhandle(h, c->root);
|
||||
fsysconfig._lookup = fsysconfiglookup;
|
||||
fsysconfig._access = fsysconfigaccess;
|
||||
fsysconfig._getattr = fsysconfiggetattr;
|
||||
fsysconfig._readdir = fsysconfigreaddir;
|
||||
fsysconfig._readfile = fsysconfigreadfile;
|
||||
fsysconfig._readlink = fsysconfigreadlink;
|
||||
fsysconfig._root = fsysconfigroot;
|
||||
fsysconfig._close = fsysconfigclose;
|
||||
return 0;
|
||||
}
|
54
src/cmd/vbackup/diskcat.c
Normal file
54
src/cmd/vbackup/diskcat.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <diskfs.h>
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: fscat fspartition\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
extern int nfilereads;
|
||||
u8int *zero;
|
||||
u32int i;
|
||||
u32int n;
|
||||
Block *b;
|
||||
Disk *disk;
|
||||
Fsys *fsys;
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc != 1)
|
||||
usage();
|
||||
|
||||
if((disk = diskopenfile(argv[0])) == nil)
|
||||
sysfatal("diskopen: %r");
|
||||
if((disk = diskcache(disk, 16384, 16)) == nil)
|
||||
sysfatal("diskcache: %r");
|
||||
if((fsys = fsysopen(disk)) == nil)
|
||||
sysfatal("ffsopen: %r");
|
||||
|
||||
zero = emalloc(fsys->blocksize);
|
||||
fprint(2, "%d blocks total\n", fsys->nblock);
|
||||
n = 0;
|
||||
for(i=0; i<fsys->nblock; i++){
|
||||
if((b = fsysreadblock(fsys, i)) != nil){
|
||||
write(1, b->data, fsys->blocksize);
|
||||
n++;
|
||||
blockput(b);
|
||||
}else
|
||||
write(1, zero, fsys->blocksize);
|
||||
if(b == nil && i < 2)
|
||||
sysfatal("block %d not in use", i);
|
||||
}
|
||||
fprint(2, "%d blocks in use, %d file reads\n", n, nfilereads);
|
||||
exits(nil);
|
||||
return 0;
|
||||
}
|
134
src/cmd/vbackup/diskftp.c
Normal file
134
src/cmd/vbackup/diskftp.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include <sunrpc.h>
|
||||
#include <nfs3.h>
|
||||
#include <diskfs.h>
|
||||
|
||||
int debug;
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: fsview fspartition cmd\n");
|
||||
fprint(2, "cmd is:\n");
|
||||
fprint(2, "\tcat file\n");
|
||||
fprint(2, "\tls dir\n");
|
||||
fprint(2, "\tstat file\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
printattr(Nfs3Attr *attr)
|
||||
{
|
||||
Fmt fmt;
|
||||
char buf[256];
|
||||
|
||||
fmtfdinit(&fmt, 1, buf, sizeof buf);
|
||||
nfs3attrprint(&fmt, attr);
|
||||
fmtfdflush(&fmt);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
char buf[8192];
|
||||
|
||||
void
|
||||
x(int ok)
|
||||
{
|
||||
if(ok != Nfs3Ok){
|
||||
nfs3errstr(ok);
|
||||
sysfatal("%r");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
threadmain(int argc, char **argv)
|
||||
{
|
||||
char *p, *q;
|
||||
u32int n;
|
||||
Disk *disk;
|
||||
Fsys *fsys;
|
||||
Nfs3Handle h;
|
||||
SunAuthUnix au;
|
||||
Nfs3Attr attr;
|
||||
u64int offset;
|
||||
u1int eof;
|
||||
uchar *data;
|
||||
char *link;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc != 3)
|
||||
usage();
|
||||
|
||||
if((disk = diskopenfile(argv[0])) == nil)
|
||||
sysfatal("diskopen: %r");
|
||||
if((disk = diskcache(disk, 16384, 16)) == nil)
|
||||
sysfatal("diskcache: %r");
|
||||
if((fsys = fsysopen(disk)) == nil)
|
||||
sysfatal("ffsopen: %r");
|
||||
|
||||
allowall = 1;
|
||||
memset(&au, 0, sizeof au);
|
||||
|
||||
/* walk */
|
||||
if(debug) fprint(2, "get root...");
|
||||
x(fsysroot(fsys, &h));
|
||||
p = argv[2];
|
||||
while(*p){
|
||||
while(*p == '/')
|
||||
p++;
|
||||
if(*p == 0)
|
||||
break;
|
||||
q = strchr(p, '/');
|
||||
if(q){
|
||||
*q = 0;
|
||||
q++;
|
||||
}else
|
||||
q = "";
|
||||
if(debug) fprint(2, "walk %s...", p);
|
||||
x(fsyslookup(fsys, &au, &h, p, &h));
|
||||
p = q;
|
||||
}
|
||||
|
||||
if(debug) fprint(2, "getattr...");
|
||||
x(fsysgetattr(fsys, &au, &h, &attr));
|
||||
printattr(&attr);
|
||||
|
||||
/* do the op */
|
||||
if(strcmp(argv[1], "cat") == 0){
|
||||
switch(attr.type){
|
||||
case Nfs3FileReg:
|
||||
offset = 0;
|
||||
for(;;){
|
||||
x(fsysreadfile(fsys, &au, &h, sizeof buf, offset, &data, &n, &eof));
|
||||
if(n){
|
||||
write(1, data, n);
|
||||
free(data);
|
||||
offset += n;
|
||||
}
|
||||
if(eof)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Nfs3FileSymlink:
|
||||
x(fsysreadlink(fsys, &au, &h, &link));
|
||||
print("%s\n", link);
|
||||
break;
|
||||
default:
|
||||
print("cannot cat: not file, not link\n");
|
||||
break;
|
||||
}
|
||||
}else if(strcmp(argv[1], "ls") == 0){
|
||||
/* not implemented */
|
||||
}else if(strcmp(argv[1], "stat") == 0){
|
||||
/* already done */
|
||||
}
|
||||
threadexitsall(nil);
|
||||
}
|
126
src/cmd/vbackup/disknfs.c
Normal file
126
src/cmd/vbackup/disknfs.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ip.h>
|
||||
#include <thread.h>
|
||||
#include <sunrpc.h>
|
||||
#include <nfs3.h>
|
||||
#include <diskfs.h>
|
||||
#include "nfs3srv.h"
|
||||
|
||||
Disk *disk;
|
||||
Fsys *fsys;
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: disknfs [-RTr] disk\n");
|
||||
threadexitsall("usage");
|
||||
}
|
||||
|
||||
extern int _threaddebuglevel;
|
||||
|
||||
void
|
||||
threadmain(int argc, char **argv)
|
||||
{
|
||||
char *addr;
|
||||
SunSrv *srv;
|
||||
Channel *nfs3chan;
|
||||
Channel *mountchan;
|
||||
Nfs3Handle h;
|
||||
|
||||
fmtinstall('B', sunrpcfmt);
|
||||
fmtinstall('C', suncallfmt);
|
||||
fmtinstall('H', encodefmt);
|
||||
fmtinstall('I', eipfmt);
|
||||
sunfmtinstall(&nfs3prog);
|
||||
sunfmtinstall(&nfsmount3prog);
|
||||
|
||||
srv = sunsrv();
|
||||
addr = "*";
|
||||
|
||||
ARGBEGIN{
|
||||
case 'R':
|
||||
srv->chatty++;
|
||||
break;
|
||||
case 'T':
|
||||
_threaddebuglevel = 0xFFFFFFFF;
|
||||
break;
|
||||
case 'r':
|
||||
srv->alwaysreject++;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(argc != 1 && argc != 2)
|
||||
usage();
|
||||
|
||||
if((disk = diskopenfile(argv[0])) == nil)
|
||||
sysfatal("diskopen: %r");
|
||||
if((disk = diskcache(disk, 16384, 256)) == nil)
|
||||
sysfatal("diskcache: %r");
|
||||
|
||||
if((fsys = fsysopen(disk)) == nil)
|
||||
sysfatal("ffsopen: %r");
|
||||
|
||||
nfs3chan = chancreate(sizeof(SunMsg*), 0);
|
||||
mountchan = chancreate(sizeof(SunMsg*), 0);
|
||||
|
||||
if(argc > 1)
|
||||
addr = argv[1];
|
||||
addr = netmkaddr(addr, "udp", "2049");
|
||||
|
||||
if(sunsrvudp(srv, addr) < 0)
|
||||
sysfatal("starting server: %r");
|
||||
sunsrvprog(srv, &nfs3prog, nfs3chan);
|
||||
sunsrvprog(srv, &nfsmount3prog, mountchan);
|
||||
|
||||
sunsrvthreadcreate(srv, nfs3proc, nfs3chan);
|
||||
sunsrvthreadcreate(srv, mount3proc, mountchan);
|
||||
|
||||
fsgetroot(&h);
|
||||
print("mountbackups -h %.*H %s /mountpoint\n", h.len, h.h, addr);
|
||||
|
||||
threadexits(nil);
|
||||
}
|
||||
|
||||
void
|
||||
fsgetroot(Nfs3Handle *h)
|
||||
{
|
||||
fsysroot(fsys, h);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsgetattr(SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
|
||||
{
|
||||
return fsysgetattr(fsys, au, h, attr);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fslookup(SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
|
||||
{
|
||||
return fsyslookup(fsys, au, h, name, nh);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsaccess(SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
|
||||
{
|
||||
return fsysaccess(fsys, au, h, want, got, attr);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsreadlink(SunAuthUnix *au, Nfs3Handle *h, char **link)
|
||||
{
|
||||
return fsysreadlink(fsys, au, h, link);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsreadfile(SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
|
||||
{
|
||||
return fsysreadfile(fsys, au, h, count, offset, data, pcount, peof);
|
||||
}
|
||||
|
||||
Nfs3Status
|
||||
fsreaddir(SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **data, u32int *pcount, u1int *peof)
|
||||
{
|
||||
return fsysreaddir(fsys, au, h, count, cookie, data, pcount, peof);
|
||||
}
|
||||
|
282
src/cmd/vbackup/hist.c
Normal file
282
src/cmd/vbackup/hist.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define MINUTE(x) ((long)(x)*60L)
|
||||
#define HOUR(x) (MINUTE(x)*60L)
|
||||
#define YEAR(x) (HOUR(x)*24L*360L)
|
||||
|
||||
int verb;
|
||||
int uflag;
|
||||
int force;
|
||||
int diff;
|
||||
int diffb;
|
||||
char* sflag;
|
||||
char *sys;
|
||||
|
||||
void ysearch(char*);
|
||||
long starttime(char*);
|
||||
void lastbefore(ulong, char*, char*, char*);
|
||||
char* prtime(ulong);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
sys = sysname();
|
||||
if(strncmp(sys, "amsterdam", 9) == 0)
|
||||
sys = "am";
|
||||
else if(strncmp(sys, "toil", 4) == 0)
|
||||
sys = "toil";
|
||||
|
||||
ARGBEGIN {
|
||||
default:
|
||||
goto usage;
|
||||
case 'v':
|
||||
verb = 1;
|
||||
break;
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
case 'd':
|
||||
diff = 1;
|
||||
break;
|
||||
case 'b':
|
||||
diffb = 1;
|
||||
break;
|
||||
case 's':
|
||||
sflag = ARGF();
|
||||
break;
|
||||
case 'u':
|
||||
uflag = 1;
|
||||
break;
|
||||
} ARGEND
|
||||
|
||||
if(argc == 0) {
|
||||
usage:
|
||||
fprint(2, "usage: hist [-bdfuv] [-s yyyymmdd] files\n");
|
||||
exits(0);
|
||||
}
|
||||
|
||||
for(i=0; i<argc; i++)
|
||||
ysearch(argv[i]);
|
||||
exits(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
strprefix(char *a, char *aa)
|
||||
{
|
||||
return memcmp(a, aa, strlen(a)) == 0;
|
||||
}
|
||||
|
||||
void
|
||||
ysearch(char *file)
|
||||
{
|
||||
char *ndump;
|
||||
char fil[400], buf[500], nbuf[100], pair[2][500], *p;
|
||||
Tm *tm;
|
||||
Dir *dir, *d;
|
||||
ulong otime, dt;
|
||||
int toggle, started, missing;
|
||||
|
||||
started = 0;
|
||||
dir = dirstat(file);
|
||||
if(dir == nil)
|
||||
fprint(2, "history: warning: %s does not exist\n", file);
|
||||
else{
|
||||
print("%s %s %lld\n", prtime(dir->mtime), file, dir->length);
|
||||
started = 1;
|
||||
strcpy(pair[1], file);
|
||||
}
|
||||
free(dir);
|
||||
fil[0] = 0;
|
||||
if(file[0] != '/') {
|
||||
getwd(strchr(fil, 0), 100);
|
||||
strcat(fil, "/");
|
||||
}
|
||||
strcat(fil, file);
|
||||
cleanname(fil);
|
||||
|
||||
sprint(nbuf, "/dump/%s", sys);
|
||||
ndump = nbuf;
|
||||
|
||||
tm = localtime(time(0));
|
||||
sprint(buf, "%s/%.4d/", ndump, tm->year+1900);
|
||||
if(access(buf, AREAD) < 0){
|
||||
print("cannot access %s\n", buf);
|
||||
return;
|
||||
}
|
||||
|
||||
otime = starttime(sflag);
|
||||
toggle = 0;
|
||||
for(;;) {
|
||||
lastbefore(otime, fil, buf, ndump);
|
||||
dir = dirstat(buf);
|
||||
if(dir == nil) {
|
||||
if(!force)
|
||||
return;
|
||||
dir = malloc(sizeof(Dir));
|
||||
nulldir(dir);
|
||||
dir->mtime = otime + 1;
|
||||
}
|
||||
dt = HOUR(12);
|
||||
missing = 0;
|
||||
while(otime <= dir->mtime){
|
||||
if(verb)
|
||||
print("backup %ld, %ld\n", dir->mtime, otime-dt);
|
||||
lastbefore(otime-dt, fil, buf, ndump);
|
||||
d = dirstat(buf);
|
||||
if(d == nil){
|
||||
if(!force)
|
||||
return;
|
||||
if(!missing)
|
||||
print("removed %s\n", buf);
|
||||
missing = 1;
|
||||
}else{
|
||||
free(dir);
|
||||
dir = d;
|
||||
}
|
||||
dt += HOUR(12);
|
||||
}
|
||||
strcpy(pair[toggle], buf);
|
||||
if(diff && started){
|
||||
if(verb)
|
||||
print("diff %s %s\n", pair[toggle^1], pair[toggle]);
|
||||
switch(rfork(RFFDG|RFPROC)){
|
||||
case 0:
|
||||
execlp("diff", "diff", diffb ? "-cb" : "-c", pair[toggle], pair[toggle ^ 1], 0);
|
||||
fprint(2, "can't exec diff: %r\n");
|
||||
exits(0);
|
||||
case -1:
|
||||
fprint(2, "can't fork diff: %r\n");
|
||||
break;
|
||||
default:
|
||||
while(waitpid() != -1)
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
print("%s %s %lld\n", prtime(dir->mtime), buf, dir->length);
|
||||
toggle ^= 1;
|
||||
started = 1;
|
||||
otime = dir->mtime;
|
||||
free(dir);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lastbefore(ulong t, char *f, char *b, char *ndump)
|
||||
{
|
||||
Tm *tm;
|
||||
Dir *dir;
|
||||
int vers, try;
|
||||
ulong t0, mtime;
|
||||
|
||||
t0 = t;
|
||||
if(verb)
|
||||
print("%ld lastbefore %s\n", t0, f);
|
||||
mtime = 0;
|
||||
for(try=0; try<10; try++) {
|
||||
tm = localtime(t);
|
||||
sprint(b, "%s/%.4d/%.2d%.2d", ndump,
|
||||
tm->year+1900, tm->mon+1, tm->mday);
|
||||
dir = dirstat(b);
|
||||
if(dir){
|
||||
mtime = dir->mtime;
|
||||
free(dir);
|
||||
}
|
||||
if(dir==nil || mtime > t0) {
|
||||
if(verb)
|
||||
print("%ld earlier %s\n", mtime, b);
|
||||
t -= HOUR(24);
|
||||
continue;
|
||||
}
|
||||
for(vers=0;; vers++) {
|
||||
sprint(b, "%s/%.4d/%.2d%.2d%d", ndump,
|
||||
tm->year+1900, tm->mon+1, tm->mday, vers+1);
|
||||
dir = dirstat(b);
|
||||
if(dir){
|
||||
mtime = dir->mtime;
|
||||
free(dir);
|
||||
}
|
||||
if(dir==nil || mtime > t0)
|
||||
break;
|
||||
if(verb)
|
||||
print("%ld later %s\n", mtime, b);
|
||||
}
|
||||
sprint(b, "%s/%.4d/%.2d%.2d%s", ndump,
|
||||
tm->year+1900, tm->mon+1, tm->mday, f);
|
||||
if(vers)
|
||||
sprint(b, "%s/%.4d/%.2d%.2d%d%s", ndump,
|
||||
tm->year+1900, tm->mon+1, tm->mday, vers, f);
|
||||
return;
|
||||
}
|
||||
strcpy(b, "XXX"); /* error */
|
||||
}
|
||||
|
||||
char*
|
||||
prtime(ulong t)
|
||||
{
|
||||
static char buf[100];
|
||||
char *b;
|
||||
Tm *tm;
|
||||
|
||||
if(uflag)
|
||||
tm = gmtime(t);
|
||||
else
|
||||
tm = localtime(t);
|
||||
b = asctime(tm);
|
||||
memcpy(buf, b+4, 24);
|
||||
buf[24] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
long
|
||||
starttime(char *s)
|
||||
{
|
||||
Tm *tm;
|
||||
long t, dt;
|
||||
int i, yr, mo, da;
|
||||
|
||||
t = time(0);
|
||||
if(s == 0)
|
||||
return t;
|
||||
for(i=0; s[i]; i++)
|
||||
if(s[i] < '0' || s[i] > '9') {
|
||||
fprint(2, "bad start time: %s\n", s);
|
||||
return t;
|
||||
}
|
||||
if(strlen(s)==6){
|
||||
yr = (s[0]-'0')*10 + s[1]-'0';
|
||||
mo = (s[2]-'0')*10 + s[3]-'0' - 1;
|
||||
da = (s[4]-'0')*10 + s[5]-'0';
|
||||
if(yr < 70)
|
||||
yr += 100;
|
||||
}else if(strlen(s)==8){
|
||||
yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
|
||||
yr -= 1900;
|
||||
mo = (s[4]-'0')*10 + s[5]-'0' - 1;
|
||||
da = (s[6]-'0')*10 + s[7]-'0';
|
||||
}else{
|
||||
fprint(2, "bad start time: %s\n", s);
|
||||
return t;
|
||||
}
|
||||
t = 0;
|
||||
dt = YEAR(10);
|
||||
for(i=0; i<50; i++) {
|
||||
tm = localtime(t+dt);
|
||||
if(yr > tm->year ||
|
||||
(yr == tm->year && mo > tm->mon) ||
|
||||
(yr == tm->year && mo == tm->mon) && da > tm->mday) {
|
||||
t += dt;
|
||||
continue;
|
||||
}
|
||||
dt /= 2;
|
||||
if(dt == 0)
|
||||
break;
|
||||
}
|
||||
t += HOUR(12); /* .5 day to get to noon of argument */
|
||||
return t;
|
||||
}
|
28
src/cmd/vbackup/mkfile
Normal file
28
src/cmd/vbackup/mkfile
Normal file
|
@ -0,0 +1,28 @@
|
|||
<$PLAN9/src/mkhdr
|
||||
CC=9c
|
||||
|
||||
TARG=\
|
||||
disknfs\
|
||||
vbackup\
|
||||
vcat\
|
||||
vmount0\
|
||||
vnfs\
|
||||
|
||||
OFILES=util.$O
|
||||
HFILES=$PLAN9/include/diskfs.h
|
||||
|
||||
<$PLAN9/src/mkmany
|
||||
|
||||
disknfs.$O: nfs3srv.h
|
||||
mount-%.$O: mountnfs.h
|
||||
nfs3srv.$O: nfs3srv.h
|
||||
queue.$O: queue.h
|
||||
vbackup.$O: queue.h
|
||||
vmount0.$O: mountnfs.h
|
||||
vnfs.$O: nfs3srv.h
|
||||
|
||||
$O.disknfs: nfs3srv.$O
|
||||
$O.vbackup: vbackup.$O queue.$O
|
||||
$O.vmount0: vmount0.$O mount-$SYSNAME.$O
|
||||
$O.vnfs: nfs3srv.$O
|
||||
|
52
src/cmd/vbackup/mount-FreeBSD.c
Normal file
52
src/cmd/vbackup/mount-FreeBSD.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include <u.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <rpc/pmap_clnt.h>
|
||||
#include <rpc/pmap_prot.h>
|
||||
#include <nfs/rpcv2.h>
|
||||
#include <nfs/nfsproto.h>
|
||||
#include <nfs/nfs.h>
|
||||
#include <libc.h>
|
||||
#include "mountnfs.h"
|
||||
|
||||
void
|
||||
mountnfs(int proto, struct sockaddr_in *sa,
|
||||
uchar *handle, int nhandle, char *mtpt)
|
||||
{
|
||||
int mflag;
|
||||
struct nfs_args na;
|
||||
|
||||
memset(&na, 0, sizeof na);
|
||||
na.version = NFS_ARGSVERSION;
|
||||
na.addr = (struct sockaddr*)sa;
|
||||
na.addrlen = sizeof *sa;
|
||||
na.sotype = proto;
|
||||
na.proto = (proto == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
|
||||
na.fh = handle;
|
||||
na.fhsize = nhandle;
|
||||
na.flags = NFSMNT_RESVPORT|NFSMNT_NFSV3|NFSMNT_INT;
|
||||
na.wsize = NFS_WSIZE;
|
||||
na.rsize = NFS_RSIZE;
|
||||
na.readdirsize = NFS_READDIRSIZE;
|
||||
na.timeo = 2;
|
||||
na.retrans = NFS_RETRANS;
|
||||
na.maxgrouplist = NFS_MAXGRPS;
|
||||
na.readahead = 0;
|
||||
na.leaseterm = 0;
|
||||
na.deadthresh = 0;
|
||||
na.hostname = "backup";
|
||||
na.acregmin = 60;
|
||||
na.acregmax = 600;
|
||||
na.acdirmin = 60;
|
||||
na.acdirmax = 600;
|
||||
|
||||
mflag = MNT_RDONLY|MNT_NOSUID|MNT_NOATIME|MNT_NODEV;
|
||||
if(mount("nfs", mtpt, mflag, &na) < 0)
|
||||
sysfatal("mount: %r");
|
||||
}
|
58
src/cmd/vbackup/mount-Linux.c
Normal file
58
src/cmd/vbackup/mount-Linux.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include <u.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mount.h>
|
||||
#ifdef __Linux24__
|
||||
# define __KERNEL__
|
||||
# include <linux/nfs.h>
|
||||
# undef __KERNEL__
|
||||
#else
|
||||
# include <linux/nfs.h>
|
||||
#endif
|
||||
#include <linux/nfs2.h>
|
||||
#include <linux/nfs_mount.h>
|
||||
#include <libc.h>
|
||||
#include "mountnfs.h"
|
||||
|
||||
void
|
||||
mountnfs(int proto, struct sockaddr_in *sa, uchar *handle, int nhandle, char *mtpt)
|
||||
{
|
||||
int mflag, fd;
|
||||
struct nfs_mount_data nfs;
|
||||
|
||||
fd = socket(AF_INET, proto, proto==SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP);
|
||||
if(fd < 0)
|
||||
sysfatal("socket: %r");
|
||||
|
||||
memset(&nfs, 0, sizeof nfs);
|
||||
nfs.version = NFS_MOUNT_VERSION;
|
||||
nfs.fd = fd;
|
||||
nfs.flags =
|
||||
NFS_MOUNT_SOFT|
|
||||
NFS_MOUNT_INTR|
|
||||
NFS_MOUNT_NOAC|
|
||||
NFS_MOUNT_NOCTO|
|
||||
NFS_MOUNT_VER3|
|
||||
NFS_MOUNT_NONLM;
|
||||
if(proto==SOCK_STREAM)
|
||||
nfs.flags |= NFS_MOUNT_TCP;
|
||||
nfs.rsize = 8192;
|
||||
nfs.wsize = 8192;
|
||||
nfs.timeo = 120;
|
||||
nfs.retrans = 2;
|
||||
nfs.acregmin = 60;
|
||||
nfs.acregmax = 600;
|
||||
nfs.acdirmin = 60;
|
||||
nfs.acdirmax = 600;
|
||||
nfs.addr = *sa;
|
||||
strcpy(nfs.hostname, "backup");
|
||||
nfs.namlen = 1024;
|
||||
nfs.bsize = 8192;
|
||||
memcpy(nfs.root.data, handle, nhandle);
|
||||
nfs.root.size = nhandle;
|
||||
mflag = MS_NOATIME|MS_NODEV|MS_NODIRATIME|
|
||||
MS_NOEXEC|MS_NOSUID|MS_RDONLY;
|
||||
|
||||
if(mount("backup:/", mtpt, "nfs", mflag, &nfs) < 0)
|
||||
sysfatal("mount: %r");
|
||||
}
|
||||
|
12
src/cmd/vbackup/mount-none.c
Normal file
12
src/cmd/vbackup/mount-none.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <u.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <libc.h>
|
||||
#include "mountnfs.h"
|
||||
|
||||
void
|
||||
mountnfs(int proto, struct sockaddr_in *addr, uchar *handle, int hlen, char *mtpt)
|
||||
{
|
||||
sysfatal("mountnfs not implemented");
|
||||
}
|
||||
|
1
src/cmd/vbackup/mountnfs.h
Normal file
1
src/cmd/vbackup/mountnfs.h
Normal file
|
@ -0,0 +1 @@
|
|||
void mountnfs(int proto, struct sockaddr_in*, uchar*, int, char*);
|
428
src/cmd/vbackup/nfs3srv.c
Normal file
428
src/cmd/vbackup/nfs3srv.c
Normal file
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* Simple read-only NFS v3 server.
|
||||
* Runs every request in its own thread.
|
||||
* Expects client to provide the fsxxx routines in nfs3srv.h.
|
||||
*/
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include <sunrpc.h>
|
||||
#include <nfs3.h>
|
||||
#include "nfs3srv.h"
|
||||
|
||||
static SunStatus
|
||||
authunixunpack(SunRpc *rpc, SunAuthUnix *au)
|
||||
{
|
||||
uchar *p, *ep;
|
||||
SunAuthInfo *ai;
|
||||
|
||||
ai = &rpc->cred;
|
||||
if(ai->flavor != SunAuthSys)
|
||||
return SunAuthTooWeak;
|
||||
p = ai->data;
|
||||
ep = p+ai->ndata;
|
||||
if(sunauthunixunpack(p, ep, &p, au) < 0)
|
||||
return SunGarbageArgs;
|
||||
if(au->uid == 0)
|
||||
au->uid = -1;
|
||||
if(au->gid == 0)
|
||||
au->gid = -1;
|
||||
|
||||
return SunSuccess;
|
||||
}
|
||||
|
||||
static int
|
||||
rnull(SunMsg *m)
|
||||
{
|
||||
NfsMount3RNull rx;
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rmnt(SunMsg *m)
|
||||
{
|
||||
Nfs3Handle nh;
|
||||
NfsMount3RMnt rx;
|
||||
SunAuthUnix au;
|
||||
int ok;
|
||||
|
||||
if((ok = authunixunpack(&m->rpc, &au)) != SunSuccess)
|
||||
return sunmsgreplyerror(m, ok);
|
||||
|
||||
/* ignore file system path and return the dump tree */
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.nauth = 0;
|
||||
rx.status = 0;
|
||||
memset(&nh, 0, sizeof nh);
|
||||
fsgetroot(&nh);
|
||||
rx.handle = nh.h;
|
||||
rx.len = nh.len;
|
||||
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rumnt(SunMsg *m)
|
||||
{
|
||||
NfsMount3RUmnt rx;
|
||||
|
||||
/* ignore */
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rumntall(SunMsg *m)
|
||||
{
|
||||
NfsMount3RUmntall rx;
|
||||
|
||||
/* ignore */
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rexport(SunMsg *m)
|
||||
{
|
||||
NfsMount3RExport rx;
|
||||
|
||||
/* ignore */
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.count = 0;
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static void
|
||||
rmount3(void *v)
|
||||
{
|
||||
SunMsg *m;
|
||||
|
||||
m = v;
|
||||
switch(m->call->type){
|
||||
default:
|
||||
sunmsgreplyerror(m, SunProcUnavail);
|
||||
case NfsMount3CallTNull:
|
||||
rnull(m);
|
||||
break;
|
||||
case NfsMount3CallTMnt:
|
||||
rmnt(m);
|
||||
break;
|
||||
case NfsMount3CallTDump:
|
||||
rmnt(m);
|
||||
break;
|
||||
case NfsMount3CallTUmnt:
|
||||
rumnt(m);
|
||||
break;
|
||||
case NfsMount3CallTUmntall:
|
||||
rumntall(m);
|
||||
break;
|
||||
case NfsMount3CallTExport:
|
||||
rexport(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mount3proc(void *v)
|
||||
{
|
||||
Channel *c;
|
||||
SunMsg *m;
|
||||
|
||||
threadsetname("mount1");
|
||||
c = v;
|
||||
while((m=recvp(c)) != nil)
|
||||
threadcreate(rmount3, m, SunStackSize);
|
||||
}
|
||||
|
||||
static int
|
||||
senderror(SunMsg *m, SunCall *rc, Nfs3Status status)
|
||||
{
|
||||
/* knows that status is first field in all replies */
|
||||
((Nfs3RGetattr*)rc)->status = status;
|
||||
return sunmsgreply(m, rc);
|
||||
}
|
||||
|
||||
static int
|
||||
rnull0(SunMsg *m)
|
||||
{
|
||||
Nfs3RNull rx;
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rgetattr(SunMsg *m)
|
||||
{
|
||||
Nfs3TGetattr *tx = (Nfs3TGetattr*)m->call;
|
||||
Nfs3RGetattr rx;
|
||||
SunAuthUnix au;
|
||||
int ok;
|
||||
|
||||
if((ok = authunixunpack(&m->rpc, &au)) != SunSuccess)
|
||||
return sunmsgreplyerror(m, ok);
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.status = fsgetattr(&au, &tx->handle, &rx.attr);
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rlookup(SunMsg *m)
|
||||
{
|
||||
Nfs3TLookup *tx = (Nfs3TLookup*)m->call;
|
||||
Nfs3RLookup rx;
|
||||
SunAuthUnix au;
|
||||
int ok;
|
||||
|
||||
if((ok = authunixunpack(&m->rpc, &au)) != SunSuccess)
|
||||
return sunmsgreplyerror(m, ok);
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.status = fsgetattr(&au, &tx->handle, &rx.dirAttr);
|
||||
if(rx.status != Nfs3Ok)
|
||||
return sunmsgreply(m, &rx.call);
|
||||
rx.haveDirAttr = 1;
|
||||
rx.status = fslookup(&au, &tx->handle, tx->name, &rx.handle);
|
||||
if(rx.status != Nfs3Ok)
|
||||
return sunmsgreply(m, &rx.call);
|
||||
rx.status = fsgetattr(&au, &rx.handle, &rx.attr);
|
||||
if(rx.status != Nfs3Ok)
|
||||
return sunmsgreply(m, &rx.call);
|
||||
rx.haveAttr = 1;
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
raccess(SunMsg *m)
|
||||
{
|
||||
Nfs3TAccess *tx = (Nfs3TAccess*)m->call;
|
||||
Nfs3RAccess rx;
|
||||
SunAuthUnix au;
|
||||
int ok;
|
||||
|
||||
if((ok = authunixunpack(&m->rpc, &au)) != SunSuccess)
|
||||
return sunmsgreplyerror(m, ok);
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.haveAttr = 1;
|
||||
rx.status = fsaccess(&au, &tx->handle, tx->access, &rx.access, &rx.attr);
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rreadlink(SunMsg *m)
|
||||
{
|
||||
Nfs3RReadlink rx;
|
||||
Nfs3TReadlink *tx = (Nfs3TReadlink*)m->call;
|
||||
SunAuthUnix au;
|
||||
int ok;
|
||||
|
||||
if((ok = authunixunpack(&m->rpc, &au)) != SunSuccess)
|
||||
return sunmsgreplyerror(m, ok);
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.haveAttr = 0;
|
||||
rx.data = nil;
|
||||
rx.status = fsreadlink(&au, &tx->handle, &rx.data);
|
||||
sunmsgreply(m, &rx.call);
|
||||
free(rx.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rread(SunMsg *m)
|
||||
{
|
||||
Nfs3TRead *tx = (Nfs3TRead*)m->call;
|
||||
Nfs3RRead rx;
|
||||
SunAuthUnix au;
|
||||
int ok;
|
||||
|
||||
if((ok = authunixunpack(&m->rpc, &au)) != SunSuccess)
|
||||
return sunmsgreplyerror(m, ok);
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.haveAttr = 0;
|
||||
rx.data = nil;
|
||||
rx.status = fsreadfile(&au, &tx->handle, tx->count, tx->offset, &rx.data, &rx.count, &rx.eof);
|
||||
if(rx.status == Nfs3Ok)
|
||||
rx.ndata = rx.count;
|
||||
|
||||
sunmsgreply(m, &rx.call);
|
||||
free(rx.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rreaddir(SunMsg *m)
|
||||
{
|
||||
Nfs3TReadDir *tx = (Nfs3TReadDir*)m->call;
|
||||
Nfs3RReadDir rx;
|
||||
SunAuthUnix au;
|
||||
int ok;
|
||||
|
||||
if((ok = authunixunpack(&m->rpc, &au)) != SunSuccess)
|
||||
return sunmsgreplyerror(m, ok);
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.status = fsreaddir(&au, &tx->handle, tx->count, tx->cookie, &rx.data, &rx.count, &rx.eof);
|
||||
sunmsgreply(m, &rx.call);
|
||||
free(rx.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rreaddirplus(SunMsg *m)
|
||||
{
|
||||
Nfs3RReadDirPlus rx;
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.status = Nfs3ErrNotSupp;
|
||||
sunmsgreply(m, &rx.call);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rfsstat(SunMsg *m)
|
||||
{
|
||||
Nfs3RFsStat rx;
|
||||
|
||||
/* just make something up */
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.status = Nfs3Ok;
|
||||
rx.haveAttr = 0;
|
||||
rx.totalBytes = 1000000000;
|
||||
rx.freeBytes = 0;
|
||||
rx.availBytes = 0;
|
||||
rx.totalFiles = 100000;
|
||||
rx.freeFiles = 0;
|
||||
rx.availFiles = 0;
|
||||
rx.invarSec = 0;
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rfsinfo(SunMsg *m)
|
||||
{
|
||||
Nfs3RFsInfo rx;
|
||||
|
||||
/* just make something up */
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.status = Nfs3Ok;
|
||||
rx.haveAttr = 0;
|
||||
rx.readMax = MaxDataSize;
|
||||
rx.readPref = MaxDataSize;
|
||||
rx.readMult = MaxDataSize;
|
||||
rx.writeMax = MaxDataSize;
|
||||
rx.writePref = MaxDataSize;
|
||||
rx.writeMult = MaxDataSize;
|
||||
rx.readDirPref = MaxDataSize;
|
||||
rx.maxFileSize = 1LL<<60;
|
||||
rx.timePrec.sec = 1;
|
||||
rx.timePrec.nsec = 0;
|
||||
rx.flags = Nfs3FsHomogeneous|Nfs3FsCanSetTime;
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rpathconf(SunMsg *m)
|
||||
{
|
||||
Nfs3RPathconf rx;
|
||||
|
||||
memset(&rx, 0, sizeof rx);
|
||||
rx.status = Nfs3Ok;
|
||||
rx.haveAttr = 0;
|
||||
rx.maxLink = 1;
|
||||
rx.maxName = 1024;
|
||||
rx.noTrunc = 1;
|
||||
rx.chownRestricted = 0;
|
||||
rx.caseInsensitive = 0;
|
||||
rx.casePreserving = 1;
|
||||
return sunmsgreply(m, &rx.call);
|
||||
}
|
||||
|
||||
static int
|
||||
rrofs(SunMsg *m)
|
||||
{
|
||||
uchar buf[512]; /* clumsy hack*/
|
||||
|
||||
memset(buf, 0, sizeof buf);
|
||||
return senderror(m, (SunCall*)buf, Nfs3ErrRoFs);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
rnfs3(void *v)
|
||||
{
|
||||
SunMsg *m;
|
||||
|
||||
m = v;
|
||||
switch(m->call->type){
|
||||
default:
|
||||
abort();
|
||||
case Nfs3CallTNull:
|
||||
rnull0(m);
|
||||
break;
|
||||
case Nfs3CallTGetattr:
|
||||
rgetattr(m);
|
||||
break;
|
||||
case Nfs3CallTLookup:
|
||||
rlookup(m);
|
||||
break;
|
||||
case Nfs3CallTAccess:
|
||||
raccess(m);
|
||||
break;
|
||||
case Nfs3CallTReadlink:
|
||||
rreadlink(m);
|
||||
break;
|
||||
case Nfs3CallTRead:
|
||||
rread(m);
|
||||
break;
|
||||
case Nfs3CallTReadDir:
|
||||
rreaddir(m);
|
||||
break;
|
||||
case Nfs3CallTReadDirPlus:
|
||||
rreaddirplus(m);
|
||||
break;
|
||||
case Nfs3CallTFsStat:
|
||||
rfsstat(m);
|
||||
break;
|
||||
case Nfs3CallTFsInfo:
|
||||
rfsinfo(m);
|
||||
break;
|
||||
case Nfs3CallTPathconf:
|
||||
rpathconf(m);
|
||||
break;
|
||||
case Nfs3CallTSetattr:
|
||||
case Nfs3CallTWrite:
|
||||
case Nfs3CallTCreate:
|
||||
case Nfs3CallTMkdir:
|
||||
case Nfs3CallTSymlink:
|
||||
case Nfs3CallTMknod:
|
||||
case Nfs3CallTRemove:
|
||||
case Nfs3CallTRmdir:
|
||||
case Nfs3CallTLink:
|
||||
case Nfs3CallTCommit:
|
||||
rrofs(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nfs3proc(void *v)
|
||||
{
|
||||
Channel *c;
|
||||
SunMsg *m;
|
||||
|
||||
c = v;
|
||||
threadsetname("nfs3");
|
||||
while((m = recvp(c)) != nil)
|
||||
threadcreate(rnfs3, m, SunStackSize);
|
||||
}
|
||||
|
16
src/cmd/vbackup/nfs3srv.h
Normal file
16
src/cmd/vbackup/nfs3srv.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
void fsgetroot(Nfs3Handle*);
|
||||
Nfs3Status fsgetattr(SunAuthUnix*, Nfs3Handle*, Nfs3Attr*);
|
||||
Nfs3Status fslookup(SunAuthUnix*, Nfs3Handle*, char*, Nfs3Handle*);
|
||||
Nfs3Status fsaccess(SunAuthUnix*, Nfs3Handle*, u32int, u32int*, Nfs3Attr*);
|
||||
Nfs3Status fsreadlink(SunAuthUnix*, Nfs3Handle*, char**);
|
||||
Nfs3Status fsreadfile(SunAuthUnix*, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);
|
||||
Nfs3Status fsreaddir(SunAuthUnix*, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);
|
||||
|
||||
extern void nfs3proc(void*);
|
||||
extern void mount3proc(void*);
|
||||
|
||||
enum
|
||||
{
|
||||
MaxDataSize = 8192,
|
||||
};
|
||||
|
64
src/cmd/vbackup/queue.c
Normal file
64
src/cmd/vbackup/queue.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include <venti.h>
|
||||
#include <diskfs.h>
|
||||
#include "queue.h"
|
||||
|
||||
Queue*
|
||||
qalloc(void)
|
||||
{
|
||||
Queue *q;
|
||||
|
||||
q = vtmallocz(sizeof(Queue));
|
||||
q->r.l = &q->lk;
|
||||
return q;
|
||||
}
|
||||
|
||||
Block*
|
||||
qread(Queue *q, u32int *pbno)
|
||||
{
|
||||
Block *db;
|
||||
u32int bno;
|
||||
|
||||
qlock(&q->lk);
|
||||
while(q->nel == 0 && !q->closed)
|
||||
rsleep(&q->r);
|
||||
if(q->nel == 0 && q->closed){
|
||||
qunlock(&q->lk);
|
||||
return nil;
|
||||
}
|
||||
db = q->el[q->ri].db;
|
||||
bno = q->el[q->ri].bno;
|
||||
if(++q->ri == MAXQ)
|
||||
q->ri = 0;
|
||||
if(q->nel-- == MAXQ/2)
|
||||
rwakeup(&q->r);
|
||||
qunlock(&q->lk);
|
||||
*pbno = bno;
|
||||
return db;
|
||||
}
|
||||
|
||||
void
|
||||
qwrite(Queue *q, Block *db, u32int bno)
|
||||
{
|
||||
qlock(&q->lk);
|
||||
while(q->nel == MAXQ)
|
||||
rsleep(&q->r);
|
||||
q->el[q->wi].db = db;
|
||||
q->el[q->wi].bno = bno;
|
||||
if(++q->wi == MAXQ)
|
||||
q->wi = 0;
|
||||
if(q->nel++ == MAXQ/2)
|
||||
rwakeup(&q->r);
|
||||
qunlock(&q->lk);
|
||||
}
|
||||
|
||||
void
|
||||
qclose(Queue *q)
|
||||
{
|
||||
qlock(&q->lk);
|
||||
q->closed = 1;
|
||||
rwakeup(&q->r);
|
||||
qunlock(&q->lk);
|
||||
}
|
22
src/cmd/vbackup/queue.h
Normal file
22
src/cmd/vbackup/queue.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
enum
|
||||
{
|
||||
MAXQ = 256,
|
||||
};
|
||||
|
||||
typedef struct Queue Queue;
|
||||
struct Queue
|
||||
{
|
||||
struct {
|
||||
Block *db;
|
||||
u32int bno;
|
||||
} el[MAXQ];
|
||||
int ri, wi, nel, closed;
|
||||
|
||||
QLock lk;
|
||||
Rendez r;
|
||||
};
|
||||
|
||||
Queue *qalloc(void);
|
||||
void qclose(Queue*);
|
||||
Block *qread(Queue*, u32int*);
|
||||
void qwrite(Queue*, Block*, u32int);
|
23
src/cmd/vbackup/util.c
Normal file
23
src/cmd/vbackup/util.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <diskfs.h>
|
||||
|
||||
void*
|
||||
emalloc(ulong n)
|
||||
{
|
||||
void *v;
|
||||
|
||||
v = mallocz(n, 1);
|
||||
if(v == nil)
|
||||
abort();
|
||||
return v;
|
||||
}
|
||||
|
||||
void*
|
||||
erealloc(void *v, ulong n)
|
||||
{
|
||||
v = realloc(v, n);
|
||||
if(v == nil)
|
||||
abort();
|
||||
return v;
|
||||
}
|
524
src/cmd/vbackup/vbackup.c
Normal file
524
src/cmd/vbackup/vbackup.c
Normal file
|
@ -0,0 +1,524 @@
|
|||
/*
|
||||
* vbackup [-Dnv] fspartition [score]
|
||||
*
|
||||
* Copy a file system to a disk image stored on Venti.
|
||||
* Prints a vnfs config line for the copied image.
|
||||
*
|
||||
* -D print debugging
|
||||
* -m set mount name
|
||||
* -n nop -- don't actually write blocks
|
||||
* -s print status updates
|
||||
* -v print debugging trace
|
||||
* -w write parallelism
|
||||
*
|
||||
* If score is given on the command line, it should be the
|
||||
* score from a previous vbackup on this fspartition.
|
||||
* In this mode, only the new blocks are stored to Venti.
|
||||
* The result is still a complete image, but requires many
|
||||
* fewer Venti writes in the common case.
|
||||
*
|
||||
* This program is structured as three processes connected
|
||||
* by buffered queues:
|
||||
*
|
||||
* fsysproc | cmpproc | ventiproc
|
||||
*
|
||||
* Fsysproc reads the disk and queues the blocks.
|
||||
* Cmpproc compares the blocks against the SHA1 hashes
|
||||
* in the old image, if any. It discards the unchanged blocks
|
||||
* and queues the changed ones. Ventiproc writes blocks to Venti.
|
||||
*
|
||||
* There is a fourth proc, statusproc, which prints status
|
||||
* updates about how the various procs are progressing.
|
||||
*/
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <thread.h>
|
||||
#include <libsec.h>
|
||||
#include <venti.h>
|
||||
#include <diskfs.h>
|
||||
#include "queue.h"
|
||||
|
||||
enum
|
||||
{
|
||||
STACK = 8192,
|
||||
};
|
||||
|
||||
typedef struct WriteReq WriteReq;
|
||||
struct WriteReq
|
||||
{
|
||||
Packet *p;
|
||||
uint type;
|
||||
};
|
||||
|
||||
Biobuf bscores; /* biobuf filled with block scores */
|
||||
int debug; /* debugging flag (not used) */
|
||||
Disk* disk; /* disk being backed up */
|
||||
RWLock endlk; /* silly synchonization */
|
||||
int errors; /* are we exiting with an error status? */
|
||||
int fsscanblock; /* last block scanned */
|
||||
Fsys* fsys; /* file system being backed up */
|
||||
int nchange; /* number of changed blocks */
|
||||
int nop; /* don't actually send blocks to venti */
|
||||
int nwrite; /* number of write-behind threads */
|
||||
Queue* qcmp; /* queue fsys->cmp */
|
||||
Queue* qventi; /* queue cmp->venti */
|
||||
int statustime; /* print status every _ seconds */
|
||||
int verbose; /* print extra stuff */
|
||||
VtFile* vfile; /* venti file being written */
|
||||
Channel* writechan; /* chan(WriteReq) */
|
||||
VtConn* z; /* connection to venti */
|
||||
VtCache* zcache; /* cache of venti blocks */
|
||||
uchar* zero; /* blocksize zero bytes */
|
||||
|
||||
extern int ncopy, nread, nwrite; /* hidden in libventi */
|
||||
|
||||
void cmpproc(void*);
|
||||
void fsysproc(void*);
|
||||
void statusproc(void*);
|
||||
void ventiproc(void*);
|
||||
int timefmt(Fmt*);
|
||||
char* mountplace(char *dev);
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: vbackup [-DVnv] [-m mtpt] [-s secs] [-w n] disk [score]\n");
|
||||
threadexitsall("usage");
|
||||
}
|
||||
|
||||
void
|
||||
threadmain(int argc, char **argv)
|
||||
{
|
||||
char *pref, *mountname;
|
||||
uchar score[VtScoreSize], prev[VtScoreSize];
|
||||
int i, fd, csize;
|
||||
vlong bsize;
|
||||
Tm tm;
|
||||
VtEntry e;
|
||||
VtBlock *b;
|
||||
VtCache *c;
|
||||
VtRoot root;
|
||||
char *tmp, *tmpnam;
|
||||
|
||||
fmtinstall('F', vtfcallfmt);
|
||||
fmtinstall('H', encodefmt);
|
||||
fmtinstall('T', timefmt);
|
||||
fmtinstall('V', vtscorefmt);
|
||||
|
||||
mountname = sysname();
|
||||
ARGBEGIN{
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
case 'D':
|
||||
debug++;
|
||||
break;
|
||||
case 'V':
|
||||
chattyventi = 1;
|
||||
break;
|
||||
case 'm':
|
||||
mountname = EARGF(usage());
|
||||
break;
|
||||
case 'n':
|
||||
nop = 1;
|
||||
break;
|
||||
case 's':
|
||||
statustime = atoi(EARGF(usage()));
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'w':
|
||||
nwrite = atoi(EARGF(usage()));
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if(argc != 1 && argc != 2)
|
||||
usage();
|
||||
|
||||
if(statustime)
|
||||
print("# %T vbackup %s %s\n", argv[0], argc>=2 ? argv[1] : "");
|
||||
/*
|
||||
* open fs
|
||||
*/
|
||||
if((disk = diskopenfile(argv[0])) == nil)
|
||||
sysfatal("diskopen: %r");
|
||||
if((disk = diskcache(disk, 16384, 2*MAXQ+16)) == nil)
|
||||
sysfatal("diskcache: %r");
|
||||
if((fsys = fsysopen(disk)) == nil)
|
||||
sysfatal("ffsopen: %r");
|
||||
|
||||
/*
|
||||
* connect to venti
|
||||
*/
|
||||
if((z = vtdial(nil)) == nil)
|
||||
sysfatal("vtdial: %r");
|
||||
if(vtconnect(z) < 0)
|
||||
sysfatal("vtconnect: %r");
|
||||
|
||||
/*
|
||||
* set up venti block cache
|
||||
*/
|
||||
zero = vtmallocz(fsys->blocksize);
|
||||
bsize = fsys->blocksize;
|
||||
csize = 50; /* plenty; could probably do with 5 */
|
||||
|
||||
if(verbose)
|
||||
fprint(2, "cache %d blocks\n", csize);
|
||||
c = vtcachealloc(z, bsize, csize, VtORDWR);
|
||||
zcache = c;
|
||||
|
||||
/*
|
||||
* parse starting score
|
||||
*/
|
||||
memset(prev, 0, sizeof prev);
|
||||
if(argc == 1){
|
||||
vfile = vtfilecreateroot(c, (fsys->blocksize/VtScoreSize)*VtScoreSize,
|
||||
fsys->blocksize, VtDataType);
|
||||
if(vfile == nil)
|
||||
sysfatal("vtfilecreateroot: %r");
|
||||
vtfilelock(vfile, VtORDWR);
|
||||
if(vtfilewrite(vfile, zero, 1, bsize*fsys->nblock-1) != 1)
|
||||
sysfatal("vtfilewrite: %r");
|
||||
if(vtfileflush(vfile) < 0)
|
||||
sysfatal("vtfileflush: %r");
|
||||
}else{
|
||||
if(vtparsescore(argv[1], &pref, score) < 0)
|
||||
sysfatal("bad score: %r");
|
||||
if(pref!=nil && strcmp(pref, fsys->type) != 0)
|
||||
sysfatal("score is %s but fsys is %s", pref, fsys->type);
|
||||
b = vtcacheglobal(c, score, VtRootType);
|
||||
if(b){
|
||||
if(vtrootunpack(&root, b->data) < 0)
|
||||
sysfatal("bad root: %r");
|
||||
if(strcmp(root.type, fsys->type) != 0)
|
||||
sysfatal("root is %s but fsys is %s", root.type, fsys->type);
|
||||
memmove(prev, score, VtScoreSize);
|
||||
memmove(score, root.score, VtScoreSize);
|
||||
vtblockput(b);
|
||||
}
|
||||
b = vtcacheglobal(c, score, VtDirType);
|
||||
if(b == nil)
|
||||
sysfatal("vtcacheglobal %V: %r", score);
|
||||
if(vtentryunpack(&e, b->data, 0) < 0)
|
||||
sysfatal("%V: vtentryunpack failed", score);
|
||||
if(verbose)
|
||||
fprint(2, "entry: size %llud psize %d dsize %d\n",
|
||||
e.size, e.psize, e.dsize);
|
||||
vtblockput(b);
|
||||
if((vfile = vtfileopenroot(c, &e)) == nil)
|
||||
sysfatal("vtfileopenroot: %r");
|
||||
vtfilelock(vfile, VtORDWR);
|
||||
if(e.dsize != bsize)
|
||||
sysfatal("file system block sizes don't match %d %lld", e.dsize, bsize);
|
||||
if(e.size != fsys->nblock*bsize)
|
||||
sysfatal("file system block counts don't match %lld %lld", e.size, fsys->nblock*bsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* write scores of blocks into temporary file
|
||||
*/
|
||||
if((tmp = getenv("TMP")) != nil){
|
||||
/* okay, good */
|
||||
}else if(access("/var/tmp", 0) >= 0)
|
||||
tmp = "/var/tmp";
|
||||
else
|
||||
tmp = "/tmp";
|
||||
tmpnam = smprint("%s/vbackup.XXXXXX", tmp);
|
||||
if(tmpnam == nil)
|
||||
sysfatal("smprint: %r");
|
||||
|
||||
if((fd = opentemp(tmpnam)) < 0)
|
||||
sysfatal("opentemp %s: %r", tmpnam);
|
||||
if(statustime)
|
||||
print("# %T reading scores into %s\n", tmpnam);
|
||||
if(verbose)
|
||||
fprint(2, "read scores into %s...\n", tmpnam);
|
||||
|
||||
Binit(&bscores, fd, OWRITE);
|
||||
for(i=0; i<fsys->nblock; i++){
|
||||
if(vtfileblockscore(vfile, i, score) < 0)
|
||||
sysfatal("vtfileblockhash %d: %r", i);
|
||||
if(Bwrite(&bscores, score, VtScoreSize) != VtScoreSize)
|
||||
sysfatal("Bwrite: %r");
|
||||
}
|
||||
Bterm(&bscores);
|
||||
vtfileunlock(vfile);
|
||||
|
||||
/*
|
||||
* prep scores for rereading
|
||||
*/
|
||||
seek(fd, 0, 0);
|
||||
Binit(&bscores, fd, OREAD);
|
||||
|
||||
/*
|
||||
* start the main processes
|
||||
*/
|
||||
if(statustime)
|
||||
print("# %T starting procs\n");
|
||||
qcmp = qalloc();
|
||||
qventi = qalloc();
|
||||
|
||||
rlock(&endlk);
|
||||
proccreate(fsysproc, nil, STACK);
|
||||
rlock(&endlk);
|
||||
proccreate(ventiproc, nil, STACK);
|
||||
rlock(&endlk);
|
||||
proccreate(cmpproc, nil, STACK);
|
||||
if(statustime){
|
||||
rlock(&endlk);
|
||||
proccreate(statusproc, nil, STACK);
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for processes to finish
|
||||
*/
|
||||
wlock(&endlk);
|
||||
|
||||
if(statustime)
|
||||
print("# %T procs exited: %d blocks changed, %d read, %d written, %d copied\n",
|
||||
nchange, nread, nwrite, ncopy);
|
||||
|
||||
/*
|
||||
* prepare root block
|
||||
*/
|
||||
vtfilelock(vfile, -1);
|
||||
if(vtfileflush(vfile) < 0)
|
||||
sysfatal("vtfileflush: %r");
|
||||
if(vtfilegetentry(vfile, &e) < 0)
|
||||
sysfatal("vtfilegetentry: %r");
|
||||
|
||||
b = vtcacheallocblock(c, VtDirType);
|
||||
if(b == nil)
|
||||
sysfatal("vtcacheallocblock: %r");
|
||||
vtentrypack(&e, b->data, 0);
|
||||
if(vtblockwrite(b) < 0)
|
||||
sysfatal("vtblockwrite: %r");
|
||||
|
||||
memset(&root, 0, sizeof root);
|
||||
strecpy(root.name, root.name+sizeof root.name, argv[0]);
|
||||
strecpy(root.type, root.type+sizeof root.type, fsys->type);
|
||||
memmove(root.score, b->score, VtScoreSize);
|
||||
root.blocksize = fsys->blocksize;
|
||||
memmove(root.prev, prev, VtScoreSize);
|
||||
vtblockput(b);
|
||||
|
||||
b = vtcacheallocblock(c, VtRootType);
|
||||
if(b == nil)
|
||||
sysfatal("vtcacheallocblock: %r");
|
||||
vtrootpack(&root, b->data);
|
||||
if(vtblockwrite(b) < 0)
|
||||
sysfatal("vtblockwrite: %r");
|
||||
|
||||
tm = *localtime(time(0));
|
||||
tm.year += 1900;
|
||||
tm.mon++;
|
||||
print("mount /%s/%d/%02d%02d%s %s:%V %d/%02d%02d/%02d%02d\n",
|
||||
mountname, tm.year, tm.mon, tm.mday,
|
||||
mountplace(argv[0]),
|
||||
root.type, b->score,
|
||||
tm.year, tm.mon, tm.mday, tm.hour, tm.min);
|
||||
print("# %T %s %s:%V\n", argv[0], root.type, b->score);
|
||||
if(statustime)
|
||||
print("# %T venti sync\n");
|
||||
vtblockput(b);
|
||||
if(vtsync(z) < 0)
|
||||
sysfatal("vtsync: %r");
|
||||
if(statustime)
|
||||
print("# %T synced\n");
|
||||
threadexitsall(nil);
|
||||
}
|
||||
|
||||
void
|
||||
fsysproc(void *dummy)
|
||||
{
|
||||
u32int i;
|
||||
Block *db;
|
||||
|
||||
USED(dummy);
|
||||
|
||||
for(i=0; i<fsys->nblock; i++){
|
||||
fsscanblock = i;
|
||||
if((db = fsysreadblock(fsys, i)) != nil)
|
||||
qwrite(qcmp, db, i);
|
||||
}
|
||||
fsscanblock = i;
|
||||
qclose(qcmp);
|
||||
|
||||
print("# %T fsys proc exiting\n");
|
||||
runlock(&endlk);
|
||||
}
|
||||
|
||||
void
|
||||
cmpproc(void *dummy)
|
||||
{
|
||||
uchar *data;
|
||||
Block *db;
|
||||
u32int bno, bsize;
|
||||
uchar score[VtScoreSize];
|
||||
uchar score1[VtScoreSize];
|
||||
|
||||
USED(dummy);
|
||||
|
||||
bsize = fsys->blocksize;
|
||||
while((db = qread(qcmp, &bno)) != nil){
|
||||
data = db->data;
|
||||
sha1(data, vtzerotruncate(VtDataType, data, bsize), score, nil);
|
||||
if(Bseek(&bscores, (vlong)bno*VtScoreSize, 0) < 0)
|
||||
sysfatal("cmpproc Bseek: %r");
|
||||
if(Bread(&bscores, score1, VtScoreSize) != VtScoreSize)
|
||||
sysfatal("cmpproc Bread: %r");
|
||||
if(memcmp(score, score1, VtScoreSize) != 0){
|
||||
nchange++;
|
||||
if(verbose)
|
||||
print("# block %ud: old %V new %V\n", bno, score1, score);
|
||||
qwrite(qventi, db, bno);
|
||||
}else
|
||||
blockput(db);
|
||||
}
|
||||
qclose(qventi);
|
||||
runlock(&endlk);
|
||||
}
|
||||
|
||||
void
|
||||
writethread(void *v)
|
||||
{
|
||||
WriteReq wr;
|
||||
uchar score[VtScoreSize];
|
||||
|
||||
USED(v);
|
||||
|
||||
while(recv(writechan, &wr) == 1){
|
||||
if(wr.p == nil)
|
||||
break;
|
||||
if(vtwritepacket(z, score, wr.type, wr.p) < 0)
|
||||
sysfatal("vtwritepacket: %r");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
myvtwrite(VtConn *z, uchar score[VtScoreSize], uint type, uchar *buf, int n)
|
||||
{
|
||||
WriteReq wr;
|
||||
|
||||
if(nwrite == 0)
|
||||
return vtwrite(z, score, type, buf, n);
|
||||
|
||||
wr.p = packetalloc();
|
||||
packetappend(wr.p, buf, n);
|
||||
packetsha1(wr.p, score);
|
||||
wr.type = type;
|
||||
send(writechan, &wr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ventiproc(void *dummy)
|
||||
{
|
||||
int i;
|
||||
Block *db;
|
||||
u32int bno;
|
||||
u64int bsize;
|
||||
|
||||
USED(dummy);
|
||||
|
||||
proccreate(vtsendproc, z, STACK);
|
||||
proccreate(vtrecvproc, z, STACK);
|
||||
|
||||
writechan = chancreate(sizeof(WriteReq), 0);
|
||||
for(i=0; i<nwrite; i++)
|
||||
threadcreate(writethread, nil, STACK);
|
||||
vtcachesetwrite(zcache, myvtwrite);
|
||||
|
||||
bsize = fsys->blocksize;
|
||||
vtfilelock(vfile, -1);
|
||||
while((db = qread(qventi, &bno)) != nil){
|
||||
if(nop){
|
||||
blockput(db);
|
||||
continue;
|
||||
}
|
||||
if(vtfilewrite(vfile, db->data, bsize, bno*bsize) != bsize)
|
||||
sysfatal("ventiproc vtfilewrite: %r");
|
||||
if(vtfileflushbefore(vfile, (bno+1)*bsize) < 0)
|
||||
sysfatal("ventiproc vtfileflushbefore: %r");
|
||||
blockput(db);
|
||||
}
|
||||
vtfileunlock(vfile);
|
||||
vtcachesetwrite(zcache, nil);
|
||||
for(i=0; i<nwrite; i++)
|
||||
send(writechan, nil);
|
||||
runlock(&endlk);
|
||||
}
|
||||
|
||||
static int
|
||||
percent(u32int a, u32int b)
|
||||
{
|
||||
return (vlong)a*100/b;
|
||||
}
|
||||
|
||||
void
|
||||
statusproc(void *dummy)
|
||||
{
|
||||
int n;
|
||||
USED(dummy);
|
||||
|
||||
for(n=0;;n++){
|
||||
sleep(1000);
|
||||
if(qcmp->closed && qcmp->nel==0 && qventi->closed && qventi->nel==0)
|
||||
break;
|
||||
if(n < statustime)
|
||||
continue;
|
||||
n = 0;
|
||||
print("# %T fsscan=%d%% cmpq=%d%% ventiq=%d%%\n",
|
||||
percent(fsscanblock, fsys->nblock),
|
||||
percent(qcmp->nel, MAXQ),
|
||||
percent(qventi->nel, MAXQ));
|
||||
}
|
||||
runlock(&endlk);
|
||||
}
|
||||
|
||||
int
|
||||
timefmt(Fmt *fmt)
|
||||
{
|
||||
vlong ns;
|
||||
Tm tm;
|
||||
ns = nsec();
|
||||
tm = *localtime(time(0));
|
||||
return fmtprint(fmt, "%04d/%02d%02d %02d:%02d:%02d.%03d",
|
||||
tm.year+1900, tm.mon+1, tm.mday, tm.hour, tm.min, tm.sec,
|
||||
(int)(ns%1000000000)/1000000);
|
||||
}
|
||||
|
||||
char*
|
||||
mountplace(char *dev)
|
||||
{
|
||||
char *cmd, *q;
|
||||
int p[2], fd[3], n;
|
||||
char buf[100];
|
||||
|
||||
if(pipe(p) < 0)
|
||||
sysfatal("pipe: %r");
|
||||
|
||||
fd[0] = -1;
|
||||
fd[1] = p[1];
|
||||
fd[2] = -1;
|
||||
cmd = smprint("mount | awk '$1==\"%s\" && $2 == \"on\" {print $3}'", dev);
|
||||
if(threadspawnl(fd, "sh", "sh", "-c", cmd, nil) < 0)
|
||||
sysfatal("exec mount|awk (to find mtpt of %s): %r", dev);
|
||||
/* threadspawnl closed p[1] */
|
||||
n = readn(p[0], buf, sizeof buf-1);
|
||||
close(p[0]);
|
||||
if(n <= 0)
|
||||
return dev;
|
||||
buf[n] = 0;
|
||||
if((q = strchr(buf, '\n')) == nil)
|
||||
return dev;
|
||||
*q = 0;
|
||||
q = buf+strlen(buf);
|
||||
if(q>buf && *(q-1) == '/')
|
||||
*--q = 0;
|
||||
return strdup(buf);
|
||||
}
|
||||
|
76
src/cmd/vbackup/vcat.c
Normal file
76
src/cmd/vbackup/vcat.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <venti.h>
|
||||
#include <diskfs.h>
|
||||
#include <thread.h>
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: vcat [-z] score >diskfile\n");
|
||||
threadexitsall("usage");
|
||||
}
|
||||
|
||||
void
|
||||
threadmain(int argc, char **argv)
|
||||
{
|
||||
extern int nfilereads;
|
||||
char *pref;
|
||||
int zerotoo;
|
||||
uchar score[VtScoreSize];
|
||||
u8int *zero;
|
||||
u32int i;
|
||||
u32int n;
|
||||
Block *b;
|
||||
Disk *disk;
|
||||
Fsys *fsys;
|
||||
VtCache *c;
|
||||
VtConn *z;
|
||||
|
||||
zerotoo = 0;
|
||||
ARGBEGIN{
|
||||
case 'z':
|
||||
zerotoo = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc != 1)
|
||||
usage();
|
||||
|
||||
fmtinstall('V', vtscorefmt);
|
||||
|
||||
if(vtparsescore(argv[0], &pref, score) < 0)
|
||||
sysfatal("bad score '%s'", argv[0]);
|
||||
if((z = vtdial(nil)) == nil)
|
||||
sysfatal("vtdial: %r");
|
||||
if(vtconnect(z) < 0)
|
||||
sysfatal("vtconnect: %r");
|
||||
if((c = vtcachealloc(z, 16384, 32, VtOREAD)) == nil)
|
||||
sysfatal("vtcache: %r");
|
||||
if((disk = diskopenventi(c, score)) == nil)
|
||||
sysfatal("diskopenventi: %r");
|
||||
if((fsys = fsysopen(disk)) == nil)
|
||||
sysfatal("ffsopen: %r");
|
||||
|
||||
zero = emalloc(fsys->blocksize);
|
||||
fprint(2, "%d blocks total\n", fsys->nblock);
|
||||
n = 0;
|
||||
for(i=0; i<fsys->nblock; i++){
|
||||
if((b = fsysreadblock(fsys, i)) != nil){
|
||||
if(pwrite(1, b->data, fsys->blocksize,
|
||||
(u64int)fsys->blocksize*i) != fsys->blocksize)
|
||||
fprint(2, "error writing block %lud: %r\n", i);
|
||||
n++;
|
||||
blockput(b);
|
||||
}else if(zerotoo)
|
||||
if(pwrite(1, zero, fsys->blocksize,
|
||||
(u64int)fsys->blocksize*i) != fsys->blocksize)
|
||||
fprint(2, "error writing block %lud: %r\n", i);
|
||||
if(b == nil && i < 2)
|
||||
sysfatal("block %d not in use", i);
|
||||
}
|
||||
fprint(2, "%d blocks in use, %d file reads\n", n, nfilereads);
|
||||
threadexitsall(nil);
|
||||
}
|
0
src/cmd/vbackup/vftp.c
Normal file
0
src/cmd/vbackup/vftp.c
Normal file
77
src/cmd/vbackup/vmount.c
Normal file
77
src/cmd/vbackup/vmount.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include <u.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <libc.h>
|
||||
#include "mountnfs.h"
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: vmount [-v] [-h handle] address mountpoint\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
int handlelen = 20;
|
||||
uchar handle[64] = {
|
||||
/* SHA1("/") */
|
||||
0x42, 0x09, 0x9B, 0x4A, 0xF0, 0x21, 0xE5, 0x3F, 0xD8, 0xFD,
|
||||
0x4E, 0x05, 0x6C, 0x25, 0x68, 0xD7, 0xC2, 0xE3, 0xFF, 0xA8,
|
||||
};
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *p, *net, *unx;
|
||||
u32int host;
|
||||
int n, port, proto, verbose;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
verbose = 0;
|
||||
ARGBEGIN{
|
||||
case 'h':
|
||||
p = EARGF(usage());
|
||||
n = strlen(p);
|
||||
if(n%2)
|
||||
sysfatal("bad handle '%s'", p);
|
||||
if(n > 2*sizeof handle)
|
||||
sysfatal("handle too long '%s'", p);
|
||||
handlelen = n/2;
|
||||
if(dec16(handle, n/2, p, n) != n/2)
|
||||
sysfatal("bad hex in handle '%s'", p);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc != 2)
|
||||
usage();
|
||||
|
||||
p = p9netmkaddr(argv[0], "udp", "nfs");
|
||||
if(p9dialparse(strdup(p), &net, &unx, &host, &port) < 0)
|
||||
sysfatal("bad address '%s'", p);
|
||||
|
||||
if(verbose)
|
||||
print("nfs server is net=%s addr=%d.%d.%d.%d port=%d\n",
|
||||
net, host&0xFF, (host>>8)&0xFF, (host>>16)&0xFF, host>>24, port);
|
||||
|
||||
proto = 0;
|
||||
if(strcmp(net, "tcp") == 0)
|
||||
proto = SOCK_STREAM;
|
||||
else if(strcmp(net, "udp") == 0)
|
||||
proto = SOCK_DGRAM;
|
||||
else
|
||||
sysfatal("bad proto %s: can only handle tcp and udp", net);
|
||||
|
||||
memset(&sa, 0, sizeof sa);
|
||||
memmove(&sa.sin_addr, &host, 4);
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
mountnfs(proto, &sa, handle, handlelen, argv[1]);
|
||||
exits(0);
|
||||
}
|
19
src/cmd/vbackup/vmount.rc
Executable file
19
src/cmd/vbackup/vmount.rc
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/usr/local/plan9/bin/rc
|
||||
|
||||
if(! ~ $#* 2){
|
||||
echo 'usage: vmount server mtpt' >[1=2]
|
||||
exit usage
|
||||
}
|
||||
|
||||
server=$1
|
||||
mtpt=$2
|
||||
|
||||
switch(`{uname}){
|
||||
case Linux
|
||||
exec mount -o 'ro,timeo=100,rsize=8192,retrans=5,port=12049,mountport=12049,mountvers=3,nfsvers=3,nolock,soft,intr,udp' \
|
||||
$server:/dump $mtpt
|
||||
case *
|
||||
echo 'cannot mount on' `{uname} >[1=2]
|
||||
exit usage
|
||||
}
|
||||
|
77
src/cmd/vbackup/vmount0.c
Normal file
77
src/cmd/vbackup/vmount0.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include <u.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <libc.h>
|
||||
#include "mountnfs.h"
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: vmount [-v] [-h handle] address mountpoint\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
int handlelen = 20;
|
||||
uchar handle[64] = {
|
||||
/* SHA1("/") */
|
||||
0x42, 0x09, 0x9B, 0x4A, 0xF0, 0x21, 0xE5, 0x3F, 0xD8, 0xFD,
|
||||
0x4E, 0x05, 0x6C, 0x25, 0x68, 0xD7, 0xC2, 0xE3, 0xFF, 0xA8,
|
||||
};
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *p, *net, *unx;
|
||||
u32int host;
|
||||
int n, port, proto, verbose;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
verbose = 0;
|
||||
ARGBEGIN{
|
||||
case 'h':
|
||||
p = EARGF(usage());
|
||||
n = strlen(p);
|
||||
if(n%2)
|
||||
sysfatal("bad handle '%s'", p);
|
||||
if(n > 2*sizeof handle)
|
||||
sysfatal("handle too long '%s'", p);
|
||||
handlelen = n/2;
|
||||
if(dec16(handle, n/2, p, n) != n/2)
|
||||
sysfatal("bad hex in handle '%s'", p);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc != 2)
|
||||
usage();
|
||||
|
||||
p = p9netmkaddr(argv[0], "udp", "nfs");
|
||||
if(p9dialparse(strdup(p), &net, &unx, &host, &port) < 0)
|
||||
sysfatal("bad address '%s'", p);
|
||||
|
||||
if(verbose)
|
||||
print("nfs server is net=%s addr=%d.%d.%d.%d port=%d\n",
|
||||
net, host&0xFF, (host>>8)&0xFF, (host>>16)&0xFF, host>>24, port);
|
||||
|
||||
proto = 0;
|
||||
if(strcmp(net, "tcp") == 0)
|
||||
proto = SOCK_STREAM;
|
||||
else if(strcmp(net, "udp") == 0)
|
||||
proto = SOCK_DGRAM;
|
||||
else
|
||||
sysfatal("bad proto %s: can only handle tcp and udp", net);
|
||||
|
||||
memset(&sa, 0, sizeof sa);
|
||||
memmove(&sa.sin_addr, &host, 4);
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
mountnfs(proto, &sa, handle, handlelen, argv[1]);
|
||||
exits(0);
|
||||
}
|
1273
src/cmd/vbackup/vnfs.c
Normal file
1273
src/cmd/vbackup/vnfs.c
Normal file
File diff suppressed because it is too large
Load diff
109
src/cmd/vbackup/yesterday.rc
Executable file
109
src/cmd/vbackup/yesterday.rc
Executable file
|
@ -0,0 +1,109 @@
|
|||
#!/usr/local/plan9/bin/rc
|
||||
|
||||
path=($path $PLAN9/bin)
|
||||
|
||||
fn usage {
|
||||
echo 'usage: yesterday [-cd] [-[[yy]yy]mm]dd] [-n daysago] file ...' >[1=2]
|
||||
exit 1
|
||||
}
|
||||
|
||||
fn Xcp {
|
||||
echo cp $1 $2
|
||||
cp $1 $2
|
||||
}
|
||||
|
||||
fn Xcarefulcp {
|
||||
if(! cmp -s $1 $2) Xcp $1 $2
|
||||
}
|
||||
|
||||
fn Xdiff {
|
||||
echo diff -c $1 $2
|
||||
diff -c $1 $2
|
||||
}
|
||||
|
||||
fn Xecho {
|
||||
echo $1
|
||||
}
|
||||
|
||||
year=`{date|sed 's/.* //'}
|
||||
copy=Xecho
|
||||
last=()
|
||||
while(! ~ $#* 0 && ~ $1 -* && ! ~ $1 --){
|
||||
switch($1){
|
||||
case -c
|
||||
copy=Xcp
|
||||
shift
|
||||
case -d
|
||||
copy=Xdiff
|
||||
shift
|
||||
case -C
|
||||
copy=Xcarefulcp
|
||||
shift
|
||||
case -n*
|
||||
if(~ $1 -n){
|
||||
if(~ $#* 1)
|
||||
usage
|
||||
shift
|
||||
days=$1
|
||||
}
|
||||
if not
|
||||
days=`{echo $1 | sed 's/^-.//'}
|
||||
last=`{date -r `{perl -e 'print time() - '$days'*60*60*24'} |
|
||||
9 sed -e 's%... (...) (..) ..:..:.. ... (....)%\3/\1\2%' -e 'y/ /0/' -e $smon}
|
||||
shift
|
||||
case -[0-9]
|
||||
mon=`{date|9 sed 's/^....(...).*/\1/' -e $smon}
|
||||
last=$year/$mon ^`{echo $1|sed 's/^-/0/'}
|
||||
shift
|
||||
case -[0-9][0-9]
|
||||
mon=`{date|9 sed 's/^....(...).*/\1/' -e $smon}
|
||||
last=$year/$mon ^`{echo $1|9 sed 's/^-//'}
|
||||
shift
|
||||
case -[0-9][0-9][0-9][0-9]
|
||||
last=$year/ ^ `{echo $1|9 sed 's/^-//'}
|
||||
shift
|
||||
case -[0-9][0-9][0-9][0-9][0-9][0-9]
|
||||
last=`{echo $year|9 sed 's/..$//'} ^ `{echo $1|9 sed 's/^-(..)/\1\//'}
|
||||
shift
|
||||
case -[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]
|
||||
last=`{echo $1|9 sed 's/^-(....)/\1\//'}
|
||||
shift
|
||||
case *
|
||||
usage
|
||||
}
|
||||
}
|
||||
if(! ~ $#* 0 && ~ $1 --)
|
||||
shift
|
||||
|
||||
if(~ $#* 0)
|
||||
usage
|
||||
|
||||
dir=`{pwd}
|
||||
if(! ~ $status ''){
|
||||
echo 'yesterday: can''t find directory' >[1=2]
|
||||
exit 'pwd failed'
|
||||
}
|
||||
|
||||
h=`{hostname}
|
||||
switch($h){
|
||||
case amsterdam
|
||||
xdump=/dump/am
|
||||
case *
|
||||
if(! test -d /dump/$h){
|
||||
echo 'no dumps on '^`{hostname} >[1=2]
|
||||
exit 1
|
||||
}
|
||||
xdump=/dump/$h
|
||||
}
|
||||
|
||||
for(i){
|
||||
xpath=$i
|
||||
if(! ~ $xpath /*)
|
||||
xpath=`{9 cleanname -d `{pwd} $i}
|
||||
dumppath=$xpath
|
||||
if(~ $#last 0)
|
||||
xlast=`{9 ls -t $xdump/$year|sed 1q}
|
||||
if not
|
||||
xlast=$xdump/$last
|
||||
$copy $xlast^$dumppath $xpath
|
||||
}
|
Loading…
Reference in a new issue