Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <bio.h>
4
#include <ip.h>
5
#include <mp.h>
6
#include <libsec.h>
7
#include <auth.h>
8
#include <fcall.h>
9
#include <ctype.h>
10
#include <String.h>
11
#include "ftpfs.h"
12
 
13
enum
14
{
15
	/* return codes */
16
	Extra=		1,
17
	Success=	2,
18
	Incomplete=	3,
19
	TempFail=	4,
20
	PermFail=	5,
21
	Impossible=	6,
22
};
23
 
24
Node	*remdir;		/* current directory on remote machine */
25
Node	*remroot;		/* root directory on remote machine */
26
 
27
int	ctlfd;			/* fd for control connection */
28
Biobuf	ctlin;			/* input buffer for control connection */
29
Biobuf	stdin;			/* input buffer for standard input */
30
Biobuf	dbuf;			/* buffer for data connection */
31
char	msg[512];		/* buffer for replies */
32
char	net[Maxpath];		/* network for connections */
33
int	listenfd;		/* fd to listen on for connections */
34
char	netdir[Maxpath];
35
int	os, defos;
36
char	topsdir[64];		/* name of listed directory for TOPS */
37
String	*remrootpath;	/* path on remote side to remote root */
38
char	*user;
39
int	nopassive;
40
long	lastsend;
41
extern int usetls;
42
 
43
static void	sendrequest(char*, char*);
44
static int	getreply(Biobuf*, char*, int, int);
45
static int	active(int, Biobuf**, char*, char*);
46
static int	passive(int, Biobuf**, char*, char*);
47
static int	data(int, Biobuf**, char*, char*);
48
static int	port(void);
49
static void	ascii(void);
50
static void	image(void);
51
static void	unixpath(Node*, String*);
52
static void	vmspath(Node*, String*);
53
static void	mvspath(Node*, String*);
54
static Node*	vmsdir(char*);
55
static int	getpassword(char*, char*);
56
static int	nw_mode(char dirlet, char *s);
57
 
58
/*
59
 *  connect to remote server, default network is "tcp/ip"
60
 */
61
void
62
hello(char *dest)
63
{
64
	char *p;
65
	char dir[Maxpath];
66
	TLSconn conn;
67
 
68
	Binit(&stdin, 0, OREAD);	/* init for later use */
69
 
70
	ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0);
71
	if(ctlfd < 0){
72
		fprint(2, "can't dial %s: %r\n", dest);
73
		exits("dialing");
74
	}
75
 
76
	Binit(&ctlin, ctlfd, OREAD);
77
 
78
	/* remember network for the data connections */
79
	p = strrchr(dir+1, '/');
80
	if(p == 0)
81
		fatal("wrong dial(2) linked with ftp");
82
	*p = 0;
83
	safecpy(net, dir, sizeof(net));
84
 
85
	/* wait for hello from other side */
86
	if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
87
		fatal("bad hello");
88
	if(strstr(msg, "Plan 9"))
89
		os = Plan9;
90
 
91
	if(usetls){
92
		sendrequest("AUTH", "TLS");
93
		if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
94
			fatal("bad auth tls");
95
 
96
		ctlfd = tlsClient(ctlfd, &conn);
97
		if(ctlfd < 0)
98
			fatal("starting tls: %r");
99
		free(conn.cert);
100
 
101
		Binit(&ctlin, ctlfd, OREAD);
102
 
103
		sendrequest("PBSZ", "0");
104
		if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
105
			fatal("bad pbsz 0");
106
		sendrequest("PROT", "P");
107
		if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
108
			fatal("bad prot p");
109
	}
110
}
111
 
112
/*
113
 *  login to remote system
114
 */
115
void
116
rlogin(char *rsys, char *keyspec)
117
{
118
	char *line;
119
	char pass[128];
120
	UserPasswd *up;
121
 
122
	up = nil;
123
	for(;;){
124
		if(up == nil && os != Plan9)
125
			up = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=ftp %s", rsys, keyspec);
126
		if(up != nil){
127
			sendrequest("USER", up->user);
128
		} else {
129
			print("User[default = %s]: ", user);
130
			line = Brdline(&stdin, '\n');
131
			if(line == 0)
132
				exits(0);
133
			line[Blinelen(&stdin)-1] = 0;
134
			if(*line){
135
				free(user);
136
				user = strdup(line);
137
			}
138
			sendrequest("USER", user);
139
		}
140
		switch(getreply(&ctlin, msg, sizeof(msg), 1)){
141
		case Success:
142
			goto out;
143
		case Incomplete:
144
			break;
145
		case TempFail:
146
		case PermFail:
147
			continue;
148
		}
149
 
150
		if(up != nil){
151
			sendrequest("PASS", up->passwd);
152
		} else {
153
			if(getpassword(pass, pass+sizeof(pass)) < 0)
154
				exits(0);
155
			sendrequest("PASS", pass);
156
		}
157
		if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){
158
			if(strstr(msg, "Sess#"))
159
				defos = MVS;
160
			break;
161
		}
162
	}
163
out:
164
	if(up != nil){
165
		memset(up, 0, sizeof(*up));
166
		free(up);
167
	}
168
}
169
 
170
/*
171
 *  login to remote system with given user name and password.
172
 */
173
void
174
clogin(char *cuser, char *cpassword)
175
{
176
	free(user);
177
	user = strdup(cuser);
178
	if (strcmp(user, "anonymous") != 0 &&
179
	    strcmp(user, "ftp") != 0)
180
		fatal("User must be 'anonymous' or 'ftp'");
181
	sendrequest("USER", user);
182
	switch(getreply(&ctlin, msg, sizeof(msg), 1)){
183
	case Success:
184
		return;
185
	case Incomplete:
186
		break;
187
	case TempFail:
188
	case PermFail:
189
		fatal("login failed");
190
	}
191
	if (cpassword == 0)
192
		fatal("password needed");
193
	sendrequest("PASS", cpassword);
194
	if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
195
		fatal("password failed");
196
	if(strstr(msg, "Sess#"))
197
		defos = MVS;
198
	return;
199
}
200
 
