⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scp.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include <ctype.h>intisatty(int fd){	char buf[64];	buf[0] = '\0';	fd2path(fd, buf, sizeof buf);	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)		return 1;	return 0;}#define	OK	0x00#define	ERROR	0x01#define	FATAL	0x02char	*progname;int	dflag;int	fflag;int	iflag;int	pflag;int	rflag;int	tflag;int	vflag;int	remote;char	*exitflag = nil;void	scperror(int, char*, ...);void	mustbedir(char*);void	receive(char*);char	*fileaftercolon(char*);void	destislocal(char *cmd, int argc, char *argv[], char *dest);void	destisremote(char *cmd, int argc, char *argv[], char *host, char *dest);int	remotessh(char *host, char *cmd);void	send(char*);void	senddir(char*, int, Dir*);int 	getresponse(void);char	theuser[32];char	ssh[] = "/bin/ssh";int	remotefd0;int	remotefd1;intruncommand(char *cmd){	Waitmsg *w;	int pid;	char *argv[4];	if (cmd == nil)		return -1;	switch(pid = fork()){	case -1:		return -1;	case 0:		argv[0] = "rc";		argv[1] = "-c";		argv[2] = cmd;		argv[3] = nil;		exec("/bin/rc", argv);		exits("exec failed");	}	for(;;){		w = wait();		if(w == nil)			return -1;		if(w->pid == pid)			break;		free(w);	}	if(w->msg[0]){		free(w);		return -1;	}	free(w);	return 1;}voidvprint(char *fmt, ...){	char buf[1024];	va_list arg;	static char *name;	if(vflag == 0)		return;	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg);	if(name == nil){		name = sysname();		if(name == nil)			name = "<unknown>";	}	fprint(2, "%s: %s\n", name, buf);}voidusage(void){	fprint(2, "Usage: scp [-Iidfprtv] source ... destination\n");	exits("usage");}#pragma	varargck	type	"F"	int#pragma	varargck	type	"V"	char*static int flag;/* flag: if integer flag, take following char *value */intflagfmt(Fmt *f){	flag = va_arg(f->args, int);	return 0;}/* flag: if previous integer flag, take char *value */intvalfmt(Fmt *f){	char *value;	value = va_arg(f->args, char*);	if(flag)		return fmtprint(f, " %s", value);	return 0;}voidsendokresponse(void){	char ok = OK;	write(remotefd1, &ok, 1);}voidmain(int argc, char *argv[]){	int i, fd;	char cmd[32];	char *p;	progname = argv[0];	fmtinstall('F', flagfmt);	fmtinstall('V', valfmt);	iflag = -1;	ARGBEGIN {	case 'I':		iflag = 0;		break;	case 'i':		iflag = 1;		break;	case 'd':		dflag++;		break;	case 'f':		fflag++;		remote++;		break;	case 'p':		pflag++;		break;	case 'r':		rflag++;		break;	case 't':		tflag++;		remote++;		break;	case 'v':		vflag++;		break;	default:		scperror(1, "unknown option %c", ARGC());	} ARGEND	if(iflag == -1)		iflag = isatty(0);	remotefd0 = 0;	remotefd1 = 1;	if(fflag){		getresponse();		for(i=0; i<argc; i++)			send(argv[i]);		exits(0);	}	if(tflag){		if(argc != 1)			usage();		receive(argv[0]);		exits(0);	}	if (argc < 2)		usage();	if (argc > 2)		dflag = 1;	i = 0;	fd = open("/dev/user", OREAD);	if(fd >= 0){		i = read(fd, theuser, sizeof theuser - 1);		close(fd);	}	if(i <= 0)		scperror(1, "can't read /dev/user: %r");	remotefd0 = -1;	remotefd1 = -1;	snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V",		dflag, "-d",		pflag, "-p",		rflag, "-r",		vflag, "-v");	p = fileaftercolon(argv[argc-1]);	if(p != nil)	/* send to remote machine. */		destisremote(cmd, argc-1, argv, argv[argc-1], p);	else{		if(dflag)			mustbedir(argv[argc-1]);		destislocal(cmd, argc-1, argv, argv[argc-1]);	}	exits(exitflag);}voiddestislocal(char *cmd, int argc, char *argv[], char *dst){	int i;	char *src;	char buf[4096];	for(i = 0; i<argc; i++){		src = fileaftercolon(argv[i]);		if(src == nil){			/* local file; no network */			snprint(buf, sizeof buf, "exec cp%F%V%F%V %s %s",				rflag, "-r",				pflag, "-p",				argv[i], dst);	  		vprint("remotetolocal: %s", buf);			if(runcommand(buf) < 0)				exitflag = "local cp exec";		}else{			/* remote file; use network */			snprint(buf, sizeof buf, "%s -f %s", cmd, src);		  	if(remotessh(argv[i], buf) < 0)				exitflag = "remote ssh exec";			else{				receive(dst);				close(remotefd0);				remotefd0 = -1;				remotefd1 = -1;			}		}	}}voiddestisremote(char *cmd, int argc, char *argv[], char *host, char *dest){	int i;	char *src;	char buf[4096];	for(i = 0; i < argc; i++){		vprint("remote destination: send %s to %s:%s", argv[i], host, dest);		/* destination is remote, but source may be local */		src = fileaftercolon(argv[i]);		if(src != nil){			/* remote to remote */			snprint(buf, sizeof buf, "exec %s%F%V%F%V %s %s %s '%s:%s'",				ssh,				iflag, " -i",				vflag, "-v",				argv[i], cmd, src,				host, dest);			vprint("localtoremote: %s", buf);			runcommand(buf);		}else{			/* local to remote */			if(remotefd0 == -1){				snprint(buf, sizeof buf, "%s -t %s", cmd, dest);				if(remotessh(host, buf) < 0)					exits("remotessh");				if(getresponse() < 0)					exits("bad response");			}			send(argv[i]);		}	}}voidreadhdr(char *p, int n){	int i;	for(i=0; i<n; i++){		if(read(remotefd0, &p[i], 1) != 1)			break;		if(p[i] == '\n'){			p[i] = '\0';			return;		}	}	/* if at beginning, this is regular EOF */	if(i == 0)		exits(nil);	scperror(1, "read error on receive header: %r");}Dir *receivedir(char *dir, int exists, Dir *d, int settimes, ulong atime, ulong mtime, ulong mode){	Dir nd;	int setmodes;	int fd;	setmodes = pflag;	if(exists){		if(!(d->qid.type & QTDIR)) {			scperror(0, "%s: protocol botch: directory requrest for non-directory", dir);			return d;		}	}else{		/* create it writeable; will fix later */		setmodes = 1;		fd = create(dir, OREAD, DMDIR|mode|0700);		if (fd < 0){			scperror(0, "%s: can't create: %r", dir);			return d;		}		d = dirfstat(fd);		close(fd);		if(d == nil){			scperror(0, "%s: can't stat: %r", dir);			return d;		}	}	receive(dir);	if(settimes || setmodes){		nulldir(&nd);		if(settimes){			nd.atime = atime;			nd.mtime = mtime;			d->atime = nd.atime;			d->mtime = nd.mtime;		}		if(setmodes){			nd.mode = DMDIR | (mode & 0777);			d->mode = nd.mode;		}		if(dirwstat(dir, &nd) < 0){			scperror(0, "can't wstat %s: %r", dir);			free(d);			return nil;		}	}	return d;}voidreceive(char *dest){	int isdir, settimes, mode;	int exists, n, i, fd, m;	int errors;	ulong atime, mtime, size;	char buf[8192], *p;	char name[1024];	Dir *d;	Dir nd;	mtime = 0L;	atime = 0L;	settimes = 0;	isdir = 0;	if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) {		isdir = 1;	}	if(dflag && !isdir)		scperror(1, "%s: not a directory: %r", dest);	sendokresponse();	for (;;) {		readhdr(buf, sizeof buf);		switch(buf[0]){		case ERROR:		case FATAL:			if(!remote)				fprint(2, "%s\n", buf+1);			exitflag = "bad receive";			if(buf[0] == FATAL)				exits(exitflag);			continue;		case 'E':			sendokresponse();			return;		case 'T':			settimes = 1;			p = buf + 1;			mtime = strtol(p, &p, 10);			if(*p++ != ' '){		Badtime:				scperror(1, "bad time format: %s", buf+1);			}			strtol(p, &p, 10);			if(*p++ != ' ')				goto Badtime;			atime = strtol(p, &p, 10);			if(*p++ != ' ')				goto Badtime;			strtol(p, &p, 10);			if(*p++ != 0)				goto Badtime;			sendokresponse();			continue;		case 'D':		case 'C':			p = buf + 1;			mode = strtol(p, &p, 8);			if (*p++ != ' '){		Badmode:				scperror(1, "bad mode/size format: %s", buf+1);			}			size = strtoll(p, &p, 10);			if(*p++ != ' ')				goto Badmode;			if(isdir){				if(dest[0] == '\0')					snprint(name, sizeof name, "%s", p);				else					snprint(name, sizeof name, "%s/%s", dest, p);			}else				snprint(name, sizeof name, "%s", dest);			if(strlen(name) > sizeof name-UTFmax)				scperror(1, "file name too long: %s", dest);			exists = 1;			free(d);			if((d = dirstat(name)) == nil)				exists = 0;			if(buf[0] == 'D'){				vprint("receive directory %s", name);				d = receivedir(name, exists, d, settimes, atime, mtime, mode);				settimes = 0;				continue;			}			vprint("receive file %s by %s", name, getuser());			fd = create(name, OWRITE, mode);			if(fd < 0){				scperror(0, "can't create %s: %r", name);				continue;			}			sendokresponse();			/*			 * Committed to receive size bytes			 */			errors = 0;			for(i = 0; i < size; i += m){				n = sizeof buf;				if(n > size - i)					n = size - i;				m = readn(remotefd0, buf, n);				if(m <= 0)					scperror(1, "read error on connection: %r");				if(errors == 0){					n = write(fd, buf, m);					if(n != m)						errors = 1;				}			}			/* if file exists, modes could be wrong */			if(errors)				scperror(0, "%s: write error: %r", name);			else if(settimes || (exists && (d->mode&0777) != (mode&0777))){				nulldir(&nd);				if(settimes){					settimes = 0;					nd.atime = atime;					nd.mtime = mtime;				}				if(exists && (d->mode&0777) != (mode&0777))					nd.mode = (d->mode & ~0777) | (mode&0777);				if(dirwstat(name, &nd) < 0)					scperror(0, "can't wstat %s: %r", name);			}			free(d);			d = nil;			close(fd);			getresponse();			if(errors)				exits("write error");			sendokresponse();			break;		default:			scperror(0, "unrecognized header type char %c", buf[0]);				scperror(1, "input line: %s", buf);			}	}}/* * Lastelem is called when we have a Dir with the final element, but if the file * has been bound, we want the original name that was used rather than * the contents of the stat buffer, so do this lexically. */char*lastelem(char *file){	char *elem;	elem = strrchr(file, '/');	if(elem == nil)		return file;	return elem+1;}voidsend(char *file){	Dir *d;	ulong i;	int m, n, fd;	char buf[8192];	if((fd = open(file, OREAD)) < 0){		scperror(0, "can't open %s: %r", file);		return;	}	if((d = dirfstat(fd)) == nil){		scperror(0, "can't fstat %s: %r", file);		goto Return;	}	if(d->qid.type & QTDIR){		if(rflag)			senddir(file, fd, d);		else			scperror(0, "%s: is a directory", file);		goto Return;	}	if(pflag){		fprint(remotefd1, "T%lud 0 %lud 0\n", d->mtime, d->atime);		if(getresponse() < 0)			goto Return;	}	fprint(remotefd1, "C%.4luo %lld %s\n", d->mode&0777, d->length, lastelem(file));	if(getresponse() < 0)		goto Return;	/*	 * We are now committed to send d.length bytes, regardless	 */	for(i=0; i<d->length; i+=m){		n = sizeof buf;		if(n > d->length - i)			n = d->length - i;		m = readn(fd, buf, n);		if(m <= 0)			break;		write(remotefd1, buf, m);	}	if(i == d->length)		sendokresponse();	else{		/* continue to send gibberish up to d.length */		for(; i<d->length; i+=n){			n = sizeof buf;			if(n > d->length - i)				n = d->length - i;			write(remotefd1, buf, n);		}		scperror(0, "%s: %r", file);	}			getresponse();    Return:	free(d);	close(fd);}intgetresponse(void){	uchar first, byte, buf[256];	int i;	if (read(remotefd0, &first, 1) != 1)		scperror(1, "lost connection");	if(first == 0)		return 0;	i = 0;	if(first > FATAL){		fprint(2, "scp: unexpected response character 0x%.2ux\n", first);		buf[i++] = first;	}	/* read error message up to newline */	for(;;){		if(read(remotefd0, &byte, 1) != 1)			scperror(1, "response: dropped connection");		if(byte == '\n')			break;		if(i < sizeof buf)			buf[i++] = byte;	}	exitflag = "bad response";	if(!remote)		fprint(2, "%.*s\n", utfnlen((char*)buf, i), (char*)buf);	if (first == ERROR)		return -1;	exits(exitflag);	return 0;	/* not reached */}voidsenddir(char *name, int fd, Dir *dirp){	Dir *d, *dir;	int n;	char file[256];	if(pflag){		fprint(remotefd1, "T%lud 0 %lud 0\n", dirp->mtime, dirp->atime);		if(getresponse() < 0)			return;	}	vprint("directory %s mode: D%.4lo %d %.1024s", name, dirp->mode&0777, 0, lastelem(name));	fprint(remotefd1, "D%.4lo %d %.1024s\n", dirp->mode&0777, 0, dirp->name);	if(getresponse() < 0)		return;	n = dirreadall(fd, &dir);	for(d = dir; d < &dir[n]; d++){		/* shouldn't happen with plan 9, but worth checking anyway */		if(strcmp(d->name, ".")==0 || strcmp(d->name, "..")==0)			continue;		if(snprint(file, sizeof file, "%s/%s", name, d->name) > sizeof file-UTFmax){			scperror(0, "%.20s.../%s: name too long; skipping file", file, d->name);			continue;		}		send(file);	}	free(dir);	fprint(remotefd1, "E\n");	getresponse();}intremotessh(char *host, char *cmd){	int i, p[2];	char *arg[32];	vprint("remotessh: %s: %s", host, cmd);	if(pipe(p) < 0)		scperror(1, "pipe: %r");	switch(fork()){	case -1:		scperror(1, "fork: %r");	case 0:		/* child */		close(p[0]);		dup(p[1], 0);		dup(p[1], 1);		for (i = 3; i < 100; i++)			close(i);			i = 0;		arg[i++] = ssh;		arg[i++] = "-x";		arg[i++] = "-a";		arg[i++] = "-m";		if(iflag)			arg[i++] = "-i";		if(vflag)			arg[i++] = "-v";		arg[i++] = host;		arg[i++] = cmd;		arg[i] = nil;			exec(ssh, arg);		exits("exec failed");	default:		/* parent */		close(p[1]);		remotefd0 = p[0];		remotefd1 = p[0];	}	return 0;}voidscperror(int exit, char *fmt, ...){	char buf[2048];	va_list arg;	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg);	fprint(remotefd1, "%cscp: %s\n", ERROR, buf);	if (!remote)		fprint(2, "scp: %s\n", buf);	exitflag = buf;	if(exit)		exits(exitflag);}char *fileaftercolon(char *file){	char *c, *s;	c = utfrune(file, ':');	if(c == nil)		return nil;	/* colon must be in middle of name to be a separator */	if(c == file)		return nil;	/* does slash occur before colon? */	s = utfrune(file, '/');	if(s != nil && s < c)		return nil;	*c++ = '\0';	if(*c == '\0')		return ".";	return c;}voidmustbedir(char *file){	Dir *d;	if((d = dirstat(file)) == nil){		scperror(1, "%s: %r", file);		return;	}	if(!(d->qid.type & QTDIR))		scperror(1, "%s: Not a directory", file);	free(d);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -