9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] Re: worm problems
@ 2001-12-10  0:48 arisawa
  2001-12-12 12:39 ` Sam Ducksworth
  0 siblings, 1 reply; 3+ messages in thread
From: arisawa @ 2001-12-10  0:48 UTC (permalink / raw)
  To: 9fans

Hello,

For the safe of fs crash, I have written another backup tool.

-WARNING-
You should examine this tool carefully by yourself
before you apply to your system.

Kenji Arisawa
E-mail: arisawa@aichi-u.ac.jp

/* ---------------------- backup.c ------------------------
name: backup

backup utility for Plan9

usage: backup [-vn] -s source -d destination [-l backuplist] [path ...]
options and arguments:
	-v: verbose
	-n: make a new backup, i.e.,
		execute backup even if destination is an empty directory
	-s source: source dir from which backup is made.
	-d destination: diestination dir in which backup is made.
	-l backuplist: path list to be processed. path list must be a path per line.
	path ... : path list to be processed.
function:
	`backup' makes an effort to make mirror of source/path in destination/path
	`backup' tries to preserve the following info.:
	- contents (judged from modification time)
	- modification time
	- permissions
	- owner and group info.
	and `backup' discards files and directories in destination/path
	that are non-existent in source/path
typical usage 1:
	# Let's consider that we (host owner) want to have a backup of 9fs in kfs,
	# then:
	# confirm 9fs and kfs are mounted on /n/9fs and /n/kfs respectively
	# and execute:
	% disk/kfscmd allow
	% backup -s /n/9fs -d /n/kfs /
	% disk/kfscmd disallow
	# note that only at first time we should execute:
	% backup -n -s /n/9fs -d /n/kfs /
typical usage 2:
	# backup my files to kfs
	# confirm 9fs and kfs are mounted on /n/9fs and /n/kfs respectively
	# and then executed
	% backup -s /n/9fs -d /n/kfs $home
backuplist sample:
	# each line is trimmed before evaluation
	# lines starting with `#'  are comments
	# one path per line
	386
	cron
	dist
	rc
	lib
	sys
	lp
	acme
	mail/lib
	adm
	wrap
	usr

Coded by Kenji Arisawa (Kenar)
Bug report to: arisawa@aichi-u.ac.jp
------------------------------------------------------- */

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#define MAXPATH 4096

void usage(void);
char *mkpath2(char *path1, char *path2);
char *mkpath3(char *path1, char *path2, char *path3);
char *strtrim(char *s);
int growto(Dir **dirbuf, int ndirbuf, long n);
int compar(Dir *a, Dir *b);
void backup(char *s);
int check(char *path);
int dirinfo(Dir **dirbuf, char *path);
void cpfile(char *path);
void rmdir(char *path);
int rmfile(char *path);
char *src, *dst;
int vflag;
int nflag;

void usage(void)
{	fprint(2, "usage: backup [-nv] -s source -d destination [-l backuplist] [path ....]\n");
	exits("usage");
}

char *
mkpath2(char *path1, char *path2)
{
	char *p;
	p = malloc(strlen(path1)+strlen(path2)+5);
	if(strlen(path1))
		sprint(p, "%s/%s", path1, path2);
	else
		sprint(p, "%s", path2);
	return p;
}

char *
mkpath3(char *path1, char *path2, char *path3)
{
	char *p;
	p = malloc(strlen(path1)+strlen(path2)+strlen(path3)+5);
	if(strlen(path2))
		sprint(p, "%s/%s/%s", path1, path2, path3);
	else
		sprint(p, "%s/%s", path1, path3);
	return p;
}

char *strtrim(char *s)
{
	char *t;
	while(*s && isspace(*s))
		s++;
	t = s + strlen(s) - 1;
	if(s < t){
		while(*t && isspace(*t)) t--;
		t++;
		*t = 0;
	}
	return s;
}

void main(int argc, char *argv[])
{
	char *file = 0;
	USED(argc);
	ARGBEGIN{
	case 'd':	dst = ARGF(); break;
	case 's':	src = ARGF(); break;
	case 'l':	file = ARGF(); break;
	case 'v':	vflag = 1; break;
	case 'n':	nflag = 1; break;
	default: usage();
	}ARGEND
	if(!src || !dst)
		usage();
	if(!file && !*argv)
		usage();

	/* check src whether it is not empty */
	switch(check(src)){
	case 1:
		fprint(2, "%s is non-existent\n", src);
		exits("error");
	case 2:
		fprint(2, "%s is not a directory\n", src);
		exits("error");
	case 3:
		fprint(2, "%s is empty\n", src);
		exits("error");
	default:
		break;
	}

	/* check dst whether it exits */
	switch(check(dst)){
	case 1:
		fprint(2, "%s is non-existent\n", dst);
		exits("error");
	case 2:
		fprint(2, "%s is not a directory\n", dst);
		exits("error");
	case 3:
		if(!nflag){
			fprint(2, "%s is empty\n", dst);
			exits("error");
		}
	default:
		break;
	}

	/* 	check that whether dst is one of subtree of src.
	*	if it is, we must abort processing	*/
	if(strlen(dst) >= strlen(src) && strncmp(dst,src,strlen(src)) == 0){
		/* then be careful */
		if(dst[strlen(src)] == '/'){
			fprint(2, "%s is a subtree of %s\n", dst, src);
			exits("error");
		}
	}

	if(file){
		Biobuf in;
		char *line;
		char *path;
		char buf[MAXPATH];
		int fd;
		int n;

		fd = open(file, OREAD);
		if(fd<0){
			fprint(2, "balckuplist %s not open\n", file);
			exits("error");
		}
		Binit(&in, fd, OREAD);
		while(line = Brdline(&in,'\n')){/* assign = */
			n = Blinelen(&in);
			strncpy(buf, line, n);
			buf[n] = 0;
			path = strtrim(buf);
			if(path[0] == '#')
				continue;
			if(vflag) print("looking... %s\n", path);
			backup(path);
		}
		Bterm(&in); // note: Bterm close the file
	}
	while(*argv){
		if(vflag) print("looking... %s\n", *argv);
		backup(*argv++);
	}
}