201
/*
202
 *  find out about the other side.  go to it's root if requested.  set
203
 *  image mode if a Plan9 system.
204
 */
205
void
206
preamble(char *mountroot)
207
{
208
	char *p, *ep;
209
	int rv;
210
	OS *o;
211
 
212
	/*
213
	 *  create a root directory mirror
214
	 */
215
	remroot = newnode(0, s_copy("/"));
216
	remroot->d->qid.type = QTDIR;
217
	remroot->d->mode = DMDIR|0777;
218
	remdir = remroot;
219
 
220
	/*
221
	 *  get system type
222
	 */
223
	sendrequest("SYST", nil);
224
	switch(getreply(&ctlin, msg, sizeof(msg), 1)){
225
	case Success:
226
		for(o = oslist; o->os != Unknown; o++)
227
			if(strncmp(msg+4, o->name, strlen(o->name)) == 0)
228
				break;
229
		os = o->os;
230
		if(os == NT)
231
			os = Unix;
232
		break;
233
	default:
234
		os = defos;
235
		break;
236
	}
237
	if(os == Unknown)
238
		os = defos;
239
 
240
	remrootpath = s_reset(remrootpath);
241
	switch(os){
242
	case NetWare:
243
              /*
244
               * Request long, rather than 8.3 filenames,
245
               * where the Servers & Volume support them.
246
               */
247
              sendrequest("SITE LONG", nil);
248
              getreply(&ctlin, msg, sizeof(msg), 0);
249
              /* FALL THRU */
250
	case Unix:
251
	case Plan9:
252
		/*
253
		 *  go to the remote root, if asked
254
		 */
255
		if(mountroot){
256
			sendrequest("CWD", mountroot);
257
			getreply(&ctlin, msg, sizeof(msg), 0);
258
		} else {
259
			s_append(remrootpath, "/usr/");
260
			s_append(remrootpath, user);
261
		}
262
 
263
		/*
264
		 *  get the root directory
265
		 */
266
		sendrequest("PWD", nil);
267
		rv = getreply(&ctlin, msg, sizeof(msg), 1);
268
		if(rv == PermFail){
269
			sendrequest("XPWD", nil);
270
			rv = getreply(&ctlin, msg, sizeof(msg), 1);
271
		}
272
		if(rv == Success){
273
			p = strchr(msg, '"');
274
			if(p){
275
				p++;
276
				ep = strchr(p, '"');
277
				if(ep){
278
					*ep = 0;
279
					s_append(s_reset(remrootpath), p);
280
				}
281
			}
282
		}
283
 
284
		break;
285
	case Tops:
286
	case VM:
287
		/*
288
		 *  top directory is a figment of our imagination.
289
		 *  make it permanently cached & valid.
290
		 */
291
		CACHED(remroot);
292
		VALID(remroot);
293
		remroot->d->atime = time(0) + 100000;
294
 
295
		/*
296
		 *  no initial directory.  We are in the
297
		 *  imaginary root.
298
		 */
299
		remdir = newtopsdir("???");
300
		topsdir[0] = 0;
301
		if(os == Tops && readdir(remdir) >= 0){
302
			CACHED(remdir);
303
			if(*topsdir)
304
				remdir->remname = s_copy(topsdir);
305
			VALID(remdir);
306
		}
307
		break;
308
	case VMS:
309
		/*
310
		 *  top directory is a figment of our imagination.
311
		 *  make it permanently cached & valid.
312
		 */
313
		CACHED(remroot);
314
		VALID(remroot);
315
		remroot->d->atime = time(0) + 100000;
316
 
317
		/*
318
		 *  get current directory
319
		 */
320
		sendrequest("PWD", nil);
321
		rv = getreply(&ctlin, msg, sizeof(msg), 1);
322
		if(rv == PermFail){
323
			sendrequest("XPWD", nil);
324
			rv = getreply(&ctlin, msg, sizeof(msg), 1);
325
		}
326
		if(rv == Success){
327
			p = strchr(msg, '"');
328
			if(p){
329
				p++;
330
				ep = strchr(p, '"');
331
				if(ep){
332
					*ep = 0;
333
					remroot = remdir = vmsdir(p);
334
				}
335
			}
336
		}
337
		break;
338
	case MVS:
339
		usenlst = 1;
340
		break;
341
	}
342
 
343
	if(os == Plan9)
344
		image();
345
}
346
 
347
static void
348
ascii(void)
349
{
350
	sendrequest("TYPE A", nil);
351
	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
352
	case Success:
353
		break;
354
	default:
355
		fatal("can't set type to ascii");
356
	}
357
}
358
 
359
static void
360
image(void)
361
{
362
	sendrequest("TYPE I", nil);
363
	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
364
	case Success:
365
		break;
366
	default:
367
		fatal("can't set type to image/binary");
368
	}
369
}
370
 
371
/*
372
 *  decode the time fields, return seconds since epoch began
373
 */
374
char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
375
static Tm now;
376
 
377
static ulong
378
cracktime(char *month, char *day, char *yr, char *hms)
379
{
380
	Tm tm;
381
	int i;
382
	char *p;
383
 
384
 
385
	/* default time */
386
	if(now.year == 0)
387
		now = *localtime(time(0));
388
	tm = now;
389
	tm.yday = 0;
390
 
391
	/* convert ascii month to a number twixt 1 and 12 */
392
	if(*month >= '0' && *month <= '9'){
393
		tm.mon = atoi(month) - 1;
394
		if(tm.mon < 0 || tm.mon > 11)
395
			tm.mon = 5;
396
	} else {
397
		for(p = month; *p; p++)
398
			*p = tolower(*p);
399
		for(i = 0; i < 12; i++)
400
			if(strncmp(&monthchars[i*3], month, 3) == 0){
401
				tm.mon = i;
402
				break;
403
			}
404
	}
405
 
406
	tm.mday = atoi(day);
407
 
408
	if(hms){
409
		tm.hour = strtol(hms, &p, 0);
410
		if(*p == ':'){
411
			tm.min = strtol(p+1, &p, 0);
412
			if(*p == ':')
413
				tm.sec = strtol(p+1, &p, 0);
414
		}
415
		if(tolower(*p) == 'p')
416
			tm.hour += 12;
417
	}
418
 
419
	if(yr){
420
		tm.year = atoi(yr);
421
		if(tm.year >= 1900)
422
			tm.year -= 1900;
423
	} else {
424
		if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
425
			tm.year--;
426
	}
427
 
428
	/* convert to epoch seconds */
429
	return tm2sec(&tm);
430
}
431
 
432
/*
433
 *  decode a Unix or Plan 9 file mode
434
 */
435
static ulong
436
crackmode(char *p)
437
{
438
	ulong flags;
439
	ulong mode;
440
	int i;
441
 
442
	flags = 0;
443
	switch(strlen(p)){
444
	case 10:	/* unix and new style plan 9 */
445
		switch(*p){
446
		case 'l':
447
			return DMSYML|0777;
448
		case 'd':
449
			flags |= DMDIR;
450
		case 'a':
451
			flags |= DMAPPEND;
452
		}
453
		p++;
454
		if(p[2] == 'l')
455
			flags |= DMEXCL;
456
		break;
457
	case 11:	/* old style plan 9 */
458
		switch(*p++){
459
		case 'd':
460
			flags |= DMDIR;
461
			break;
462
		case 'a':
463
			flags |= DMAPPEND;
464
			break;
465
		}
466
		if(*p++ == 'l')
467
			flags |= DMEXCL;
468
		break;
469
	default:
470
		return DMDIR|0777;
471
	}
472
	mode = 0;
473
	for(i = 0; i < 3; i++){
474
		mode <<= 3;
475
		if(*p++ == 'r')
476
			mode |= DMREAD;
477
		if(*p++ == 'w')
478
			mode |= DMWRITE;
479
		if(*p == 'x' || *p == 's' || *p == 'S')
480
			mode |= DMEXEC;
481
		p++;
482
	}
483
	return mode | flags;
484
}
485
 
486
/*
487
 *  find first punctuation
488
 */
489
char*
490
strpunct(char *p)
491
{
492
	int c;
493
 
494
	for(;c = *p; p++){
495
		if(ispunct(c))
496
			return p;
497
	}
498
	return 0;
499
}
500
 
501
/*
502
 *  decode a Unix or Plan 9 directory listing
503
 */
504
static Dir*
505
crackdir(char *p, String **remname)
506
{
507
	char *field[15];
508
	char *dfield[4];
509
	char *cp;
510
	String *s;
511
	int dn, n;
512
	Dir d, *dp;
513
 
514
	memset(&d, 0, sizeof(d));
515
 
516
	n = getfields(p, field, 15, 1, " \t");
517
	if(n > 2 && strcmp(field[n-2], "->") == 0)
518
		n -= 2;
519
	switch(os){
520
	case TSO:
521
		cp = strchr(field[0], '.');
522
		if(cp){
523
			*cp++ = 0;
524
			s = s_copy(cp);
525
			d.uid = field[0];
526
		} else {
527
			s = s_copy(field[0]);
528
			d.uid = "TSO";
529
		}
530
		d.gid = "TSO";
531
		d.mode = 0666;
532
		d.length = 0;
533
		d.atime = 0;
534
		break;
535
	case OS½:
536
		s = s_copy(field[n-1]);
537
		d.uid = "OS½";
538
		d.gid = d.uid;
539
		d.mode = 0666;
540
		switch(n){
541
		case 5:
542
			if(strcmp(field[1], "DIR") == 0)
543
				d.mode |= DMDIR;
544
			d.length = atoll(field[0]);
545
			dn = getfields(field[2], dfield, 4, 1, "-");
546
			if(dn == 3)
547
				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]);
548
			break;
549
		}
550
		break;
551
	case Tops:
552
		if(n != 4){ /* tops directory name */
553
			safecpy(topsdir, field[0], sizeof(topsdir));
554
			return 0;
555
		}
556
		s = s_copy(field[3]);
557
		d.length = atoll(field[0]);
558
		d.mode = 0666;
559
		d.uid = "Tops";
560
		d.gid = d.uid;
561
		dn = getfields(field[1], dfield, 4, 1, "-");
562
		if(dn == 3)
563
			d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]);
564
		else
565
			d.atime = time(0);
566
		break;
567
	case VM:
568
		switch(n){
569
		case 9:
570
			s = s_copy(field[0]);
571
			s_append(s, ".");
572
			s_append(s, field[1]);
573
			d.length = atoll(field[3]) * atoll(field[4]);
574
			if(*field[2] == 'F')
575
				d.mode = 0666;
576
			else
577
				d.mode = 0777;
578
			d.uid = "VM";
579
			d.gid = d.uid;
580
			dn = getfields(field[6], dfield, 4, 1, "/-");
581
			if(dn == 3)
582
				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]);
583
			else
584
				d.atime = time(0);
585
			break;
586
		case 1:
587
			s = s_copy(field[0]);
588
			d.uid = "VM";
589
			d.gid = d.uid;
590
			d.mode = 0777;
591
			d.atime = 0;
592
			break;
593
		default:
594
			return nil;
595
		}
596
		break;
597
	case VMS:
598
		switch(n){
599
		case 6:
600
			for(cp = field[0]; *cp; cp++)
601
				*cp = tolower(*cp);
602
			cp = strchr(field[0], ';');
603
			if(cp)
604
				*cp = 0;
605
			d.mode = 0666;
606
			cp = field[0] + strlen(field[0]) - 4;
607
			if(strcmp(cp, ".dir") == 0){
608
				d.mode |= DMDIR;
609
				*cp = 0;
610
			}
611
			s = s_copy(field[0]);
612
			d.length = atoll(field[1]) * 512;
613
			field[4][strlen(field[4])-1] = 0;
614
			d.uid = field[4]+1;
615
			d.gid = d.uid;
616
			dn = getfields(field[2], dfield, 4, 1, "/-");
617
			if(dn == 3)
618
				d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]);
619
			else
620
				d.atime = time(0);
621
			break;
622
		default:
623
			return nil;
624
		}
625
		break;
626
	case NetWare:
627
		switch(n){
628
		case 8:		/* New style */
629
			s = s_copy(field[7]);
630
			d.uid = field[2];
631
			d.gid = d.uid;
632
			d.mode = nw_mode(field[0][0], field[1]);
633
			d.length = atoll(field[3]);
634
			if(strchr(field[6], ':'))
635
				d.atime = cracktime(field[4], field[5], nil, field[6]);
636
			else
637
				d.atime = cracktime(field[4], field[5], field[6], nil);
638
			break;
639
		case 9:
640
			s = s_copy(field[8]);
641
			d.uid = field[2];
642
			d.gid = d.uid;
643
			d.mode = 0666;
644
			if(*field[0] == 'd')
645
				d.mode |= DMDIR;
646
			d.length = atoll(field[3]);
647
			d.atime = cracktime(field[4], field[5], field[6], field[7]);
648
			break;
649
		case 1:
650
			s = s_copy(field[0]);
651
			d.uid = "none";
652
			d.gid = d.uid;
653
			d.mode = 0777;
654
			d.atime = 0;
655
			break;
656
		default:
657
			return nil;
658
		}
659
		break;
660
	case Unix:
661
	case Plan9:
662
	default:
663
		switch(n){
664
		case 8:		/* ls -l */
665
			s = s_copy(field[7]);
666
			d.uid = field[2];
667
			d.gid = d.uid;
668
			d.mode = crackmode(field[0]);
669
			d.length = atoll(field[3]);
670
			if(strchr(field[6], ':'))
671
				d.atime = cracktime(field[4], field[5], 0, field[6]);
672
			else
673
				d.atime = cracktime(field[4], field[5], field[6], 0);
674
			break;
675
		case 9:		/* ls -lg */
676
			s = s_copy(field[8]);
677
			d.uid = field[2];
678
			d.gid = field[3];
679
			d.mode = crackmode(field[0]);
680
			d.length = atoll(field[4]);
681
			if(strchr(field[7], ':'))
682
				d.atime = cracktime(field[5], field[6], 0, field[7]);
683
			else
684
				d.atime = cracktime(field[5], field[6], field[7], 0);
685
			break;
686
		case 10:	/* plan 9 */
687
			s = s_copy(field[9]);
688
			d.uid = field[3];
689
			d.gid = field[4];
690
			d.mode = crackmode(field[0]);
691
			d.length = atoll(field[5]);
692
			if(strchr(field[8], ':'))
693
				d.atime = cracktime(field[6], field[7], 0, field[8]);
694
			else
695
				d.atime = cracktime(field[6], field[7], field[8], 0);
696
			break;
697
		case 4:		/* a Windows_NT version */
698
			s = s_copy(field[3]);
699
			d.uid = "NT";
700
			d.gid = d.uid;
701
			if(strcmp("<DIR>", field[2]) == 0){
702
				d.length = 0;
703
				d.mode = DMDIR|0777;
704
			} else {
705
				d.mode = 0666;
706
				d.length = atoll(field[2]);
707
			}
708
			dn = getfields(field[0], dfield, 4, 1, "/-");
709
			if(dn == 3)
710
				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]);
711
			break;
712
		case 1:
713
			s = s_copy(field[0]);
714
			d.uid = "none";
715
			d.gid = d.uid;
716
			d.mode = 0777;
717
			d.atime = 0;
718
			break;
719
		default:
720
			return nil;
721
		}
722
	}
723
	d.muid = d.uid;
724
	d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE;
725
	d.mtime = d.atime;
726
	if(ext && (d.qid.type & QTDIR) == 0)
727
		s_append(s, ext);
728
	d.name = s_to_c(s);
729
 
730
	/* allocate a freeable dir structure */
731
	dp = reallocdir(&d, 0);
732
	*remname = s;
733
 
734
	return dp;
735
}
736
 
737
/*
738
 *  probe files in a directory to see if they are directories
739
 */
740
/*
741
 *  read a remote directory
742
 */
743
int
744
readdir(Node *node)
745
{
746
	Biobuf *bp;
747
	char *line;
748
	Node *np;
749
	Dir *d;
750
	long n;
751
	int tries, x, files;
752
	static int uselist;
753
	int usenlist;
754
	String *remname;
755
 
756
	if(changedir(node) < 0)
757
		return -1;
758
 
759
	usenlist = 0;
760
	for(tries = 0; tries < 3; tries++){
761
		if(usenlist || usenlst)
762
			x = data(OREAD, &bp, "NLST", nil);
763
		else if(os == Unix && !uselist)
764
			x = data(OREAD, &bp, "LIST -l", nil);
765
		else
766
			x = data(OREAD, &bp, "LIST", nil);
767
		switch(x){
768
		case Extra:
769
			break;
770
/*		case TempFail:
771
			continue;
772
*/
773
		default:
774
			if(os == Unix && uselist == 0){
775
				uselist = 1;
776
				continue;
777
			}
778
			return seterr(nosuchfile);
779
		}
780
		files = 0;
781
		while(line = Brdline(bp, '\n')){
782
			n = Blinelen(bp);
783
			if(debug)
784
				write(2, line, n);
785
			if(n > 1 && line[n-2] == '\r')
786
				n--;
787
			line[n - 1] = 0;
788
 
789
			d = crackdir(line, &remname);
790
			if(d == nil)
791
				continue;
792
			files++;
793
			np = extendpath(node, remname);
794
			d->qid.path = np->d->qid.path;
795
			d->qid.vers = np->d->qid.vers;
796
			d->type = np->d->type;
797
			d->dev = 1;			/* mark node as valid */
798
			if(os == MVS && node == remroot){
799
				d->qid.type = QTDIR;
800
				d->mode |= DMDIR;
801
			}
802
			free(np->d);
803
			np->d = d;
804
		}
805
		close(Bfildes(bp));
806
 
807
		switch(getreply(&ctlin, msg, sizeof(msg), 0)){
808
		case Success:
809
			if(files == 0 && !usenlst && !usenlist){
810
				usenlist = 1;
811
				continue;
812
			}
813
			if(files && usenlist)
814
				usenlst = 1;
815
			if(usenlst)
816
				node->chdirunknown = 1;
817
			return 0;
818
		case TempFail:
819
			break;
820
		default:
821
			return seterr(nosuchfile);
822
		}
823
	}
824
	return seterr(nosuchfile);
825
}
826
 
