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