Dump-like file system backup for Unix, built on Venti.

This commit is contained in:
rsc 2005-07-13 03:49:41 +00:00
parent 0c98da8bf8
commit 004aa293f3
27 changed files with 4437 additions and 0 deletions

78
man/man1/hist.1 Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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");
}

View 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");
}

View 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");
}

View file

@ -0,0 +1 @@
void mountnfs(int proto, struct sockaddr_in*, uchar*, int, char*);

428
src/cmd/vbackup/nfs3srv.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

77
src/cmd/vbackup/vmount.c Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

109
src/cmd/vbackup/yesterday.rc Executable file
View 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
}