827
/*
828
 *  create a remote directory
829
 */
830
int
831
createdir(Node *node)
832
{
833
	if(changedir(node->parent) < 0)
834
		return -1;
835
 
836
	sendrequest("MKD", node->d->name);
837
	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
838
		return -1;
839
	return 0;
840
}
841
 
842
/*
843
 *  change to a remote directory.
844
 */
845
int
846
changedir(Node *node)
847
{
848
	Node *to;
849
	String *cdpath;
850
 
851
	to = node;
852
	if(to == remdir)
853
		return 0;
854
 
855
	/* build an absolute path */
856
	switch(os){
857
	case Tops:
858
	case VM:
859
		switch(node->depth){
860
		case 0:
861
			remdir = node;
862
			return 0;
863
		case 1:
864
			cdpath = s_clone(node->remname);
865
			break;
866
		default:
867
			return seterr(nosuchfile);
868
		}
869
		break;
870
	case VMS:
871
		switch(node->depth){
872
		case 0:
873
			remdir = node;
874
			return 0;
875
		default:
876
			cdpath = s_new();
877
			vmspath(node, cdpath);
878
		}
879
		break;
880
	case MVS:
881
		if(node->depth == 0)
882
			cdpath = s_clone(remrootpath);
883
		else{
884
			cdpath = s_new();
885
			mvspath(node, cdpath);
886
		}
887
		break;
888
	default:
889
		if(node->depth == 0)
890
			cdpath = s_clone(remrootpath);
891
		else{
892
			cdpath = s_new();
893
			unixpath(node, cdpath);
894
		}
895
		break;
896
	}
897
 
898
	uncachedir(remdir, 0);
899
 
900
	/*
901
	 *  connect, if we need a password (Incomplete)
902
	 *  act like it worked (best we can do).
903
	 */
904
	sendrequest("CWD", s_to_c(cdpath));
905
	s_free(cdpath);
906
	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
907
	case Success:
908
	case Incomplete:
909
		remdir = node;
910
		return 0;
911
	default:
912
		return seterr(nosuchfile);
913
	}
914
}
915
 
916
/*
917
 *  read a remote file
918
 */
919
int
920
readfile1(Node *node)
921
{
922
	Biobuf *bp;
923
	char buf[4*1024];
924
	long off;
925
	int n;
926
	int tries;
927
 
928
	if(changedir(node->parent) < 0)
929
		return -1;
930
 
931
	for(tries = 0; tries < 4; tries++){
932
		switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){
933
		case Extra:
934
			break;
935
		case TempFail:
936
			continue;
937
		default:
938
			return seterr(nosuchfile);
939
		}
940
		off = 0;
941
		while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){
942
			if(filewrite(node, buf, off, n) != n){
943
				off = -1;
944
				break;
945
			}
946
			off += n;
947
		}
948
		if(off < 0)
949
			return -1;
950
 
951
		/* make sure a file gets created even for a zero length file */
952
		if(off == 0)
953
			filewrite(node, buf, 0, 0);
954
 
955
		close(Bfildes(bp));
956
		switch(getreply(&ctlin, msg, sizeof(msg), 0)){
957
		case Success:
958
			return off;
959
		case TempFail:
960
			continue;
961
		default:
962
			return seterr(nosuchfile);
963
		}
964
	}
965
	return seterr(nosuchfile);
966
}
967
 
968
int
969
readfile(Node *node)
970
{
971
	int rv, inimage;
972
 
973
	switch(os){
974
	case MVS:
975
	case Plan9:
976
	case Tops:
977
	case TSO:
978
		inimage = 0;
979
		break;
980
	default:
981
		inimage = 1;
982
		image();
983
		break;
984
	}
985
 
986
	rv = readfile1(node);
987
 
988
	if(inimage)
989
		ascii();
990
	return rv;
991
}
992
 
993
/*
994
 *  write back a file
995
 */
996
int
997
createfile1(Node *node)
998
{
999
	Biobuf *bp;
1000
	char buf[4*1024];
1001
	long off;
1002
	int n;
1003
 
1004
	if(changedir(node->parent) < 0)
1005
		return -1;
1006
 
1007
	if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra)
1008
		return -1;
1009
	for(off = 0; ; off += n){
1010
		n = fileread(node, buf, off, sizeof(buf));
1011
		if(n <= 0)
1012
			break;
1013
		write(Bfildes(bp), buf, n);
1014
	}
1015
	close(Bfildes(bp));
1016
	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1017
		return -1;
1018
	return off;
1019
}
1020
 
1021
int
1022
createfile(Node *node)
1023
{
1024
	int rv;
1025
 
1026
	switch(os){
1027
	case Plan9:
1028
	case Tops:
1029
		break;
1030
	default:
1031
		image();
1032
		break;
1033
	}
1034
	rv = createfile1(node);
1035
	switch(os){
1036
	case Plan9:
1037
	case Tops:
1038
		break;
1039
	default:
1040
		ascii();
1041
		break;
1042
	}
1043
	return rv;
1044
}
1045
 
1046
/*
1047
 *  remove a remote file
1048
 */
1049
int
1050
removefile(Node *node)
1051
{
1052
	if(changedir(node->parent) < 0)
1053
		return -1;
1054
 
1055
	sendrequest("DELE", s_to_c(node->remname));
1056
	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1057
		return -1;
1058
	return 0;
1059
}
1060
 
1061
/*
1062
 *  remove a remote directory
1063
 */
1064
int
1065
removedir(Node *node)
1066
{
1067
	if(changedir(node->parent) < 0)
1068
		return -1;
1069
 
1070
	sendrequest("RMD", s_to_c(node->remname));
1071
	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1072
		return -1;
1073
	return 0;
1074
}
1075
 
