1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4
5 int
isatty(int fd)6 isatty(int fd)
7 {
8 char buf[64];
9
10 buf[0] = '\0';
11 fd2path(fd, buf, sizeof buf);
12 if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
13 return 1;
14 return 0;
15 }
16
17 #define OK 0x00
18 #define ERROR 0x01
19 #define FATAL 0x02
20
21 char *progname;
22
23 int dflag;
24 int fflag;
25 int iflag;
26 int pflag;
27 int rflag;
28 int tflag;
29 int vflag;
30
31 int remote;
32
33 char *exitflag = nil;
34
35 void scperror(int, char*, ...);
36 void mustbedir(char*);
37 void receive(char*);
38 char *fileaftercolon(char*);
39 void destislocal(char *cmd, int argc, char *argv[], char *dest);
40 void destisremote(char *cmd, int argc, char *argv[], char *host, char *dest);
41 int remotessh(char *host, char *cmd);
42 void send(char*);
43 void senddir(char*, int, Dir*);
44 int getresponse(void);
45
46 char theuser[32];
47
48 char ssh[] = "/bin/ssh";
49
50 int remotefd0;
51 int remotefd1;
52
53 int
runcommand(char * cmd)54 runcommand(char *cmd)
55 {
56 Waitmsg *w;
57 int pid;
58 char *argv[4];
59
60 if (cmd == nil)
61 return -1;
62 switch(pid = fork()){
63 case -1:
64 return -1;
65 case 0:
66 argv[0] = "rc";
67 argv[1] = "-c";
68 argv[2] = cmd;
69 argv[3] = nil;
70 exec("/bin/rc", argv);
71 exits("exec failed");
72 }
73 for(;;){
74 w = wait();
75 if(w == nil)
76 return -1;
77 if(w->pid == pid)
78 break;
79 free(w);
80 }
81 if(w->msg[0]){
82 free(w);
83 return -1;
84 }
85 free(w);
86 return 1;
87 }
88
89 void
vprint(char * fmt,...)90 vprint(char *fmt, ...)
91 {
92 char buf[1024];
93 va_list arg;
94 static char *name;
95
96 if(vflag == 0)
97 return;
98
99 va_start(arg, fmt);
100 vseprint(buf, buf+sizeof(buf), fmt, arg);
101 va_end(arg);
102
103 if(name == nil){
104 name = sysname();
105 if(name == nil)
106 name = "<unknown>";
107 }
108 fprint(2, "%s: %s\n", name, buf);
109 }
110
111 void
usage(void)112 usage(void)
113 {
114 fprint(2, "Usage: scp [-Iidfprtv] source ... destination\n");
115 exits("usage");
116 }
117
118
119 #pragma varargck type "F" int
120 #pragma varargck type "V" char*
121 static int flag;
122
123 /* flag: if integer flag, take following char *value */
124 int
flagfmt(Fmt * f)125 flagfmt(Fmt *f)
126 {
127 flag = va_arg(f->args, int);
128 return 0;
129 }
130
131 /* flag: if previous integer flag, take char *value */
132 int
valfmt(Fmt * f)133 valfmt(Fmt *f)
134 {
135 char *value;
136
137 value = va_arg(f->args, char*);
138 if(flag)
139 return fmtprint(f, " %s", value);
140 return 0;
141 }
142
143 void
sendokresponse(void)144 sendokresponse(void)
145 {
146 char ok = OK;
147
148 write(remotefd1, &ok, 1);
149 }
150
151 void
main(int argc,char * argv[])152 main(int argc, char *argv[])
153 {
154 int i, fd;
155 char cmd[32];
156 char *p;
157
158 progname = argv[0];
159 fmtinstall('F', flagfmt);
160 fmtinstall('V', valfmt);
161 iflag = -1;
162
163 ARGBEGIN {
164 case 'I':
165 iflag = 0;
166 break;
167 case 'i':
168 iflag = 1;
169 break;
170 case 'd':
171 dflag++;
172 break;
173 case 'f':
174 fflag++;
175 remote++;
176 break;
177 case 'p':
178 pflag++;
179 break;
180 case 'r':
181 rflag++;
182 break;
183 case 't':
184 tflag++;
185 remote++;
186 break;
187 case 'v':
188 vflag++;
189 break;
190 default:
191 scperror(1, "unknown option %c", ARGC());
192 } ARGEND
193
194 if(iflag == -1)
195 iflag = isatty(0);
196
197 remotefd0 = 0;
198 remotefd1 = 1;
199
200 if(fflag){
201 getresponse();
202 for(i=0; i<argc; i++)
203 send(argv[i]);
204 exits(0);
205 }
206 if(tflag){
207 if(argc != 1)
208 usage();
209 receive(argv[0]);
210 exits(0);
211 }
212
213 if (argc < 2)
214 usage();
215 if (argc > 2)
216 dflag = 1;
217
218 i = 0;
219 fd = open("/dev/user", OREAD);
220 if(fd >= 0){
221 i = read(fd, theuser, sizeof theuser - 1);
222 close(fd);
223 }
224 if(i <= 0)
225 scperror(1, "can't read /dev/user: %r");
226
227 remotefd0 = -1;
228 remotefd1 = -1;
229
230 snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V",
231 dflag, "-d",
232 pflag, "-p",
233 rflag, "-r",
234 vflag, "-v");
235
236 p = fileaftercolon(argv[argc-1]);
237 if(p != nil) /* send to remote machine. */
238 destisremote(cmd, argc-1, argv, argv[argc-1], p);
239 else{
240 if(dflag)
241 mustbedir(argv[argc-1]);
242 destislocal(cmd, argc-1, argv, argv[argc-1]);
243 }
244
245 exits(exitflag);
246 }
247
248 void
destislocal(char * cmd,int argc,char * argv[],char * dst)249 destislocal(char *cmd, int argc, char *argv[], char *dst)
250 {
251 int i;
252 char *src;
253 char buf[4096];
254
255 for(i = 0; i<argc; i++){
256 src = fileaftercolon(argv[i]);
257 if(src == nil){
258 /* local file; no network */
259 snprint(buf, sizeof buf, "exec cp%F%V%F%V %s %s",
260 rflag, "-r",
261 pflag, "-p",
262 argv[i], dst);
263 vprint("remotetolocal: %s", buf);
264 if(runcommand(buf) < 0)
265 exitflag = "local cp exec";
266 }else{
267 /* remote file; use network */
268 snprint(buf, sizeof buf, "%s -f %s", cmd, src);
269 if(remotessh(argv[i], buf) < 0)
270 exitflag = "remote ssh exec";
271 else{
272 receive(dst);
273 close(remotefd0);
274 remotefd0 = -1;
275 remotefd1 = -1;
276 }
277 }
278 }
279 }
280
281 void
destisremote(char * cmd,int argc,char * argv[],char * host,char * dest)282 destisremote(char *cmd, int argc, char *argv[], char *host, char *dest)
283 {
284 int i;
285 char *src;
286 char buf[4096];
287
288 for(i = 0; i < argc; i++){
289 vprint("remote destination: send %s to %s:%s", argv[i], host, dest);
290 /* destination is remote, but source may be local */
291 src = fileaftercolon(argv[i]);
292 if(src != nil){
293 /* remote to remote */
294 snprint(buf, sizeof buf, "exec %s%F%V%F%V %s %s %s '%s:%s'",
295 ssh,
296 iflag, " -i",
297 vflag, "-v",
298 argv[i], cmd, src,
299 host, dest);
300 vprint("localtoremote: %s", buf);
301 runcommand(buf);
302 }else{
303 /* local to remote */
304 if(remotefd0 == -1){
305 snprint(buf, sizeof buf, "%s -t %s", cmd, dest);
306 if(remotessh(host, buf) < 0)
307 exits("remotessh");
308 if(getresponse() < 0)
309 exits("bad response");
310 }
311 send(argv[i]);
312 }
313 }
314 }
315
316 void
readhdr(char * p,int n)317 readhdr(char *p, int n)
318 {
319 int i;
320
321 for(i=0; i<n; i++){
322 if(read(remotefd0, &p[i], 1) != 1)
323 break;
324 if(p[i] == '\n'){
325 p[i] = '\0';
326 return;
327 }
328 }
329 /* if at beginning, this is regular EOF */
330 if(i == 0)
331 exits(nil);
332 scperror(1, "read error on receive header: %r");
333 }
334
335 Dir *
receivedir(char * dir,int exists,Dir * d,int settimes,ulong atime,ulong mtime,ulong mode)336 receivedir(char *dir, int exists, Dir *d, int settimes, ulong atime, ulong mtime, ulong mode)
337 {
338 Dir nd;
339 int setmodes;
340 int fd;
341
342 setmodes = pflag;
343 if(exists){
344 if(!(d->qid.type & QTDIR)) {
345 scperror(0, "%s: protocol botch: directory requrest for non-directory", dir);
346 return d;
347 }
348 }else{
349 /* create it writeable; will fix later */
350 setmodes = 1;
351 fd = create(dir, OREAD, DMDIR|mode|0700);
352 if (fd < 0){
353 scperror(0, "%s: can't create: %r", dir);
354 return d;
355 }
356 d = dirfstat(fd);
357 close(fd);
358 if(d == nil){
359 scperror(0, "%s: can't stat: %r", dir);
360 return d;
361 }
362 }
363 receive(dir);
364 if(settimes || setmodes){
365 nulldir(&nd);
366 if(settimes){
367 nd.atime = atime;
368 nd.mtime = mtime;
369 d->atime = nd.atime;
370 d->mtime = nd.mtime;
371 }
372 if(setmodes){
373 nd.mode = DMDIR | (mode & 0777);
374 d->mode = nd.mode;
375 }
376 if(dirwstat(dir, &nd) < 0){
377 scperror(0, "can't wstat %s: %r", dir);
378 free(d);
379 return nil;
380 }
381 }
382 return d;
383 }
384
385 void
receive(char * dest)386 receive(char *dest)
387 {
388 int isdir, settimes, mode;
389 int exists, n, i, fd, m;
390 int errors;
391 ulong atime, mtime, size;
392 char buf[8192], *p;
393 char name[1024];
394 Dir *d;
395 Dir nd;
396
397 mtime = 0L;
398 atime = 0L;
399 settimes = 0;
400 isdir = 0;
401 if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) {
402 isdir = 1;
403 }
404 if(dflag && !isdir)
405 scperror(1, "%s: not a directory: %r", dest);
406
407 sendokresponse();
408
409 for (;;) {
410 readhdr(buf, sizeof buf);
411
412 switch(buf[0]){
413 case ERROR:
414 case FATAL:
415 if(!remote)
416 fprint(2, "%s\n", buf+1);
417 exitflag = "bad receive";
418 if(buf[0] == FATAL)
419 exits(exitflag);
420 continue;
421
422 case 'E':
423 sendokresponse();
424 return;
425
426 case 'T':
427 settimes = 1;
428 p = buf + 1;
429 mtime = strtol(p, &p, 10);
430 if(*p++ != ' '){
431 Badtime:
432 scperror(1, "bad time format: %s", buf+1);
433 }
434 strtol(p, &p, 10);
435 if(*p++ != ' ')
436 goto Badtime;
437 atime = strtol(p, &p, 10);
438 if(*p++ != ' ')
439 goto Badtime;
440 strtol(p, &p, 10);
441 if(*p++ != 0)
442 goto Badtime;
443
444 sendokresponse();
445 continue;
446
447 case 'D':
448 case 'C':
449 p = buf + 1;
450 mode = strtol(p, &p, 8);
451 if (*p++ != ' '){
452 Badmode:
453 scperror(1, "bad mode/size format: %s", buf+1);
454 }
455 size = strtoll(p, &p, 10);
456 if(*p++ != ' ')
457 goto Badmode;
458
459 if(isdir){
460 if(dest[0] == '\0')
461 snprint(name, sizeof name, "%s", p);
462 else
463 snprint(name, sizeof name, "%s/%s", dest, p);
464 }else
465 snprint(name, sizeof name, "%s", dest);
466 if(strlen(name) > sizeof name-UTFmax)
467 scperror(1, "file name too long: %s", dest);
468
469 exists = 1;
470 free(d);
471 if((d = dirstat(name)) == nil)
472 exists = 0;
473
474 if(buf[0] == 'D'){
475 vprint("receive directory %s", name);
476 d = receivedir(name, exists, d, settimes, atime, mtime, mode);
477 settimes = 0;
478 continue;
479 }
480
481 vprint("receive file %s by %s", name, getuser());
482 fd = create(name, OWRITE, mode);
483 if(fd < 0){
484 scperror(0, "can't create %s: %r", name);
485 continue;
486 }
487 sendokresponse();
488
489 /*
490 * Committed to receive size bytes
491 */
492 errors = 0;
493 for(i = 0; i < size; i += m){
494 n = sizeof buf;
495 if(n > size - i)
496 n = size - i;
497 m = readn(remotefd0, buf, n);
498 if(m <= 0)
499 scperror(1, "read error on connection: %r");
500 if(errors == 0){
501 n = write(fd, buf, m);
502 if(n != m)
503 errors = 1;
504 }
505 }
506
507 /* if file exists, modes could be wrong */
508 if(errors)
509 scperror(0, "%s: write error: %r", name);
510 else if(settimes || (exists && (d->mode&0777) != (mode&0777))){
511 nulldir(&nd);
512 if(settimes){
513 settimes = 0;
514 nd.atime = atime;
515 nd.mtime = mtime;
516 }
517 if(exists && (d->mode&0777) != (mode&0777))
518 nd.mode = (d->mode & ~0777) | (mode&0777);
519 if(dirwstat(name, &nd) < 0)
520 scperror(0, "can't wstat %s: %r", name);
521 }
522 free(d);
523 d = nil;
524 close(fd);
525 getresponse();
526 if(errors)
527 exits("write error");
528 sendokresponse();
529 break;
530
531 default:
532 scperror(0, "unrecognized header type char %c", buf[0]);
533 scperror(1, "input line: %s", buf);
534 }
535 }
536 }
537
538 /*
539 * Lastelem is called when we have a Dir with the final element, but if the file
540 * has been bound, we want the original name that was used rather than
541 * the contents of the stat buffer, so do this lexically.
542 */
543 char*
lastelem(char * file)544 lastelem(char *file)
545 {
546 char *elem;
547
548 elem = strrchr(file, '/');
549 if(elem == nil)
550 return file;
551 return elem+1;
552 }
553
554 void
send(char * file)555 send(char *file)
556 {
557 Dir *d;
558 ulong i;
559 int m, n, fd;
560 char buf[8192];
561
562 if((fd = open(file, OREAD)) < 0){
563 scperror(0, "can't open %s: %r", file);
564 return;
565 }
566 if((d = dirfstat(fd)) == nil){
567 scperror(0, "can't fstat %s: %r", file);
568 goto Return;
569 }
570
571 if(d->qid.type & QTDIR){
572 if(rflag)
573 senddir(file, fd, d);
574 else
575 scperror(0, "%s: is a directory", file);
576 goto Return;
577 }
578
579 if(pflag){
580 fprint(remotefd1, "T%lud 0 %lud 0\n", d->mtime, d->atime);
581 if(getresponse() < 0)
582 goto Return;
583 }
584
585 fprint(remotefd1, "C%.4luo %lld %s\n", d->mode&0777, d->length, lastelem(file));
586 if(getresponse() < 0)
587 goto Return;
588
589 /*
590 * We are now committed to send d.length bytes, regardless
591 */
592 for(i=0; i<d->length; i+=m){
593 n = sizeof buf;
594 if(n > d->length - i)
595 n = d->length - i;
596 m = readn(fd, buf, n);
597 if(m <= 0)
598 break;
599 write(remotefd1, buf, m);
600 }
601
602 if(i == d->length)
603 sendokresponse();
604 else{
605 /* continue to send gibberish up to d.length */
606 for(; i<d->length; i+=n){
607 n = sizeof buf;
608 if(n > d->length - i)
609 n = d->length - i;
610 write(remotefd1, buf, n);
611 }
612 scperror(0, "%s: %r", file);
613 }
614
615 getresponse();
616
617 Return:
618 free(d);
619 close(fd);
620 }
621
622 int
getresponse(void)623 getresponse(void)
624 {
625 uchar first, byte, buf[256];
626 int i;
627
628 if (read(remotefd0, &first, 1) != 1)
629 scperror(1, "lost connection");
630
631 if(first == 0)
632 return 0;
633
634 i = 0;
635 if(first > FATAL){
636 fprint(2, "scp: unexpected response character 0x%.2ux\n", first);
637 buf[i++] = first;
638 }
639
640 /* read error message up to newline */
641 for(;;){
642 if(read(remotefd0, &byte, 1) != 1)
643 scperror(1, "response: dropped connection");
644 if(byte == '\n')
645 break;
646 if(i < sizeof buf)
647 buf[i++] = byte;
648 }
649
650 exitflag = "bad response";
651 if(!remote)
652 fprint(2, "%.*s\n", utfnlen((char*)buf, i), (char*)buf);
653
654 if (first == ERROR)
655 return -1;
656 exits(exitflag);
657 return 0; /* not reached */
658 }
659
660 void
senddir(char * name,int fd,Dir * dirp)661 senddir(char *name, int fd, Dir *dirp)
662 {
663 Dir *d, *dir;
664 int n;
665 char file[256];
666
667 if(pflag){
668 fprint(remotefd1, "T%lud 0 %lud 0\n", dirp->mtime, dirp->atime);
669 if(getresponse() < 0)
670 return;
671 }
672
673 vprint("directory %s mode: D%.4lo %d %.1024s", name, dirp->mode&0777, 0, lastelem(name));
674
675 fprint(remotefd1, "D%.4lo %d %.1024s\n", dirp->mode&0777, 0, dirp->name);
676 if(getresponse() < 0)
677 return;
678
679 n = dirreadall(fd, &dir);
680 for(d = dir; d < &dir[n]; d++){
681 /* shouldn't happen with plan 9, but worth checking anyway */
682 if(strcmp(d->name, ".")==0 || strcmp(d->name, "..")==0)
683 continue;
684 if(snprint(file, sizeof file, "%s/%s", name, d->name) > sizeof file-UTFmax){
685 scperror(0, "%.20s.../%s: name too long; skipping file", file, d->name);
686 continue;
687 }
688 send(file);
689 }
690 free(dir);
691 fprint(remotefd1, "E\n");
692 getresponse();
693 }
694
695 int
remotessh(char * host,char * cmd)696 remotessh(char *host, char *cmd)
697 {
698 int i, p[2];
699 char *arg[32];
700
701 vprint("remotessh: %s: %s", host, cmd);
702
703 if(pipe(p) < 0)
704 scperror(1, "pipe: %r");
705
706 switch(fork()){
707 case -1:
708 scperror(1, "fork: %r");
709
710 case 0:
711 /* child */
712 close(p[0]);
713 dup(p[1], 0);
714 dup(p[1], 1);
715 for (i = 3; i < 100; i++)
716 close(i);
717
718 i = 0;
719 arg[i++] = ssh;
720 arg[i++] = "-x";
721 arg[i++] = "-a";
722 arg[i++] = "-m";
723 if(iflag)
724 arg[i++] = "-i";
725 if(vflag)
726 arg[i++] = "-v";
727 arg[i++] = host;
728 arg[i++] = cmd;
729 arg[i] = nil;
730
731 exec(ssh, arg);
732 exits("exec failed");
733
734 default:
735 /* parent */
736 close(p[1]);
737 remotefd0 = p[0];
738 remotefd1 = p[0];
739 }
740 return 0;
741 }
742
743 void
scperror(int exit,char * fmt,...)744 scperror(int exit, char *fmt, ...)
745 {
746 char buf[2048];
747 va_list arg;
748
749
750 va_start(arg, fmt);
751 vseprint(buf, buf+sizeof(buf), fmt, arg);
752 va_end(arg);
753
754 fprint(remotefd1, "%cscp: %s\n", ERROR, buf);
755
756 if (!remote)
757 fprint(2, "scp: %s\n", buf);
758 exitflag = buf;
759 if(exit)
760 exits(exitflag);
761 }
762
763 char *
fileaftercolon(char * file)764 fileaftercolon(char *file)
765 {
766 char *c, *s;
767
768 c = utfrune(file, ':');
769 if(c == nil)
770 return nil;
771
772 /* colon must be in middle of name to be a separator */
773 if(c == file)
774 return nil;
775
776 /* does slash occur before colon? */
777 s = utfrune(file, '/');
778 if(s != nil && s < c)
779 return nil;
780
781 *c++ = '\0';
782 if(*c == '\0')
783 return ".";
784 return c;
785 }
786
787 void
mustbedir(char * file)788 mustbedir(char *file)
789 {
790 Dir *d;
791
792 if((d = dirstat(file)) == nil){
793 scperror(1, "%s: %r", file);
794 return;
795 }
796 if(!(d->qid.type & QTDIR))
797 scperror(1, "%s: Not a directory", file);
798 free(d);
799 }
800