void backup(char *path)
{
	int f;
	int	nsrcdir;
	int	ndstdir;
	Dir *srcdirbuf, *sd, *sdend, *ddend;
	Dir *dstdirbuf, *dd;
	char *newpath;
	char srcpath[MAXPATH];
	char dstpath[MAXPATH];

	if(path[0] == '/') path++;
	snprint(srcpath,sizeof(srcpath),"%s/%s", src, path);
	nsrcdir = dirinfo(&srcdirbuf, srcpath);
	if(nsrcdir < 0){
		/* skip this backup */
		fprint(2, "%s unreadable\n", srcpath);
		return;
	}

	snprint(dstpath,sizeof(dstpath),"%s/%s", dst, path);
	ndstdir = dirinfo(&dstdirbuf, dstpath);
	if(ndstdir < 0){
		/* skip this backup */
		fprint(2, "%s unreadable\n", dstpath);
		free(srcdirbuf);
		return;
	}

	sdend = srcdirbuf + nsrcdir;
	ddend = dstdirbuf + ndstdir;
	/* backup them */
	for(sd = srcdirbuf, dd = dstdirbuf; sd < sdend && dd < ddend; ){
		if(strcmp(sd->name, dd->name) < 0){
			/* create destination */
			newpath = mkpath2(path, sd->name);
			if(vflag) print("creating: %s/%s\n", dst, newpath);
			if(sd->mode & CHDIR){
				char *p;
				/* create the directory and go on */
				p = mkpath2(dst, newpath);
				f = create(dstpath, OREAD, sd->mode);
				if(f < 0)
					fprint(2, "mkdir: can't create %s: %r\n", p);
				close(f);
				free(p);
				backup(newpath);
			}
			else
			{
				cpfile(newpath);
			}
			free(newpath);
			sd++;
		}
		else if(strcmp(sd->name, dd->name) > 0){
			/* remove destination */
			char *p;
			p = mkpath3(dst, path, dd->name);
			if(vflag) print("removing: %s\n", p);
			if(dd->mode & CHDIR)
				rmdir(p);
			else
				rmfile(p);
			free(p);
			dd++;
		}
		else {
			/* both names exist */
			char *target;
			newpath = mkpath2(path, sd->name);
			target = mkpath3(dst, path, sd->name);
			if(sd->mode & CHDIR){
				int err = 0;
				if((dd->mode & CHDIR) == 0){
					/* file. so we remove it and create dir. */
					if(rmfile(target) < 0){
						err = 1;
					}
					f = create(target, OREAD, sd->mode);
					if(f < 0){
						fprint(2, "unable to create %s", target);
						err = 1;
					}
					close(f);
				}
				if(!err) backup(newpath);
			}

			/* check the date */
			if(sd->mtime > dd->mtime){
				if(sd->mode & CHDIR ){
					strcpy(dd->uid, sd->uid);
					strcpy(dd->gid, sd->gid);
					dd->mode = sd->mode;
					dd->mtime = sd->mtime;
					dirwstat(target, dd);
				}
				else{
					/* file */
					if(vflag) print("copying: %s\n", target);
					cpfile(newpath);
				}
			}
			else{
				int cf = 0;
				if(strcmp(sd->uid,dd->uid) != 0){
					/* change owner */;
					if(vflag) print("chown: %s\n", target);
					strcpy(dd->uid, sd->uid);
					cf = 1;
				}
				if(strcmp(sd->gid, dd->gid) != 0){
					/* change group */;
					if(vflag) print("chgrp: %s\n", target);
					strcpy(dd->gid, sd->gid);
					cf = 1;
				}
				if(sd->mode != dd->mode){
					/* change mode */;
					if(vflag) print("chmod: %s\n", target);
					dd->mode = sd->mode;
					cf = 1;
				}
				if(cf)
					dirwstat(target, dd);
			}
			free(newpath);
			free(target);
			sd++;
			dd++;
		}
	}

	if(sd < sdend){
		char *p;
		int f;
		for(; sd < sdend; sd++){
			newpath = mkpath2(path, sd->name);
			p = mkpath3(dst, path, sd->name);
			if(vflag) print("creating: %s\n", p);
			if(sd->mode & CHDIR){
				f = create(p, OREAD, sd->mode);
				if(f < 0)
					fprint(2, "mkdir: can't create %s: %r\n", p);
				close(f);
				backup(newpath);
			}
			else
				cpfile(newpath);
			free(newpath);
			free(p);
		}
	}
	else if(dd < ddend){
		char *p;
		for(; dd < ddend; dd++){
			p = mkpath3(dst, path, dd->name);
			if(vflag) print("removing: %s\n", p);
			if(dd->mode & CHDIR)
				rmdir(p);
			else
				rmfile(p);
			free(p);
		}
	}

	/* free dirbuf */
	free(srcdirbuf);
	free(dstdirbuf);
}

int
check(char *path)
{
	int fd;
	Dir db[1], d;
	fd = open(path, OREAD);
	if(fd == -1)
		return 1; // non existent
	dirfstat(fd, &d);
	if((d.mode & CHDIR) == 0){
		close(fd);
		return 2; // not a directory
	}
	if(dirread(fd, db, sizeof db) == 0){
		close(fd);
		return 3; // empty
	}
	close(fd);
	return 0;
}



/*
*	dirinfo
*
*	return value
*	case -1: not exit
*	case 0: empty
*	others: the numbers of files/directory
*
*	note:
*		free(dirbuf) if return value is not -1
*
*	ref: /sys/src/cmd/ls.c
*/
int dirinfo(Dir **dirbufp, char *path)
{
	int fd;
	int	ndirbuf = 0;
	int	ndir = 0;
	Dir *dirbuf = 0;
	Dir db[50],d;
	int i,n;

	fd = open(path, OREAD);
	if(fd == -1)
		return -1;
	dirfstat(fd, &d);
	while((n=dirread(fd, db, sizeof db)) > 0){
		n /= sizeof(Dir);
		ndirbuf = growto(&dirbuf, ndirbuf, ndir+n);
		for(i=0; i<n; i++)
			memmove(dirbuf+ndir+i, db+i, sizeof(Dir));
		ndir += n;
	}
	close(fd);

	/* sort by name */
	qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(void*, void*))compar);

	*dirbufp = dirbuf;
	return ndir;
}