1076
/*
1077
 *  tell remote that we're exiting and then do it
1078
 */
1079
void
1080
quit(void)
1081
{
1082
	sendrequest("QUIT", nil);
1083
	getreply(&ctlin, msg, sizeof(msg), 0);
1084
	exits(0);
1085
}
1086
 
1087
/*
1088
 *  send a request
1089
 */
1090
static void
1091
sendrequest(char *a, char *b)
1092
{
1093
	char buf[2*1024];
1094
	int n;
1095
 
1096
	n = strlen(a)+2+1;
1097
	if(b != nil)
1098
		n += strlen(b)+1;
1099
	if(n >= sizeof(buf))
1100
		fatal("proto request too long");
1101
	strcpy(buf, a);
1102
	if(b != nil){
1103
		strcat(buf, " ");
1104
		strcat(buf, b);
1105
	}
1106
	strcat(buf, "\r\n");
1107
	n = strlen(buf);
1108
	if(write(ctlfd, buf, n) != n)
1109
		fatal("remote side hung up");
1110
	if(debug)
1111
		write(2, buf, n);
1112
	lastsend = time(0);
1113
}
1114
 
1115
/*
1116
 *  replies codes are in the range [100, 999] and may contain multiple lines of
1117
 *  continuation.
1118
 */
1119
static int
1120
getreply(Biobuf *bp, char *msg, int len, int printreply)
1121
{
1122
	char *line;
1123
	char *p;
1124
	int rv;
1125
	int i, n;
1126
 
1127
	while(line = Brdline(bp, '\n')){
1128
		/* add line to message buffer, strip off \r */
1129
		n = Blinelen(bp);
1130
		if(n > 1 && line[n-2] == '\r'){
1131
			n--;
1132
			line[n-1] = '\n';
1133
		}
1134
		if(printreply && !quiet)
1135
			write(1, line, n);
1136
		else if(debug)
1137
			write(2, line, n);
1138
		if(n > len - 1)
1139
			i = len - 1;
1140
		else
1141
			i = n;
1142
		if(i > 0){
1143
			memmove(msg, line, i);
1144
			msg += i;
1145
			len -= i;
1146
			*msg = 0;
1147
		}
1148
 
1149
		/* stop if not a continuation */
1150
		rv = strtol(line, &p, 10);
1151
		if(rv >= 100 && rv < 600 && p==line+3 && *p != '-')
1152
			return rv/100;
1153
 
1154
		/* tell user about continuations */
1155
		if(!debug && !quiet && !printreply)
1156
			write(2, line, n);
1157
	}
1158
 
1159
	fatal("remote side closed connection");
1160
	return 0;
1161
}
1162
 
1163
/*
1164
 *  Announce on a local port and tell its address to the the remote side
1165
 */
1166
static int
1167
port(void)
1168
{
1169
	char buf[256];
1170
	int n, fd;
1171
	char *ptr;
1172
	uchar ipaddr[IPaddrlen];
1173
	int port;
1174
 
1175
	/* get a channel to listen on, let kernel pick the port number */
1176
	sprint(buf, "%s!*!0", net);
1177
	listenfd = announce(buf, netdir);
1178
	if(listenfd < 0)
1179
		return seterr("can't announce");
1180
 
1181
	/* get the local address and port number */
1182
	sprint(buf, "%s/local", netdir);
1183
	fd = open(buf, OREAD);
1184
	if(fd < 0)
1185
		return seterr("opening %s: %r", buf);
1186
	n = read(fd, buf, sizeof(buf)-1);
1187
	close(fd);
1188
	if(n <= 0)
1189
		return seterr("opening %s/local: %r", netdir);
1190
	buf[n] = 0;
1191
	ptr = strchr(buf, ' ');
1192
	if(ptr)
1193
		*ptr = 0;
1194
	ptr = strchr(buf, '!')+1;
1195
	port = atoi(ptr);
1196
 
1197
	memset(ipaddr, 0, IPaddrlen);
1198
	if (*net){
1199
		strcpy(buf, net);
1200
		ptr = strchr(buf +1, '/');
1201
		if (ptr)
1202
			*ptr = 0;
1203
		myipaddr(ipaddr, buf);
1204
	}
1205
 
1206
	/* tell remote side */
1207
	sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1],
1208
		ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff);
1209
	sendrequest(buf, nil);
1210
	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
1211
		return seterr(msg);
1212
	return 0;
1213
}
1214
 
1215
/*
1216
 *  have server call back for a data connection
1217
 */
1218
static int
1219
active(int mode, Biobuf **bpp, char *cmda, char *cmdb)
1220
{
1221
	int cfd, dfd, rv;
1222
	char newdir[Maxpath];
1223
	char datafile[Maxpath + 6];
1224
	TLSconn conn;
1225
 
1226
	if(port() < 0)
1227
		return TempFail;
1228
 
1229
	sendrequest(cmda, cmdb);
1230
 
1231
	rv = getreply(&ctlin, msg, sizeof(msg), 0);
1232
	if(rv != Extra){
1233
		close(listenfd);
1234
		return rv;
1235
	}
1236
 
1237
	/* wait for a new call */
1238
	cfd = listen(netdir, newdir);
1239
	if(cfd < 0)
1240
		fatal("waiting for data connection");
1241
	close(listenfd);
1242
 
1243
	/* open it's data connection and close the control connection */
1244
	sprint(datafile, "%s/data", newdir);
1245
	dfd = open(datafile, ORDWR);
1246
	close(cfd);
1247
	if(dfd < 0)
1248
		fatal("opening data connection");
1249
 
1250
	if(usetls){
1251
		memset(&conn, 0, sizeof(conn));
1252
		dfd = tlsClient(dfd, &conn);
1253
		if(dfd < 0)
1254
			fatal("starting tls: %r");
1255
		free(conn.cert);
1256
	}
1257
 
1258
	Binit(&dbuf, dfd, mode);
1259
	*bpp = &dbuf;
1260
	return Extra;
1261
}
1262
 
