Beispiel #1
0
void
createuser(void)
{
	Dir d;
	char file[128], *user;
	int fd;

	user = getuser();
	snprint(file, sizeof file, "/cron/%s", user);
	fd = create(file, OREAD, 0755|DMDIR);
	if(fd < 0)
		fatal("couldn't create %s: %r", file);
	nulldir(&d);
	d.gid = user;
	dirfwstat(fd, &d);
	close(fd);
	snprint(file, sizeof file, "/cron/%s/cron", user);
	fd = create(file, OREAD, 0644);
	if(fd < 0)
		fatal("couldn't create %s: %r", file);
	nulldir(&d);
	d.gid = user;
	dirfwstat(fd, &d);
	close(fd);
}
Beispiel #2
0
/*
 * copies while holding the mail lock,
 * then tries to copy permissions and group ownership
 */
static int
copyData(int ffd, int tfd, MbLock *ml)
{
	Dir *fd, td;
	char buf[BufSize];
	int n;

	for(;;){
		n = read(ffd, buf, BufSize);
		if(n <= 0){
			if(n < 0)
				return 0;
			break;
		}
		if(write(tfd, buf, n) != n)
			return 0;
		mbLockRefresh(ml);
	}
	fd = dirfstat(ffd);
	if(fd != nil){
		nulldir(&td);
		td.mode = fd->mode;
		if(dirfwstat(tfd, &td) >= 0){
			nulldir(&td);
			td.gid = fd->gid;
			dirfwstat(tfd, &td);
		}
	}
	return 1;
}
Beispiel #3
0
static int
cmd9pTwstat(Fcall* f, int, char **argv)
{
	Dir d;
	static uchar buf[DIRMAX];

	memset(&d, 0, sizeof d);
	nulldir(&d);
	d.name = argv[1];
	d.uid = argv[2];
	d.gid = argv[3];
	d.mode = cmd9pStrtoul(argv[4]);
	d.mtime = cmd9pStrtoul(argv[5]);
	d.length = cmd9pStrtoull(argv[6]);

	f->fid = strtol(argv[0], 0, 0);
	f->stat = buf;
	f->nstat = convD2M(&d, buf, sizeof buf);
	if(f->nstat < BIT16SZ){
		vtSetError("Twstat: convD2M failed (internal error)");
		return 0;
	}

	return 1;
}
Beispiel #4
0
static int
vnop_fsync_9p(struct vnop_fsync_args *ap)
{
	node_9p *np;
	dir_9p d;
	int e;

	TRACE();
	if (!vnode_isreg(ap->a_vp))
		return 0;

	np = NTO9P(ap->a_vp);
	nlock_9p(np, NODE_LCK_EXCLUSIVE);
	if (ubc_getsize(ap->a_vp)>0 && !vnode_isnocache(ap->a_vp)) {
		if (ISSET(np->flags, NODE_MMAPPED))
			ubc_msync(np->vp, 0, ubc_getsize(np->vp), NULL, UBC_PUSHDIRTY|UBC_SYNC);
		else
			cluster_push(np->vp, IO_SYNC);
	}
	e = 0;
	/* only sync write fids */
	if (np->openfid[OWRITE].fid!=NOFID || np->openfid[ORDWR].fid!=NOFID) {
		nulldir(&d);
		e = wstat_9p(np->nmp, np->fid, &d);
	}
	nunlock_9p(np);
	return e;
}
Beispiel #5
0
/* write address file, should be append only */
void
writeaddr(char *file, char *addr, int rem, char *listname)
{
	int fd;
	Dir nd;

	fd = open(file, OWRITE);
	if(fd < 0){
		fd = create(file, OWRITE, DMAPPEND|0666);
		if(fd < 0)
			sysfatal("creating address list %s: %r", file);
		nulldir(&nd);
		nd.mode = DMAPPEND|0666;
		dirwstat(file, &nd);
	} else
		seek(fd, 0, 2);
	if(rem)
		fprint(fd, "!%s\n", addr);
	else
		fprint(fd, "%s\n", addr);
	close(fd);

	if(*addr != '#')
		sendnotification(addr, listname, rem);
}
Beispiel #6
0
int
ffcreate(char *name, uint32_t mode, char *uid, char *gid, uint32_t mtime,
	 int length)
{
	int fd, om;
	Dir nd;

	sprint(buf, "%s/%s", mtpt, name);
	om = ORDWR;
	if(mode&DMDIR)
		om = OREAD;
	if((fd = create(buf, om, (mode&DMDIR)|0666)) < 0)
		error("create %s: %r", buf);

	nulldir(&nd);
	nd.mode = mode;
	nd.uid = uid;
	nd.gid = gid;
	nd.mtime = mtime;
	if(length)
		nd.length = length;
	if(dirfwstat(fd, &nd) < 0)	
		error("fwstat %s: %r", buf);

	return fd;
}
Beispiel #7
0
void
sbrollback(Shabuf *sb, vlong offset)
{
	int x;
	vlong o;
	Dir d;
	
	if(!sb->rollback || !sb->r0){
		print("cannot rollback sha\n");
		return;
	}
	if(offset >= sb->offset)
		return;
	o = offset - sb->r0;
	x = o/(4*M);
	if(x >= sb->nhist){
		print("cannot rollback sha\n");
		return;
	}
	sb->state = sb->hist[x];
	sb->offset = sb->r0 + x*4*M;
	assert(sb->offset <= offset);
	
	if(sb->fd > 0){
		nulldir(&d);
		d.length = sb->offset - sb->r0;
		dirfwstat(sb->fd, &d);
	}
}
Beispiel #8
0
static void
wrmeta(int fd, Hdr *hp, int32_t mtime, int mode)		/* update metadata */
{
	Dir nd;

	nulldir(&nd);
	nd.mtime = mtime;
	nd.mode = mode;
	dirfwstat(fd, &nd);
	if (isustar(hp)) {
		nulldir(&nd);
		nd.gid = hp->gname;
		dirfwstat(fd, &nd);
		nulldir(&nd);
		nd.uid = hp->uname;
		dirfwstat(fd, &nd);
	}
}
Beispiel #9
0
static int
nfssetattr(int n, Rpccall *cmd, Rpccall *reply)
{
	Xfid *xf;
	Dir dir, nd;
	Sattr sattr;
	int r;
	uchar *argptr = cmd->args;
	uchar *dataptr = reply->results;

	chat("setattr...");
	if(n <= FHSIZE)
		return garbage(reply, "count too small");
	xf = rpc2xfid(cmd, &dir);
	argptr += FHSIZE;
	argptr += convM2sattr(argptr, &sattr);
	if(argptr != &((uchar *)cmd->args)[n])
		return garbage(reply, "bad count");
	chat("mode=0%lo,u=%ld,g=%ld,size=%ld,atime=%ld,mtime=%ld...",
		sattr.mode, sattr.uid, sattr.gid, sattr.size,
		sattr.atime, sattr.mtime);
	if(xf == 0)
		return error(reply, NFSERR_STALE);
	if(sattr.uid != NOATTR || sattr.gid != NOATTR)
		return error(reply, NFSERR_PERM);
	if(sattr.size == 0){
		if(xf->xp->s != xf->xp->parent->s){
			if(xfauthremove(xf, cmd->user) < 0)
				return error(reply, NFSERR_PERM);
		}else if(dir.length && xfopen(xf, Trunc|Oread|Owrite) < 0)
			return error(reply, NFSERR_PERM);
	}else if(sattr.size != NOATTR)
		return error(reply, NFSERR_PERM);
	r = 0;
	nulldir(&nd);
	if(sattr.mode != NOATTR)
		++r, nd.mode = (dir.mode & ~0777) | (sattr.mode & 0777);
	if(sattr.atime != NOATTR)
		++r, nd.atime = sattr.atime;
	if(sattr.mtime != NOATTR)
		++r, nd.mtime = sattr.mtime;
	chat("sattr.mode=%luo dir.mode=%luo nd.mode=%luo...", sattr.mode, dir.mode, nd.mode);
	if(r){
		r = xfwstat(xf, &nd);
		if(r < 0)
			return error(reply, NFSERR_PERM);
	}
	if(xfstat(xf, &dir) < 0)
		return error(reply, NFSERR_STALE);
	PLONG(NFS_OK);
	dataptr += dir2fattr(cmd->up, &dir, dataptr);
	chat("OK\n");
	return dataptr - (uchar *)reply->results;
}
Beispiel #10
0
int
chgtime(char *name)
{
	Dir sbuf;

	if(access(name, AEXIST) >= 0) {
		nulldir(&sbuf);
		sbuf.mtime = time((long *)0);
		return dirwstat(name, &sbuf);
	}
	return close(create(name, OWRITE, 0666));
}
Beispiel #11
0
void
mkdir(char *name, uint32_t mode, uint32_t mtime, char *uid,
      char *gid)
{
	Dir *d, xd;
	int fd;
	char *p;
	char olderr[256];

	fd = create(name, OREAD, mode);
	if(fd < 0){
		rerrstr(olderr, sizeof(olderr));
		if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){
			free(d);
			warn("can't make directory %q, mode %luo: %s", name, mode, olderr);
			return;
		}
		free(d);
	}
	close(fd);

	d = &xd;
	nulldir(d);
	p = utfrrune(name, L'/');
	if(p)
		p++;
	else
		p = name;
	d->name = p;
	if(uflag){
		d->uid = uid;
		d->gid = gid;
	}
	if(Tflag)
		d->mtime = mtime;
	d->mode = mode;
	if(dirwstat(name, d) < 0)
		warn("can't set modes for %q: %r", name);

	if(uflag||Tflag){
		if((d = dirstat(name)) == nil){
			warn("can't reread modes for %q: %r", name);
			return;
		}
		if(Tflag && d->mtime != mtime)
			warn("%q: time mismatch %lu %lu\n", name, mtime, d->mtime);
		if(uflag && strcmp(uid, d->uid))
			warn("%q: uid mismatch %q %q", name, uid, d->uid);
		if(uflag && strcmp(gid, d->gid))
			warn("%q: gid mismatch %q %q", name, gid, d->gid);
	}
}
Beispiel #12
0
void
main(int argc, char **argv)
{
	Dir d;

	if(argc != 3){
		fprint(2, "usage: trunc file size\n");
		exits("usage");
	}

	nulldir(&d);
	d.length = strtoull(argv[2], 0, 0);
	if(dirwstat(argv[1], &d) < 0)
		sysfatal("dirwstat: %r");
	exits(0);
}
Beispiel #13
0
static int
nfsrename(int n, Rpccall *cmd, Rpccall *reply)
{
	Xfid *xf, *newxf;
	Xfile *xp;
	uchar *fromdir, *todir;
	String fromelem, toelem;
	Dir dir;
	uchar *argptr = cmd->args;
	uchar *dataptr = reply->results;

	chat("rename...");
	if(n <= FHSIZE)
		return garbage(reply, "count too small");
	xf = rpc2xfid(cmd, 0);
	fromdir = argptr;
	argptr += FHSIZE;
	argptr += string2S(argptr, &fromelem);
	todir = argptr;
	argptr += FHSIZE;
	argptr += string2S(argptr, &toelem);
	if(argptr != &((uchar *)cmd->args)[n])
		return garbage(reply, "bad count");
	if(xf == 0)
		return error(reply, NFSERR_STALE);
	xp = xf->xp;
	if(!(xp->qid.type & QTDIR))
		return error(reply, NFSERR_NOTDIR);
	if(memcmp(fromdir, todir, FHSIZE) != 0)
		return error(reply, NFSERR_NXIO);
	newxf = xfwalkcr(Twalk, xf, &fromelem, 0);
	if(newxf == 0)
		return error(reply, NFSERR_NOENT);
	if(xfstat(newxf, &dir) < 0)
		return error(reply, NFSERR_IO);

	if(xp->parent == xp && toelem.s[0] == '#')
		return error(reply, NFSERR_PERM);
	nulldir(&dir);
	dir.name = toelem.s;
	if(xfwstat(newxf, &dir) < 0)
		return error(reply, NFSERR_PERM);
	PLONG(NFS_OK);
	chat("OK\n");
	return dataptr - (uchar *)reply->results;
}
Beispiel #14
0
// writefile writes b to the named file, creating it if needed.
void
writefile(Buf *b, char *file, int exec)
{
	int fd;
	Dir d;
	
	fd = create(file, ORDWR, 0666);
	if(fd < 0)
		fatal("create %s", file);
	if(write(fd, b->p, b->len) != b->len)
		fatal("short write");
	if(exec) {
		nulldir(&d);
		d.mode = 0755;
		dirfwstat(fd, &d);
	}
	close(fd);
}
Beispiel #15
0
static int
vnop_rename_9p(struct vnop_rename_args *ap)
{
	struct componentname *tcnp;
	vnode_t fdvp, tdvp, fvp;
	node_9p *fdnp, *fnp;
	dir_9p d;
	char *s;
	int e;

	TRACE();
	fdvp = ap->a_fdvp;
	tdvp = ap->a_tdvp;
	fvp = ap->a_fvp;
	tcnp = ap->a_tcnp;
	fdnp = NTO9P(fdvp);
	fnp = NTO9P(fvp);

	if (fdvp!=tdvp || NTO9P(fdvp)!=NTO9P(tdvp))
		return ENOTSUP;

	nlock_9p(fdnp, NODE_LCK_EXCLUSIVE);
	nlock_9p(fnp, NODE_LCK_EXCLUSIVE);
	nulldir(&d);
	e = ENOMEM;
	s = malloc_9p(tcnp->cn_namelen+1);
	if (s == NULL)
		goto error;

	bcopy(tcnp->cn_nameptr, s, tcnp->cn_namelen);
	s[tcnp->cn_namelen] = 0;
	d.name = s;
	e = wstat_9p(fnp->nmp, fnp->fid, &d);
	free_9p(s);
	if (e == 0) {
		cache_purge(fvp);
		cache_purge(fdvp);
	}

error:
	nunlock_9p(fnp);
	nunlock_9p(fdnp);
	return e;
}
Beispiel #16
0
void
xcmd(char *arname, int count, char **files)
{
    int fd, f, mode, i;
    Armember *bp;
    Dir dx;

    fd = openar(arname, OREAD, 0);
    Binit(&bar, fd, OREAD);
    Bseek(&bar,seek(fd,0,1), 1);
    i = 0;
    while (bp = getdir(&bar)) {
        if(count == 0 || match(count, files)) {
            mode = strtoul(bp->hdr.mode, 0, 8) & 0777;
            f = create(file, OWRITE, mode);
            if(f < 0) {
                fprint(2, "ar: %s cannot create\n", file);
                skip(&bar, bp->size);
            } else {
                mesg('x', file);
                arcopy(&bar, 0, bp);
                if (write(f, bp->member, bp->size) < 0)
                    wrerr();
                if(oflag) {
                    nulldir(&dx);
                    dx.atime = bp->date;
                    dx.mtime = bp->date;
                    if(dirwstat(file, &dx) < 0)
                        perror(file);
                }
                free(bp->member);
                close(f);
            }
            free(bp);
            if (count && ++i >= count)
                break;
        } else {
            skip(&bar, bp->size);
            free(bp);
        }
    }
    close(fd);
}
Beispiel #17
0
/*
 * move one mail folder to another
 * destination mailbox doesn't exist.
 * the source folder may be a directory or a mailbox,
 * and may be in the same directory as the destination,
 * or a completely different directory.
 */