/*
*	stolen from /sys/src/cmd/ls.c
*/
int
growto(Dir **dirbuf, int ndirbuf, long n)
{
	if(n <= ndirbuf)
		return ndirbuf;
	ndirbuf = n;
	*dirbuf=(Dir *)realloc(*dirbuf, ndirbuf*sizeof(Dir));
	if(*dirbuf == 0){
		fprint(2, "ls: malloc fail\n");
		exits("malloc fail");
	}
	return ndirbuf;
}

int
compar(Dir *a, Dir *b)
{
	return strcmp(a->name, b->name);
}


void cpfile(char *path)
{
	char srcpath[MAXPATH];
	char dstpath[MAXPATH];
	Dir d, d1;
	char buf[4096];
	int n;
	int sfd, dfd;
	if(path[0] == '/') path++;
	snprint(srcpath,sizeof(srcpath),"%s/%s", src, path);
	snprint(dstpath,sizeof(dstpath),"%s/%s", dst, path);
	sfd = open(srcpath, OREAD);
	if(sfd < 0){
		fprint(2, "open error: %s\n", srcpath);
		return;
	}
	dirfstat(sfd, &d);
	remove(dstpath);
	dfd = create(dstpath, OWRITE | OTRUNC, 0777);
	if(dfd < 0){
		fprint(2, "unable to create:  %s\n", dstpath);
		close(sfd);
		return;
	}
	for(;;){
		n = read(sfd, buf, sizeof buf);
		if(n <= 0) break;
		if(write(dfd, buf, n) != n){
			fprint(2, "write error: %s\n", dstpath);
			break;
		}
	}
	dirfstat(dfd, &d1);
	close(sfd);
	close(dfd);

	/* and we copy dirstat info */
	d1.mode = d.mode;
	d1.mtime = d.mtime;
	strcpy(d1.uid, d.uid);
	strcpy(d1.gid, d.gid);
	dirwstat(dstpath, &d1);
}

int
rmfile(char *path){
	int status;
	status = remove(path);
	if(status == -1)
		fprint(2,"%s not removed", path);
	return status;
}

void
rmdir(char *path)
{
	char *name;
	int i, ndir;
	Dir *dirbuf;

	ndir = dirinfo(&dirbuf, path);
	if(ndir < 0){
		fprint(2, "cannot get dir info %s\n", path);
		return;
	}

	for(i=0; i<ndir; i++){
		name = mkpath2(path, dirbuf[i].name);
		if(dirbuf[i].mode & CHDIR)
			rmdir(name);
		else
			rmfile(name);
		free(name);
	}
	rmfile(path);
	free(dirbuf);
}


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [9fans] Re: worm problems
  2001-12-10  0:48 [9fans] Re: worm problems arisawa
@ 2001-12-12 12:39 ` Sam Ducksworth
  0 siblings, 0 replies; 3+ messages in thread
From: Sam Ducksworth @ 2001-12-12 12:39 UTC (permalink / raw)
  To: 9fans

kenji,

thanks, i will give this a try and let you know my results.

--sam

On Mon, 10 Dec 2001 arisawa@ar.aichi-u.ac.jp wrote:

> Hello,
>
> For the safe of fs crash, I have written another backup tool.
>
> -WARNING-
> You should examine this tool carefully by yourself
> before you apply to your system.
>
--sam




^ permalink raw reply	[flat|nested] 3+ messages in thread

* [9fans] Re: worm problems
@ 2001-12-13  1:27 arisawa
  0 siblings, 0 replies; 3+ messages in thread
From: arisawa @ 2001-12-13  1:27 UTC (permalink / raw)
  To: 9fans

Hello

>kenji,
>
>thanks, i will give this a try and let you know my results.
>
>--sam

Latest version is in:
ftp://plan9.aichi-u.ac.jp/cmd/backup/
or
ftp://ar.aichi-u.ac.jp/plan9/cmd/backup/

Some bugs are removed from posted version.
Try the latest verion.

Kenji Arisawa
E-mail: arisawa@aichi-u.ac.jp


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2001-12-13  1:27 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-12-10  0:48 [9fans] Re: worm problems arisawa
2001-12-12 12:39 ` Sam Ducksworth
2001-12-13  1:27 arisawa

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).