1263
/*
1264
 *  call out for a data connection
1265
 */
1266
static int
1267
passive(int mode, Biobuf **bpp, char *cmda, char *cmdb)
1268
{
1269
	char msg[1024];
1270
	char ds[1024];
1271
	char *f[6];
1272
	char *p;
1273
	int x, fd;
1274
	TLSconn conn;
1275
 
1276
	if(nopassive)
1277
		return Impossible;
1278
 
1279
	sendrequest("PASV", nil);
1280
	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){
1281
		nopassive = 1;
1282
		return Impossible;
1283
	}
1284
 
1285
	/* get address and port number from reply, this is AI */
1286
	p = strchr(msg, '(');
1287
	if(p == 0){
1288
		for(p = msg+3; *p; p++)
1289
			if(isdigit(*p))
1290
				break;
1291
	} else
1292
		p++;
1293
	if(getfields(p, f, 6, 0, ",") < 6){
1294
		if(debug)
1295
			fprint(2, "passive mode protocol botch: %s\n", msg);
1296
		werrstr("ftp protocol botch");
1297
		nopassive = 1;
1298
		return Impossible;
1299
	}
1300
	snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net,
1301
		f[0], f[1], f[2], f[3],
1302
		((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff));
1303
 
1304
	/* open data connection */
1305
	fd = dial(ds, 0, 0, 0);
1306
	if(fd < 0){
1307
		if(debug)
1308
			fprint(2, "passive mode connect to %s failed: %r\n", ds);
1309
		nopassive = 1;
1310
		return TempFail;
1311
	}
1312
 
1313
	/* tell remote to send a file */
1314
	sendrequest(cmda, cmdb);
1315
	x = getreply(&ctlin, msg, sizeof(msg), 0);
1316
	if(x != Extra){
1317
		close(fd);
1318
		if(debug)
1319
			fprint(2, "passive mode retrieve failed: %s\n", msg);
1320
		werrstr(msg);
1321
		return x;
1322
	}
1323
 
1324
	if(usetls){
1325
		memset(&conn, 0, sizeof(conn));
1326
		fd = tlsClient(fd, &conn);
1327
		if(fd < 0)
1328
			fatal("starting tls: %r");
1329
		free(conn.cert);
1330
	}
1331
	Binit(&dbuf, fd, mode);
1332
 
1333
	*bpp = &dbuf;
1334
	return Extra;
1335
}
1336
 
1337
static int
1338
data(int mode, Biobuf **bpp, char* cmda, char *cmdb)
1339
{
1340
	int x;
1341
 
1342
	x = passive(mode, bpp, cmda, cmdb);
1343
	if(x != Impossible)
1344
		return x;
1345
	return active(mode, bpp, cmda, cmdb);
1346
}
1347
 
1348
/*
1349
 *  used for keep alives
1350
 */
1351
void
1352
nop(void)
1353
{
1354
	if(lastsend - time(0) < 15)
1355
		return;
1356
	sendrequest("PWD", nil);
1357
	getreply(&ctlin, msg, sizeof(msg), 0);
1358
}
1359
 
1360
/*
1361
 *  turn a vms spec into a path
1362
 */
1363
static Node*
1364
vmsextendpath(Node *np, char *name)
1365
{
1366
	np = extendpath(np, s_copy(name));
1367
	if(!ISVALID(np)){
1368
		np->d->qid.type = QTDIR;
1369
		np->d->atime = time(0);
1370
		np->d->mtime = np->d->atime;
1371
		strcpy(np->d->uid, "who");
1372
		strcpy(np->d->gid, "cares");
1373
		np->d->mode = DMDIR|0777;
1374
		np->d->length = 0;
1375
		if(changedir(np) >= 0)
1376
			VALID(np);
1377
	}
1378
	return np;
1379
}
1380
static Node*
1381
vmsdir(char *name)
1382
{
1383
	char *cp;
1384
	Node *np;
1385
	char *oname;
1386
 
1387
	np = remroot;
1388
	cp = strchr(name, '[');
1389
	if(cp)
1390
		strcpy(cp, cp+1);
1391
	cp = strchr(name, ']');
1392
	if(cp)
1393
		*cp = 0;
1394
	oname = name = strdup(name);
1395
	if(name == 0)
1396
		return 0;
1397
 
1398
	while(cp = strchr(name, '.')){
1399
		*cp = 0;
1400
		np = vmsextendpath(np, name);
1401
		name = cp+1;
1402
	}
1403
	np = vmsextendpath(np, name);
1404
 
1405
	/*
1406
	 *  walk back to first accessible directory
1407
	 */
1408
	for(; np->parent != np; np = np->parent)
1409
		if(ISVALID(np)){
1410
			CACHED(np->parent);
1411
			break;
1412
		}
1413
 
1414
	free(oname);
1415
	return np;
1416
}
1417
 
1418
/*
1419
 *  walk up the tree building a VMS style path
1420
 */
1421
static void
1422
vmspath(Node *node, String *path)
1423
{
1424
	char *p;
1425
	int n;
1426
 
1427
	if(node->depth == 1){
1428
		p = strchr(s_to_c(node->remname), ':');
1429
		if(p){
1430
			n = p - s_to_c(node->remname) + 1;
1431
			s_nappend(path, s_to_c(node->remname), n);
1432
			s_append(path, "[");
1433
			s_append(path, p+1);
1434
		} else {
1435
			s_append(path, "[");
1436
			s_append(path, s_to_c(node->remname));
1437
		}
1438
		s_append(path, "]");
1439
		return;
1440
	}
1441
	vmspath(node->parent, path);
1442
	s_append(path, ".");
1443
	s_append(path, s_to_c(node->remname));
1444
}
1445
 
1446
/*
1447
 *  walk up the tree building a Unix style path
1448
 */
1449
static void
1450
unixpath(Node *node, String *path)
1451
{
1452
	if(node == node->parent){
1453
		s_append(path, s_to_c(remrootpath));
1454
		return;
1455
	}
1456
	unixpath(node->parent, path);
1457
	if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0)
1458
		s_append(path, "/");