int
moveBox(char *from, char *to)
{
	Dir *d;
	char *fd, *fe, *td, *te, *fimp;

	splitr(from, '/', &fd, &fe);
	splitr(to, '/', &td, &te);

	/*
	 * in the same directory: try rename
	 */
	d = cdDirstat(mboxDir, from);
	if(d == nil)
		return 0;
	if(strcmp(fd, td) == 0){
		nulldir(d);
		d->name = te;
		if(cdDirwstat(mboxDir, from, d) >= 0){
			fimp = impName(from);
			d->name = impName(te);
			cdDirwstat(mboxDir, fimp, d);
			free(d);
			return 1;
		}
	}

	/*
	 * directory copy is too hard for now
	 */
	if(d->mode & DMDIR)
		return 0;
	free(d);

	return copyBox(from, to, 1);
}
Beispiel #18
0
void
copy(char *from, char *to, int todir)
{
	Dir *dirb, dirt;
	char name[256];
	int fdf, fdt, mode;

	if(todir){
		char *s, *elem;
		elem=s=from;
		while(*s++)
			if(s[-1]=='/')
				elem=s;
		sprint(name, "%s/%s", to, elem);
		to=name;
	}

	if((dirb=dirstat(from))==nil){
		fprint(2,"fcp: can't stat %s: %r\n", from);
		failed = 1;
		return;
	}
	mode = dirb->mode;
	if(mode&DMDIR){
		fprint(2, "fcp: %s is a directory\n", from);
		free(dirb);
		failed = 1;
		return;
	}
	if(samefile(dirb, from, to)){
		free(dirb);
		failed = 1;
		return;
	}
	mode &= 0777;
	fdf=open(from, OREAD);
	if(fdf<0){
		fprint(2, "fcp: can't open %s: %r\n", from);
		free(dirb);
		failed = 1;
		return;
	}
	fdt=create(to, OWRITE, mode);
	if(fdt<0){
		fprint(2, "fcp: can't create %s: %r\n", to);
		close(fdf);
		free(dirb);
		failed = 1;
		return;
	}
	if(copy1(fdf, fdt, from, to)==0 && (xflag || gflag || uflag)){
		nulldir(&dirt);
		if(xflag){
			dirt.mtime = dirb->mtime;
			dirt.mode = dirb->mode;
		}
		if(uflag)
			dirt.uid = dirb->uid;
		if(gflag)
			dirt.gid = dirb->gid;
		if(dirfwstat(fdt, &dirt) < 0)
			fprint(2, "fcp: warning: can't wstat %s: %r\n", to);
	}			
	free(dirb);
	close(fdf);
	close(fdt);
}
Beispiel #19
0
int
replacekey(char *keyfile, char *host, RSApub *hostkey)
{
	int ret;
	char *h, *nkey, *p;
	Biobuf *br, *bw;
	Dir *d, nd;
	RSApub *k;

	ret = -1;
	d = nil;
	nkey = smprint("%s.new", keyfile);
	if(nkey == nil)
		return -1;

	if((br = Bopen(keyfile, OREAD)) == nil)
		goto out;
	if((bw = Bopen(nkey, OWRITE)) == nil){
		Bterm(br);
		goto out;
	}

	while((k = readpublickey(br, &h)) != nil){
		if(match(h, host) != 0)
			Bprint(bw, "%s %d %.10M %.10M\n",
				h, mpsignif(k->n), k->ek, k->n);
		free(h);
		rsapubfree(k);
	}
	Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n),
		hostkey->ek, hostkey->n);
	Bterm(bw);
	Bterm(br);

	d = dirstat(nkey);
	if(d == nil){
		fprint(2, "new key file disappeared?\n");
		goto out;
	}

	p = strrchr(d->name, '.');
	if(p == nil || strcmp(p, ".new") != 0){
		fprint(2, "%s: new key file changed names? %s to %s\n",
			argv0, nkey, d->name);
		goto out;
	}

	*p = '\0';
	nulldir(&nd);
	nd.name = d->name;
	if(remove(keyfile) < 0){
		fprint(2, "%s: error removing %s: %r\n", argv0, keyfile);
		goto out;
	}
	if(dirwstat(nkey, &nd) < 0){
		fprint(2, "%s: error renaming %s to %s: %r\n",
			argv0, nkey, d->name);
		goto out;
	}
	ret = 0;
out:
	free(d);
	free(nkey);
	return ret;
}
Beispiel #20
0
void
threadmain(int argc, char *argv[])
{
	char *mtpt, *s;
	char *secstorepw;
	char err[ERRMAX];
	Dir d;

	rfork(RFNOTEG);

	mtpt = "/mnt";
	extrafactotumdir = 1;
	secstorepw = nil;
	quotefmtinstall();
	fmtinstall('A', attrfmt);
	fmtinstall('H', encodefmt);
	fmtinstall('N', attrnamefmt);

	if(argc == 3 && strcmp(argv[1], "-g") == 0){
		gflag(argv[2]);
		threadexitsall(nil);
	}

	ARGBEGIN{
	default:
		usage();
	case 'D':
		chatty9p++;
		break;
	case 'S':		/* server: read nvram, no prompting for keys */
		askforkeys = 0;
		trysecstore = 0;
		sflag = 1;
		break;
	case 'a':
		authaddr = EARGF(usage());
		break;
	case 'd':
		debug = 1;
		doprivate = 0;
		break;
	case 'g':
		usage();
	case 'k':		/* reinitialize nvram */
		kflag = 1;
		break;
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 'n':
		trysecstore = 0;
		break;
	case 'p':
		doprivate = 0;
		break;
	case 's':
		service = EARGF(usage());
		break;
	case 'u':		/* user: set hostowner */
		uflag = 1;
		break;
	case 'x':
		extrafactotumdir = 0;
		break;
	}ARGEND

	if(argc != 0)
		usage();
	if(doprivate)
		private();

	initcap();

	if(sflag){
		s = getnvramkey(kflag ? NVwrite : NVwriteonerr, &secstorepw);
		if(s == nil)
			fprint(2, "factotum warning: cannot read nvram: %r\n");
		else if(ctlwrite(s) < 0)
			fprint(2, "factotum warning: cannot add nvram key: %r\n");
		if(secstorepw != nil)
			trysecstore = 1;
		if (s != nil) {
			memset(s, 0, strlen(s));
			free(s);
		}
	} else if(uflag)
		promptforhostowner();
	owner = getuser();

	if(trysecstore && havesecstore()){
		while(secstorefetch(secstorepw) < 0){
			rerrstr(err, sizeof err);
			if(strcmp(err, "cancel") == 0)
				break;
			fprint(2, "secstorefetch: %r\n");
			fprint(2, "Enter an empty password to quit.\n");
			free(secstorepw);
			secstorepw = nil; /* just try nvram pw once */
		}
	}
	
	fsinit0();
	threadpostmountsrv(&fs, service, mtpt, MBEFORE);
	if(service){
		nulldir(&d);
		d.mode = 0666;
		s = emalloc(10+strlen(service));
		strcpy(s, "/srv/");
		strcat(s, service);
		if(dirwstat(s, &d) < 0)
			fprint(2, "factotum warning: cannot chmod 666 %s: %r\n", s);
		free(s);
	}
	threadexits(nil);
}
Beispiel #21
0
void
main(int argc, char **argv)
{
	int i, trysecstore;
	char err[ERRMAX], *s;
	Dir d;
	Proto *p;
	char *secstorepw;

	trysecstore = 1;
	secstorepw = nil;

	ARGBEGIN{
	case 'D':
		chatty9p++;
		break;
	case 'S':		/* server: read nvram, no prompting for keys */
		askforkeys = 0;
		trysecstore = 0;
		sflag = 1;
		break;
	case 'a':
		authaddr = EARGF(usage());
		break;
	case 'd':
		debug = 1;
		doprivate = 0;
		break;
	case 'g':		/* get: prompt for key for name and domain */
		gflag = 1;
		break;
	case 'k':		/* reinitialize nvram */
		kflag = 1;
		break;
	case 'm':		/* set default mount point */
		mtpt = EARGF(usage());
		break;
	case 'n':
		trysecstore = 0;
		break;
	case 'p':
		doprivate = 0;
		break;
	case 's':		/* set service name */
		service = EARGF(usage());
		break;
	case 'u':		/* user: set hostowner */
		uflag = 1;
		break;
	default:
		usage();
	}ARGEND

	if(argc != 0 && !gflag)
		usage();
	if(doprivate)
		private();

	initcap();

	quotefmtinstall();
	fmtinstall('A', _attrfmt);
	fmtinstall('N', attrnamefmt);
	fmtinstall('H', encodefmt);

	ring = emalloc(sizeof(*ring));
	notify(notifyf);

	if(gflag){
		if(argc != 1)
			usage();
		askuser(argv[0]);
		exits(nil);
	}

	for(i=0; prototab[i]; i++){
		p = prototab[i];
		if(p->name == nil)
			sysfatal("protocol %d has no name", i);
		if(p->init == nil)
			sysfatal("protocol %s has no init", p->name);
		if(p->write == nil)
			sysfatal("protocol %s has no write", p->name);
		if(p->read == nil)
			sysfatal("protocol %s has no read", p->name);
		if(p->close == nil)
			sysfatal("protocol %s has no close", p->name);
		if(p->keyprompt == nil)
			p->keyprompt = "";
	}

	if(sflag){
		s = getnvramkey(kflag ? NVwrite : NVwriteonerr, &secstorepw);
		if(s == nil)
			fprint(2, "factotum warning: cannot read nvram: %r\n");
		else if(ctlwrite(s, 0) < 0)
			fprint(2, "factotum warning: cannot add nvram key: %r\n");
		if(secstorepw != nil)
			trysecstore = 1;
		if (s != nil) {
			memset(s, 0, strlen(s));
			free(s);
		}
	} else if(uflag)
		promptforhostowner();
	owner = getuser();

	if(trysecstore){
		if(havesecstore() == 1){
			while(secstorefetch(secstorepw) < 0){
				rerrstr(err, sizeof err);
				if(strcmp(err, "cancel") == 0)
					break;
				fprint(2, "factotum: secstorefetch: %r\n");
				fprint(2, "Enter an empty password to quit.\n");
				free(secstorepw);
				secstorepw = nil; /* just try nvram pw once */
			}
		}else{
/*
			rerrstr(err, sizeof err);
			if(*err)
				fprint(2, "factotum: havesecstore: %r\n");
*/
		}
	}

	postmountsrv(&fs, service, mtpt, MBEFORE);
	if(service){
		nulldir(&d);
		d.mode = 0666;
		s = emalloc(10+strlen(service));
		strcpy(s, "/srv/");
		strcat(s, service);
		if(dirwstat(s, &d) < 0)
			fprint(2, "factotum warning: cannot chmod 666 %s: %r\n", s);
		free(s);
	}
	exits(nil);
}
Beispiel #22
0
Datei: mv.c Projekt: elbing/apex
int
mv1(char *from, Dir *dirb, char *todir, char *toelem)
{
	int fdf, fdt, i, j, stat;
	char toname[4096], fromname[4096];
	char *fromdir, *fromelem;
	Dir *dirt, null;

	strncpy(fromname, from, sizeof fromname);
	split(from, &fromdir, &fromelem);
	if(toelem == 0)
		toelem = fromelem;
	i = strlen(toelem);
	if(i==0){
		fprint(2, "mv: null last name element moving %s\n", fromname);
		return -1;
	}
	j = strlen(todir);
	if(i + j + 2 > sizeof toname){
		fprint(2, "mv: path too big (max %d): %s/%s\n",
			sizeof toname, todir, toelem);
		return -1;
	}
	memmove(toname, todir, j);
	toname[j] = '/';
	memmove(toname+j+1, toelem, i);
	toname[i+j+1] = 0;

	if(samefile(fromdir, todir)){
		if(samefile(fromname, toname)){
			fprint(2, "mv: %s and %s are the same\n",
				fromname, toname);
			return -1;
		}

		/* remove target if present */
		dirt = dirstat(toname);
		if(dirt != nil) {
			hardremove(toname);
			free(dirt);
		}

		/* try wstat */
		nulldir(&null);
		null.name = toelem;
		if(dirwstat(fromname, &null) >= 0)
			return 0;
		if(dirb->mode & DMDIR){
			fprint(2, "mv: can't rename directory %s: %r\n",
				fromname);
			return -1;
		}
	}
	/*
	 * Renaming won't work --- must copy
	 */
	if(dirb->mode & DMDIR){
		fprint(2, "mv: %s is a directory, not copied to %s\n",
			fromname, toname);
		return -1;
	}
	fdf = open(fromname, OREAD);
	if(fdf < 0){
		fprint(2, "mv: can't open %s: %r\n", fromname);
		return -1;
	}

	dirt = dirstat(toname);
	if(dirt != nil && (dirt->mode & DMAPPEND))
		hardremove(toname);  /* because create() won't truncate file */
	free(dirt);

	fdt = create(toname, OWRITE, dirb->mode);
	if(fdt < 0){
		fprint(2, "mv: can't create %s: %r\n", toname);
		close(fdf);
		return -1;
	}
	stat = copy1(fdf, fdt, fromname, toname);
	close(fdf);

	if(stat >= 0){
		nulldir(&null);
		null.mtime = dirb->mtime;
		null.mode = dirb->mode;
		dirfwstat(fdt, &null);	/* ignore errors; e.g. user none always fails */
		if(remove(fromname) < 0){
			fprint(2, "mv: can't remove %s: %r\n", fromname);
			stat = -1;
		}
	}
	close(fdt);
	return stat;
}
Beispiel #23
0
void
main(int argc, char **argv)
{ 
	char *f[10], *local, *name, *remote, *s, *t, verb;
	int fd, havedb, havelocal, i, k, n, nf, resolve1, skip;
	int checkedmatch1, checkedmatch2, 
		checkedmatch3, checkedmatch4;
	ulong now;
	Biobuf bin;
	Dir dbd, ld, nd, rd;
	Avlwalk *w;
	Entry *e;

	membogus(argv);
	quotefmtinstall();
	ARGBEGIN{
	case 's':
	case 'c':
		i = ARGC();
		addresolve(i, EARGF(usage()));
		break;
	case 'n':
		donothing = 1;
		verbose = 1;
		break;
	case 'S':
		safeinstall = 0;
		break;
	case 'T':
		timefile = EARGF(usage());
		break;
	case 't':
		tempspool = 0;
		break;
	case 'u':
		douid = 1;
		break;
	case 'v':
		verbose++;
		break;
	default:
		usage();
	}ARGEND

	if(argc < 3)
		usage();

	if(timefile)
		readtimefile();

	lroot = argv[1];
	if(!isdir(lroot))
		sysfatal("bad local root directory");
	rroot = argv[2];
	if(!isdir(rroot))
		sysfatal("bad remote root directory");

	match = argv+3;
	nmatch = argc-3;
	for(i=0; i<nmatch; i++)
		if(match[i][0] == '/')
			match[i]++;

	if((clientdb = opendb(argv[0])) == nil)
		sysfatal("opendb %q: %r", argv[2]);
	
	copyerr = opendb(nil);

	skip = 0;
	Binit(&bin, 0, OREAD);
	for(; s=Brdstr(&bin, '\n', 1); free(s)){
		t = estrdup(s);
		nf = tokenize(s, f, nelem(f));
		if(nf != 10 || strlen(f[2]) != 1){
			skip = 1;
			fprint(2, "warning: skipping bad log entry <%s>\n", t);
			free(t);
			continue;
		}
		free(t);
		now = strtoul(f[0], 0, 0);
		n = atoi(f[1]);
		verb = f[2][0];
		name = f[3];
		if(now < maxnow || (now==maxnow && n <= maxn))
			continue;
		local = mkname(localbuf, sizeof localbuf, lroot, name);
		if(strcmp(f[4], "-") == 0)
			f[4] = f[3];
		remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]);
		rd.name = f[4];
		rd.mode = strtoul(f[5], 0, 8);
		rd.uid = f[6];
		rd.gid = f[7];
		rd.mtime = strtoul(f[8], 0, 10);
		rd.length = strtoll(f[9], 0, 10);
		havedb = finddb(clientdb, name, &dbd)>=0;
		havelocal = localdirstat(local, &ld)>=0;

		resolve1 = resolve(name);

		/*
		 * if(!ismatch(name)){
		 *	skip = 1;
		 *	continue;
		 * }
		 * 
		 * This check used to be right here, but we want
		 * the time to be able to move forward past entries
		 * that don't match and have already been applied.
		 * So now every path below must checked !ismatch(name)
		 * before making any changes to the local file
		 * system.  The fake variable checkedmatch
		 * tracks whether !ismatch(name) has been checked.
		 * If the compiler doesn't produce any used/set
		 * warnings, then all the paths should be okay.
		 * Even so, we have the asserts to fall back on.
		 */
		switch(verb){
		case 'd':	/* delete file */
			delce(local);
			if(!havelocal)	/* doesn't exist; who cares? */
				break;
			if(access(remote, AEXIST) >= 0)	/* got recreated! */
				break;
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch1);
			if(!havedb){
				if(resolve1 == 's')
					goto DoRemove;
				else if(resolve1 == 'c')
					goto DoRemoveDb;
				conflict(name, "locally created; will not remove");
				skip = 1;
				continue;
			}
			assert(havelocal && havedb);
			if(dbd.mtime > rd.mtime)		/* we have a newer file than what was deleted */
				break;
			if(samecontents(local, remote) > 0){	/* going to get recreated */
				chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
				break;
			}
			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* locally modified since we downloaded it */
				if(resolve1 == 's')
					goto DoRemove;
				else if(resolve1 == 'c')
					break;
				conflict(name, "locally modified; will not remove");
				skip = 1;
				continue;
			}
		    DoRemove:
			USED(checkedmatch1);
			assert(ismatch(name));
			chat("d %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
			if(donothing)
				break;
			if(remove(local) < 0){
				error("removing %q: %r", name);
				skip = 1;
				continue;
			}
		    DoRemoveDb:
			USED(checkedmatch1);
			assert(ismatch(name));
			removedb(clientdb, name);
			break;

		case 'a':	/* add file */
			if(!havedb){
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch2);
				if(!havelocal)
					goto DoCreate;
				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
					break;
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCreateDb;
				}
				if(resolve1 == 's')
					goto DoCreate;
				else if(resolve1 == 'c')
					goto DoCreateDb;
				conflict(name, "locally created; will not overwrite");
				skip = 1;
				continue;
			}
			assert(havedb);
			if(dbd.mtime >= rd.mtime)	/* already created this file; ignore */
				break;
			if(havelocal){
				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
					break;
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch2);
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCreateDb;
				}
				if(dbd.mtime==ld.mtime && dbd.length==ld.length)
					goto DoCreate;
				if(resolve1=='s')
					goto DoCreate;
				else if(resolve1 == 'c')
					goto DoCreateDb;
				conflict(name, "locally modified; will not overwrite");
				skip = 1;
				continue;
			}
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch2);
		    DoCreate:
			USED(checkedmatch2);
			assert(ismatch(name));
			if(notexists(remote)){
				addce(local);
				/* no skip=1 */
				break;;
			}
			chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
			if(donothing)
				break;
			if(rd.mode&DMDIR){
				fd = create(local, OREAD, DMDIR);
				if(fd < 0 && isdir(local))
					fd = open(local, OREAD);
				if(fd  < 0){
					error("mkdir %q: %r", name);
					skip = 1;
					continue;
				}
				nulldir(&nd);
				nd.mode = rd.mode;
				if(dirfwstat(fd, &nd) < 0)
					fprint(2, "warning: cannot set mode on %q\n", local);
				nulldir(&nd);
				nd.gid = rd.gid;
				if(dirfwstat(fd, &nd) < 0)
					fprint(2, "warning: cannot set gid on %q\n", local);
				if(douid){
					nulldir(&nd);
					nd.uid = rd.uid;
					if(dirfwstat(fd, &nd) < 0)
						fprint(2, "warning: cannot set uid on %q\n", local);
				}
				close(fd);
				rd.mtime = now;
			}else{
				if(copyfile(local, remote, name, &rd, 1, &k) < 0){
					if(k)
						addce(local);
					skip = 1;
					continue;
				}
			}
		    DoCreateDb:
			USED(checkedmatch2);
			assert(ismatch(name));
			insertdb(clientdb, name, &rd);
			break;
			
		case 'c':	/* change contents */
			if(!havedb){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch3);
				if(resolve1 == 's')
					goto DoCopy;
				else if(resolve1=='c')
					goto DoCopyDb;
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCopyDb;
				}
				if(havelocal)
					conflict(name, "locally created; will not update");
				else
					conflict(name, "not replicated; will not update");
				skip = 1;
				continue;
			}
			if(dbd.mtime >= rd.mtime)		/* already have/had this version; ignore */
				break;
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch3);
			if(!havelocal){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(resolve1 == 's')
					goto DoCopy;
				else if(resolve1 == 'c')
					break;
				conflict(name, "locally removed; will not update");
				skip = 1;
				continue;
			}
			assert(havedb && havelocal);
			if(dbd.mtime != ld.mtime || dbd.length != ld.length){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCopyDb;
				}
				if(resolve1 == 's')
					goto DoCopy;
				else if(resolve1 == 'c')
					goto DoCopyDb;
				conflict(name, "locally modified; will not update [%llud %lud -> %llud %lud]", dbd.length, dbd.mtime, ld.length, ld.mtime);
				skip = 1;
				continue;
			}
		    DoCopy:
			USED(checkedmatch3);
			assert(ismatch(name));
			if(notexists(remote)){
				addce(local);
				/* no skip=1 */
				break;
			}
			chat("c %q\n", name);
			if(donothing)
				break;
			if(copyfile(local, remote, name, &rd, 0, &k) < 0){
				if(k)
					addce(local);
				skip = 1;
				continue;
			}
		    DoCopyDb:
			USED(checkedmatch3);
			assert(ismatch(name));
			if(!havedb){
				if(havelocal)
					dbd = ld;
				else
					dbd = rd;
			}
			dbd.mtime = rd.mtime;
			dbd.length = rd.length;
			insertdb(clientdb, name, &dbd);
			break;			

		case 'm':	/* change metadata */
			if(!havedb){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's'){
					USED(checkedmatch4);
					SET(checkedmatch2);
					goto DoCreate;
				}
				else if(resolve1 == 'c')
					goto DoMetaDb;
				if(havelocal)
					conflict(name, "locally created; will not update metadata");
				else
					conflict(name, "not replicated; will not update metadata");
				skip = 1;
				continue;
			}
			if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime)		/* have newer version; ignore */
				break;
			if((dbd.mode&DMDIR) && dbd.mtime > now)
				break;
			if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode)
				break;
			if(!havelocal){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's'){
					USED(checkedmatch4);
					SET(checkedmatch2);
					goto DoCreate;
				}
				else if(resolve1 == 'c')
					break;
				conflict(name, "locally removed; will not update metadata");
				skip = 1;
				continue;
			}
			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* this check might be overkill */
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's' || samecontents(local, remote) > 0)
					goto DoMeta;
				else if(resolve1 == 'c')
					break;
				conflict(name, "contents locally modified (%s); will not update metadata to %s %s %luo",
					dbd.mtime != ld.mtime ? "mtime" :
					dbd.length != ld.length ? "length" : 
					"unknown",
					rd.uid, rd.gid, rd.mode);
				skip = 1;
				continue;
			}
			if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's')
					goto DoMeta;
				else if(resolve1 == 'c')
					break;
				conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode);
				skip = 1;
				continue;
			}
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch4);
		    DoMeta:
			USED(checkedmatch4);
			assert(ismatch(name));
			if(notexists(remote)){
				addce(local);
				/* no skip=1 */
				break;
			}
			chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
			if(donothing)
				break;
			nulldir(&nd);
			nd.gid = rd.gid;
			nd.mode = rd.mode;
			if(douid)
				nd.uid = rd.uid;
			if(dirwstat(local, &nd) < 0){
				error("dirwstat %q: %r", name);
				skip = 1;
				continue;
			}
		    DoMetaDb:
			USED(checkedmatch4);
			assert(ismatch(name));
			if(!havedb){
				if(havelocal)
					dbd = ld;
				else
					dbd = rd;
			}
			if(dbd.mode&DMDIR)
				dbd.mtime = now;
			dbd.gid = rd.gid;
			dbd.mode = rd.mode;
			if(douid)
				dbd.uid = rd.uid;
			insertdb(clientdb, name, &dbd);
			break;
		}
		if(!skip && !donothing){
			maxnow = now;
			maxn = n;
		}
	}

	w = avlwalk(copyerr->avl);
	while(e = (Entry*)avlnext(w))
		error("copying %q: %s\n", e->name, e->d.name);

	if(timefile)
		writetimefile();
	if(nconf)
		exits("conflicts");

	if(errors)
		exits("errors");
	exits(nil);
}
Beispiel #24
0
static int
vnop_setattr_9p(struct vnop_setattr_args *ap)
{
	struct vnode_attr *vap;
	vnode_t vp;
	node_9p *np;
	dir_9p d;
	int e;

	TRACE();
	vp = ap->a_vp;
	vap = ap->a_vap;
	np = NTO9P(vp);

	if (vnode_vfsisrdonly(vp))
		return EROFS;
	
	if (vnode_isvroot(vp))
		return EACCES;

	nulldir(&d);
	if (VATTR_IS_ACTIVE(vap, va_data_size)) {
		if (vnode_isdir(vp))
			return EISDIR;
		d.length = vap->va_data_size;
	}
	VATTR_SET_SUPPORTED(vap, va_data_size);

	if (VATTR_IS_ACTIVE(vap, va_access_time))
		d.atime = vap->va_access_time.tv_sec;
	VATTR_SET_SUPPORTED(vap, va_access_time);

	if (VATTR_IS_ACTIVE(vap, va_modify_time))
		d.mtime = vap->va_modify_time.tv_sec;
	VATTR_SET_SUPPORTED(vap, va_modify_time);

	if (VATTR_IS_ACTIVE(vap, va_mode)) {
		d.mode = vap->va_mode & 0777;
		if (vnode_isdir(vp))
			SET(d.mode, DMDIR);
		if (ISSET(np->nmp->flags, F_DOTU)) {
			switch (vnode_vtype(vp)) {
			case VBLK:
			case VCHR:
				SET(d.mode, DMDEVICE);
				break;
			case VLNK:
				SET(d.mode, DMSYMLINK);
				break;
			case VSOCK:
				SET(d.mode, DMSOCKET);
				break;
			case VFIFO:
				SET(d.mode, DMNAMEDPIPE);
				break;
			default:
				break;
			}
		}
	}
	VATTR_SET_SUPPORTED(vap, va_mode);

	nlock_9p(np, NODE_LCK_EXCLUSIVE);
	e = wstat_9p(np->nmp, np->fid, &d);
	np->dirtimer = 0;

	if (e==0 && d.length!=~0)
		ubc_setsize(vp, d.length);

	nunlock_9p(np);
	return e;
}
Beispiel #25
0
void
extract(char *name, uint32_t mode, uint32_t mtime, char *uid,
	char *gid,
	uint64_t bytes)
{
	Dir d, *nd;
	Biobuf *b;
	char buf[LEN];
	char *p;
	uint32_t n;
	uint64_t tot;

	if(vflag)
		print("x %q %llu bytes\n", name, bytes);

	b = Bopen(name, OWRITE);
	if(!b){
		warn("can't make file %q: %r", name);
		seekpast(bytes);
		return;
	}
	for(tot = 0; tot < bytes; tot += n){
		n = sizeof buf;
		if(tot + n > bytes)
			n = bytes - tot;
		n = Bread(&bin, buf, n);
		if(n <= 0)
			error("premature eof reading %q", name);
		if(Bwrite(b, buf, n) != n)
			warn("error writing %q: %r", name);
	}

	nulldir(&d);
	p = utfrrune(name, '/');
	if(p)
		p++;
	else
		p = name;
	d.name = p;
	if(uflag){
		d.uid = uid;
		d.gid = gid;
	}
	if(Tflag)
		d.mtime = mtime;
	d.mode = mode;
	Bflush(b);
	if(dirfwstat(Bfildes(b), &d) < 0)
		warn("can't set modes for %q: %r", name);
	if(uflag||Tflag){
		if((nd = dirfstat(Bfildes(b))) == nil)
			warn("can't reread modes for %q: %r", name);
		else{
			if(Tflag && nd->mtime != mtime)
				warn("%q: time mismatch %lu %lu\n",
					name, mtime, nd->mtime);
			if(uflag && strcmp(uid, nd->uid))
				warn("%q: uid mismatch %q %q",
					name, uid, nd->uid);
			if(uflag && strcmp(gid, nd->gid))
				warn("%q: gid mismatch %q %q",
					name, gid, nd->gid);
			free(nd);
		}
	}
	Bterm(b);
}
Beispiel #26
0
void
unvac(VacFile *f, char *name, VacDir *vdir)
{
	static char buf[65536];
	int fd, n, m,  bsize;
	ulong mode, mode9;
	char *newname;
	char *what;
	vlong off;
	Dir d, *dp;
	VacDirEnum *vde;
	VacDir newvdir;
	VacFile *newf;

	if(vdir)
		mode = vdir->mode;
	else
		mode = vacfilegetmode(f);

	if(vdir){
		if(table){
			if(chatty){
				mode9 = vdir->mode&0777;
				if(mode&ModeDir)
					mode9 |= DMDIR;
				if(mode&ModeAppend)
					mode9 |= DMAPPEND;
				if(mode&ModeExclusive)
					mode9 |= DMEXCL;
				print("%M %-10s %-10s %11lld %t %s\n",
					mode9, vdir->uid, vdir->gid, vdir->size,
					vdir->mtime, name);
			}else
				print("%s%s\n", name, (mode&ModeDir) ? "/" : "");
		}
		else if(chatty)
			fprint(2, "%s%s\n", name, (mode&ModeDir) ? "/" : "");
	}

	if(mode&(ModeDevice|ModeLink|ModeNamedPipe|ModeExclusive)){
		if(table)
			return;
		if(mode&ModeDevice)
			what = "device";
		else if(mode&ModeLink)
			what = "link";
		else if(mode&ModeNamedPipe)
			what = "named pipe";
		else if(mode&ModeExclusive)
			what = "lock";
		else
			what = "unknown type of file";
		fprint(2, "warning: ignoring %s %s\n", what, name);
		return;
	}
	
	if(mode&ModeDir){
		if((vde = vdeopen(f)) == nil){
			fprint(2, "vdeopen %s: %r", name);
			errors++;
			return;
		}
		if(!table && !tostdout && vdir){
			// create directory
			if((dp = dirstat(name)) == nil){
				if((fd = create(name, OREAD, DMDIR|(mode&0777))) < 0){
					fprint(2, "mkdir %s: %r\n", name);
					vdeclose(vde);
				}
				close(fd);
			}else{
				if(!(dp->mode&DMDIR)){
					fprint(2, "%s already exists and is not a directory\n", name);
					errors++;
					free(dp);
					vdeclose(vde);
					return;
				}
				free(dp);
			}
		}
		while(vderead(vde, &newvdir) > 0){
			if(name == nil)
				newname = newvdir.elem;
			else
				newname = smprint("%s/%s", name, newvdir.elem);
			if(wantfile(newname)){
				if((newf = vacfilewalk(f, newvdir.elem)) == nil){
					fprint(2, "walk %s: %r\n", name);
					errors++;
				}else if(newf == f){
					fprint(2, "walk loop: %s\n", newname);
					vacfiledecref(newf);
				}else{
					unvac(newf, newname, &newvdir);
					vacfiledecref(newf);
				}
			}
			if(newname != newvdir.elem)
				free(newname);
			vdcleanup(&newvdir);
		}
		vdeclose(vde);
	}else{
		if(!table){
			off = 0;
			if(tostdout)
				fd = dup(1, -1);
			else if(diff && (fd = open(name, ORDWR)) >= 0){
				bsize = vacfiledsize(f);
				while((n = readn(fd, buf, bsize)) > 0){
					if(sha1matches(f, off/bsize, (uchar*)buf, n)){
						off += n;
						stats.skipdata += n;
						continue;
					}
					seek(fd, off, 0);
					if((m = vacfileread(f, buf, n, off)) < 0)
						break;
					if(writen(fd, buf, m) != m){
						fprint(2, "write %s: %r\n", name);
						goto Err;
					}
					off += m;
					stats.data += m;
					if(m < n){
						nulldir(&d);
						d.length = off;
						if(dirfwstat(fd, &d) < 0){
							fprint(2, "dirfwstat %s: %r\n", name);
							goto Err;
						}
						break;
					}
				}
			}
			else if((fd = create(name, OWRITE, mode&0777)) < 0){
				fprint(2, "create %s: %r\n", name);
				errors++;
				return;
			}
			while((n = vacfileread(f, buf, sizeof buf, off)) > 0){
				if(writen(fd, buf, n) != n){
					fprint(2, "write %s: %r\n", name);
				Err:
					errors++;
					close(fd);
					remove(name);
					return;
				}
				off += n;
				stats.data += n;
			}
			close(fd);
		}
	}
	if(vdir && settimes && !tostdout){
		nulldir(&d);
		d.mtime = vdir->mtime;
		if(dirwstat(name, &d) < 0)
			fprint(2, "warning: setting mtime on %s: %r", name);
	}
}