1459
	s_append(path, s_to_c(node->remname));
1460
}
1461
 
1462
/*
1463
 *  walk up the tree building a MVS style path
1464
 */
1465
static void
1466
mvspath(Node *node, String *path)
1467
{
1468
	if(node == node->parent){
1469
		s_append(path, s_to_c(remrootpath));
1470
		return;
1471
	}
1472
	mvspath(node->parent, path);
1473
	if(s_len(path) > 0)
1474
		s_append(path, ".");
1475
	s_append(path, s_to_c(node->remname));
1476
}
1477
 
1478
static int
1479
getpassword(char *buf, char *e)
1480
{
1481
	char *p;
1482
	int c;
1483
	int consctl, rv = 0;
1484
 
1485
	consctl = open("/dev/consctl", OWRITE);
1486
	if(consctl >= 0)
1487
		write(consctl, "rawon", 5);
1488
	print("Password: ");
1489
	e--;
1490
	for(p = buf; p <= e; p++){
1491
		c = Bgetc(&stdin);
1492
		if(c < 0){
1493
			rv = -1;
1494
			goto out;
1495
		}
1496
		if(c == '\n' || c == '\r')
1497
			break;
1498
		*p = c;
1499
	}
1500
	*p = 0;
1501
	print("\n");
1502
 
1503
out:
1504
	if(consctl >= 0)
1505
		close(consctl);
1506
	return rv;
1507
}
1508
 
1509
/*
1510
 *  convert from latin1 to utf
1511
 */
1512
static char*
1513
fromlatin1(char *from)
1514
{
1515
	char *p, *to;
1516
	Rune r;
1517
 
1518
	if(os == Plan9)
1519
		return nil;
1520
 
1521
	/* don't convert if we don't have to */
1522
	for(p = from; *p; p++)
1523
		if(*p & 0x80)
1524
			break;
1525
	if(*p == 0)
1526
		return nil;
1527
 
1528
	to = malloc(3*strlen(from)+2);
1529
	if(to == nil)
1530
		return nil;
1531
	for(p = to; *from; from++){
1532
		r = (*from) & 0xff;
1533
		p += runetochar(p, &r);
1534
	}
1535
	*p = 0;
1536
	return to;
1537
}
1538
 
1539
Dir*
1540
reallocdir(Dir *d, int dofree)
1541
{
1542
	Dir *dp;
1543
	char *p;
1544
	int nn, ng, nu, nm;
1545
	char *utf;
1546
 
1547
	if(d->name == nil)
1548
		d->name = "?name?";
1549
	if(d->uid == nil)
1550
		d->uid = "?uid?";
1551
	if(d->gid == nil)
1552
		d->gid = d->uid;
1553
	if(d->muid == nil)
1554
		d->muid = d->uid;
1555
 
1556
	utf = fromlatin1(d->name);
1557
	if(utf != nil)
1558
		d->name = utf;
1559
 
1560
	nn = strlen(d->name)+1;
1561
	nu = strlen(d->uid)+1;
1562
	ng = strlen(d->gid)+1;
1563
	nm = strlen(d->muid)+1;
1564
	dp = malloc(sizeof(Dir)+nn+nu+ng+nm);
1565
	if(dp == nil){
1566
		if(dofree)
1567
			free(d);
1568
		if(utf != nil)
1569
			free(utf);
1570
		return nil;
1571
	}
1572
	*dp = *d;
1573
	p = (char*)&dp[1];
1574
	strcpy(p, d->name);
1575
	dp->name = p;
1576
	p += nn;
1577
	strcpy(p, d->uid);
1578
	dp->uid = p;
1579
	p += nu;
1580
	strcpy(p, d->gid);
1581
	dp->gid = p;
1582
	p += ng;
1583
	strcpy(p, d->muid);
1584
	dp->muid = p;
1585
	if(dofree)
1586
		free(d);
1587
	if(utf != nil)
1588
		free(utf);
1589
	return dp;
1590
}
1591
 
1592
Dir*
1593
dir_change_name(Dir *d, char *name)
1594
{
1595
	if(d->name && strlen(d->name) >= strlen(name)){
1596
		strcpy(d->name, name);
1597
		return d;
1598
	}
1599
	d->name = name;
1600
	return reallocdir(d, 1);
1601
}
1602
 
1603
Dir*
1604
dir_change_uid(Dir *d, char *name)
1605
{
1606
	if(d->uid && strlen(d->uid) >= strlen(name)){
1607
		strcpy(d->name, name);
1608
		return d;
1609
	}
1610
	d->uid = name;
1611
	return reallocdir(d, 1);
1612
}
1613
 
1614
Dir*
1615
dir_change_gid(Dir *d, char *name)
1616
{
1617
	if(d->gid && strlen(d->gid) >= strlen(name)){
1618
		strcpy(d->name, name);
1619
		return d;
1620
	}
1621
	d->gid = name;
1622
	return reallocdir(d, 1);
1623
}
1624
 
1625
Dir*
1626
dir_change_muid(Dir *d, char *name)
1627
{
1628
	if(d->muid && strlen(d->muid) >= strlen(name)){
1629
		strcpy(d->name, name);
1630
		return d;
1631
	}
1632
	d->muid = name;
1633
	return reallocdir(d, 1);
1634
}
1635
 
1636
static int
1637
nw_mode(char dirlet, char *s)		/* NetWare file mode mapping */
1638
{
1639
	int mode = 0777;
1640
 
1641
	if(dirlet == 'd')
1642
		mode |= DMDIR;
1643
 
1644
	if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']')
1645
		return(mode);
1646
 
1647
	if (s[1] == '-')					/* can't read file */
1648
		mode &= ~0444;
1649
	if (dirlet == 'd' && s[6] == '-')			/* cannot scan dir */
1650
		mode &= ~0444;
1651
	if (s[2] == '-')					/* can't write file */
1652
		mode &= ~0222;
1653
	if (dirlet == 'd' && s[7] == '-' && s[3] == '-')	/* cannot create in, or modify dir */
1654
		mode &= ~0222;
1655
 
1656
	return(mode);
1657
}