Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <fcall.h>
4
#include <thread.h>
5
#include <libsec.h>
6
#include <9p.h>
7
#include "cifs.h"
8
 
9
#define max(a,b)	(((a) > (b))? (a): (b))
10
#define min(a,b)	(((a) < (b))? (a): (b))
11
 
12
typedef struct Aux Aux;
13
struct Aux {
14
	Aux	*next;
15
	Aux	*prev;
16
	char	*path;		/* full path fo file */
17
	Share	*sp;		/* this share's info */
18
	long	expire;		/* expiration time of cache */
19
	long	off;		/* file pos of start of cache */
20
	long	end;		/* file pos of end of cache */
21
	char	*cache;
22
	int	fh;		/* file handle */
23
	int	sh;		/* search handle */
24
	long	srch;		/* find first's internal state */
25
};
26
 
27
extern int chatty9p;
28
 
29
int Checkcase = 1;		/* enforce case significance on filenames */
30
int Dfstout = 100;		/* timeout (in ms) for ping of dfs servers (assume they are local)  */
31
int Billtrog = 1;		/* enable file owner/group resolution */
32
int Attachpid;			/* pid of proc that attaches (ugh !) */
33
char *Debug = nil;		/* messages */
34
Qid Root;			/* root of remote system */
35
Share Ipc;			/* Share info of IPC$ share */
36
Session *Sess;			/* current session */
37
int Active = IDLE_TIME;		/* secs until next keepalive is sent */
38
static int Keeppid;		/* process ID of keepalive thread */
39
Share Shares[MAX_SHARES]; 	/* table of connected shares */
40
int Nshares = 0;		/* number of Shares connected */
41
Aux *Auxroot = nil;		/* linked list of Aux structs */
42
char *Host = nil;		/* host we are connected to */
43
 
44
static char *Ipcname = "IPC$";
45
 
46
#define ptype(x)	(((x) & 0xf))
47
#define pindex(x)	(((x) & 0xff0) >> 4)
48
 
49
void
50
setup(void)
51
{
52
	int fd;
53
	char buf[32];
54
 
55
	/*
56
	 * This is revolting but I cannot see any other way to get
57
	 * the pid of the server.  We need this as Windows doesn't
58
	 * drop the TCP connection when it closes a connection.
59
	 * Thus we keepalive() to detect when/if we are thrown off.
60
	 */
61
	Attachpid = getpid();
62
 
63
	snprint(buf, sizeof buf, "#p/%d/args", getpid());
64
	if((fd = open(buf, OWRITE)) >= 0){
65
		fprint(fd, "%s network", Host);
66
		close(fd);
67
	}
68
}
69
 
70
int
71
filetableinfo(Fmt *f)
72
{
73
	Aux *ap;
74
	char *type;
75
 
76
	if((ap = Auxroot) != nil)
77
		do{
78
			type = "walked";
79
			if(ap->sh != -1)
80
				type = "opendir";
81
			if(ap->fh != -1)
82
				type = "openfile";
83
			fmtprint(f, "%-9s %s\n", type, ap->path);
84
			ap = ap->next;
85
		}while(ap != Auxroot);
86
	return 0;
87
}
88
 
89
Qid
90
mkqid(char *s, int is_dir, long vers, int subtype, long path)
91
{
92
	Qid q;
93
	union {				/* align digest suitably */
94
		uchar	digest[SHA1dlen];
95
		uvlong	uvl;
96
	} u;
97
 
98
	sha1((uchar *)s, strlen(s), u.digest, nil);
99
	q.type = (is_dir)? QTDIR: 0;
100
	q.vers = vers;
101
	if(subtype){
102
		q.path = *((uvlong *)u.digest) & ~0xfffL;
103
		q.path |= ((path & 0xff) << 4);
104
		q.path |= (subtype & 0xf);
105
	}
106
	else
107
		q.path = *((uvlong *)u.digest) & ~0xfL;
108
	return q;
109
}
110
 
111
/*
112
 * used only for root dir and shares
113
 */
114
static void
115
V2D(Dir *d, Qid qid, char *name)
116
{
117
	memset(d, 0, sizeof(Dir));
118
	d->type = 'C';
119
	d->dev = 1;
120
	d->name = estrdup9p(name);
121
	d->uid = estrdup9p("bill");
122
	d->muid = estrdup9p("boyd");
123
	d->gid = estrdup9p("trog");
124
	d->mode = 0755 | DMDIR;
125
	d->atime = time(nil);
126
	d->mtime = d->atime;
127
	d->length = 0;
128
	d->qid = qid;
129
}
130
 
131
static void
132
I2D(Dir *d, Share *sp, char *path, FInfo *fi)
133
{
134
	char *name;
135
 
136
	if((name = strrchr(fi->name, '\\')) != nil)
137
		name++;
138
	else
139
		name = fi->name;
140
	d->name = estrdup9p(name);
141
	d->type = 'C';
142
	d->dev = sp->tid;
143
	d->uid = estrdup9p("bill");
144
	d->gid = estrdup9p("trog");
145
	d->muid = estrdup9p("boyd");
146
	d->atime = fi->accessed;
147
	d->mtime = fi->written;
148
 
149
	if(fi->attribs & ATTR_READONLY)
150
		d->mode = 0444;
151
	else
152
		d->mode = 0666;
153
 
154
	d->length = fi->size;
155
	d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0);
156
 
157
	if(fi->attribs & ATTR_DIRECTORY){
158
		d->length = 0;
159
		d->mode |= DMDIR|0111;
160
	}
161
}
162
 
163
static void
164
responderrstr(Req *r)
165
{
166
	char e[ERRMAX];
167
 
168
	*e = 0;
169
	rerrstr(e, sizeof e);
170
	respond(r, e);
171
}
172
 
173
static char *
174
newpath(char *path, char *name)
175
{
176
	char *p, *q;
177
 
178
	assert((p = strrchr(path, '/')) != nil);
179
 
180
	if(strcmp(name, "..") == 0){
181
		if(p == path)
182
			return estrdup9p("/");
183
		q = emalloc9p((p-path)+1);
184
		strecpy(q, q+(p-path)+1, path);
185
		return q;
186
	}
187
	if(strcmp(path, "/") == 0)
188
		return smprint("/%s", name);
189
	return smprint("%s/%s", path, name);
190
}
191
 
192
static int
193
dirgen(int slot, Dir *d, void *aux)
194
{
195
	long off;
196
	FInfo *fi;
197
	int rc, got;
198
	Aux *a = aux;
199
	char *npath;
200
	int numinf = numinfo();
201
	int slots = min(Sess->mtu, MTU) / sizeof(FInfo);
202
 
203
	if(strcmp(a->path, "/") == 0){
204
		if(slot < numinf){
205
			dirgeninfo(slot, d);
206
			return 0;
207
		} else
208
			slot -= numinf;
209
 
210
		if(slot >= Nshares)
211
			return -1;
212
		V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot),
213
			Shares[slot].name);
214
		return 0;
215
	}
216
 
217
	off = slot * sizeof(FInfo);
218
	if(off >= a->off && off < a->end && time(nil) < a->expire)
219
		goto from_cache;
220
 
221
	if(off == 0){
222
		fi = (FInfo *)a->cache;
223
		npath = smprint("%s/*", mapfile(a->path));
224
		a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch,
225
			(FInfo *)a->cache);
226
		free(npath);
227
		if(a->sh == -1)
228
			return -1;
229
 
230
		a->off = 0;
231
		a->end = got * sizeof(FInfo);
232
 
233
		if(got >= 2 && strcmp(fi[0].name, ".") == 0 &&
234
		    strcmp(fi[1].name, "..") == 0){
235
			a->end = (got - 2) * sizeof(FInfo);
236
			memmove(a->cache, a->cache + sizeof(FInfo)*2,
237
				a->end - a->off);
238
		}
239
	}
240
 
241
	while(off >= a->end && a->sh != -1){
242
		fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo));
243
		a->off = a->end;
244
		npath = smprint("%s/%s", mapfile(a->path), fi->name);
245
		rc = T2findnext(Sess, a->sp, slots, npath,
246
			&got, &a->srch, (FInfo *)a->cache, a->sh);
247
		free(npath);
248
		if(rc == -1 || got == 0)
249
			break;
250
		a->end = a->off + got * sizeof(FInfo);
251
	}
252
	a->expire = time(nil) + CACHETIME;
253
 
254
	if(got < slots){
255
		if(a->sh != -1)
256
			CIFSfindclose2(Sess, a->sp, a->sh);
257
		a->sh = -1;
258
	}
259
 
260
	if(off >= a->end)
261
		return -1;
262
 
263
from_cache:
264
	fi = (FInfo *)(a->cache + (off - a->off));
265
	npath = smprint("%s/%s", mapfile(a->path), fi->name);
266
	I2D(d, a->sp, npath, fi);
267
	if(Billtrog == 0)
268
		upd_names(Sess, a->sp, npath, d);
269
	free(npath);
270
	return 0;
271
}
272
 
273
static void
274
fsattach(Req *r)
275
{
276
	Aux *a;
277
	static int first = 1;
278
	char *spec = r->ifcall.aname;
279
 
280
	if(first)
281
		setup();
282
 
283
	if(spec && *spec){
284
		respond(r, "invalid attach specifier");
285
		return;
286
	}
287
 
288
	r->ofcall.qid = mkqid("/", 1, 1, Proot, 0);
289
	r->fid->qid = r->ofcall.qid;
290
 
291
	a = r->fid->aux = emalloc9p(sizeof(Aux));
292
	memset(a, 0, sizeof(Aux));
293
	a->path = estrdup9p("/");
294
	a->sp = nil;
295
	a->fh = -1;
296
	a->sh = -1;
297
 
298
	if(Auxroot){
299
		a->prev = Auxroot;
300
		a->next = Auxroot->next;
301
		Auxroot->next->prev = a;
302
		Auxroot->next = a;
303
	} else {
304
		Auxroot = a;
305
		a->next = a;
306
		a->prev = a;
307
	}
308
	respond(r, nil);
309
}
310
 
311
static char*
312
fsclone(Fid *ofid, Fid *fid)
313
{
314
	Aux *oa = ofid->aux;
315
	Aux *a = emalloc9p(sizeof(Aux));
316
 
317
	fid->aux = a;
318
 
319
	memset(a, 0, sizeof(Aux));
320
	a->sh = -1;
321
	a->fh = -1;
322
	a->sp = oa->sp;
323
	a->path = estrdup9p(oa->path);
324
 
325
	if(Auxroot){
326
		a->prev = Auxroot;
327
		a->next = Auxroot->next;
328
		Auxroot->next->prev = a;
329
		Auxroot->next = a;
330
	} else {
331
		Auxroot = a;
332
		a->next = a;
333
		a->prev = a;
334
	}
335
	return nil;
336
}
337
 
338
/*
339
 * for some weird reason T2queryall() returns share names
340
 * in lower case so we have to do an extra test against
341
 * our share table to validate filename case.
342
 *
343
 * on top of this here (snell & Wilcox) most of our
344
 * redirections point to a share of the same name,
345
 * but some do not, thus the tail of the filename
346
 * returned by T2queryall() is not the same as
347
 * the name we wanted.
348
 *
349
 * We work around this by not validating the names
350
 * or files which resolve to share names as they must
351
 * be correct, having been enforced in the dfs layer.
352
 */
353
static int
354
validfile(char *found, char *want, char *winpath, Share *sp)
355
{
356
	char *share;
357
 
358
	if(strcmp(want, "..") == 0)
359
		return 1;
360
	if(strcmp(winpath, "/") == 0){
361
		share = trimshare(sp->name);
362
		if(cistrcmp(want, share) == 0)
363
			return strcmp(want, share) == 0;
364
		/*
365
		 * OK, a DFS redirection points us from a directory XXX
366
		 * to a share named YYY.  There is no case checking we can
367
		 * do so we allow either case - it's all we can do.
368
		 */
369
		return 1;
370
	}
371
	if(cistrcmp(found, want) != 0)
372
		return 0;
373
	if(!Checkcase)
374
		return 1;
375
	if(strcmp(found, want) == 0)
376
		return 1;
377
	return 0;
378
}
379
 
380
 
381
static char*
382
fswalk1(Fid *fid, char *name, Qid *qid)
383
{
384
	FInfo fi;
385
	int rc, n, i;
386
	Aux *a = fid->aux;
387
	static char e[ERRMAX];
388
	char *p, *npath, *winpath;
389
 
390
	*e = 0;
391
	npath = newpath(a->path, name);
392
	if(strcmp(npath, "/") == 0){			/* root dir */
393
		*qid = mkqid("/", 1, 1, Proot, 0);
394
		free(a->path);
395
		a->path = npath;
396
		fid->qid = *qid;
397
		return nil;
398
	}
399
 
400
	if(strrchr(npath, '/') == npath){		/* top level dir */
401
		if((n = walkinfo(name)) != -1){		/* info file */
402
			*qid = mkqid(npath, 0, 1, Pinfo, n);
403
		}
404
		else {					/* volume name */
405
			for(i = 0; i < Nshares; i++){
406
				n = strlen(Shares[i].name);
407
				if(cistrncmp(npath+1, Shares[i].name, n) != 0)
408
					continue;
409
				if(Checkcase && strncmp(npath+1, Shares[i].name, n) != 0)
410
					continue;
411
				if(npath[n+1] != 0 && npath[n+1] != '/')
412
					continue;
413
				break;
414
			}
415
			if(i >= Nshares){
416
				free(npath);
417
				return "not found";
418
			}
419
			a->sp = Shares+i;
420
			*qid = mkqid(npath, 1, 1, Pshare, i);
421
		}
422
		free(a->path);
423
		a->path = npath;
424
		fid->qid = *qid;
425
		return nil;
426
	}
427
 
428
	/* must be a vanilla file or directory */
429
again:
430
	if(mapshare(npath, &a->sp) == -1){
431
		rerrstr(e, sizeof(e));
432
		free(npath);
433
		return e;
434
	}
435
 
436
	winpath = mapfile(npath);
437
	memset(&fi, 0, sizeof fi);
438
	if(Sess->caps & CAP_NT_SMBS)
439
		rc = T2queryall(Sess, a->sp, winpath, &fi);
440
	else
441
		rc = T2querystandard(Sess, a->sp, winpath, &fi);
442
 
443
	if(rc == -1){
444
		rerrstr(e, sizeof(e));
445
		free(npath);
446
		return e;
447
	}
448
 
449
	if((a->sp->options & SMB_SHARE_IS_IN_DFS) != 0 &&
450
	    (fi.attribs & ATTR_REPARSE) != 0){
451
		if(redirect(Sess, a->sp, npath) != -1)
452
			goto again;
453
	}
454
 
455
	if((p = strrchr(fi.name, '/')) == nil && (p = strrchr(fi.name, '\\')) == nil)
456
		p = fi.name;
457
	else
458
		p++;
459
 
460
	if(! validfile(p, name, winpath, a->sp)){
461
		free(npath);
462
		return "not found";
463
 
464
	}
465
	*qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0);
466
 
467
	free(a->path);
468
	a->path = npath;
469
	fid->qid = *qid;
470
	return nil;
471
}
472
 
473
static void
474
fsstat(Req *r)
475
{
476
	int rc;
477
	FInfo fi;
478
	Aux *a = r->fid->aux;
479
 
480
	if(ptype(r->fid->qid.path) == Proot)
481
		V2D(&r->d, r->fid->qid, "");
482
	else if(ptype(r->fid->qid.path) == Pinfo)
483
		dirgeninfo(pindex(r->fid->qid.path), &r->d);
484
	else if(ptype(r->fid->qid.path) == Pshare)
485
		V2D(&r->d, r->fid->qid, a->path +1);
486
	else{
487
		memset(&fi, 0, sizeof fi);
488
		if(Sess->caps & CAP_NT_SMBS)
489
			rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi);
490
		else
491
			rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);
492
		if(rc == -1){
493
			responderrstr(r);
494
			return;
495
		}
496
		I2D(&r->d, a->sp, a->path, &fi);
497
		if(Billtrog == 0)
498
			upd_names(Sess, a->sp, mapfile(a->path), &r->d);
499
	}
500
	respond(r, nil);
501
}
502
 
503
static int
504
smbcreateopen(Aux *a, char *path, int mode, int perm, int is_create,
505
	int is_dir, FInfo *fip)
506
{
507
	int rc, action, attrs, access, result;
508
 
509
	if(is_create && is_dir){
510
		if(CIFScreatedirectory(Sess, a->sp, path) == -1)
511
			return -1;
512
		return 0;
513
	}
514
 
515
	if(mode & DMAPPEND) {
516
		werrstr("filesystem does not support DMAPPEND");
517
		return -1;
518
	}
519
 
520
	if(is_create)
521
		action = 0x12;
522
	else if(mode & OTRUNC)
523
		action = 0x02;
524
	else
525
		action = 0x01;
526
 
527
	if(perm & 0222)
528
		attrs = ATTR_NORMAL;
529
	else
530
		attrs = ATTR_NORMAL|ATTR_READONLY;
531
 
532
	switch (mode & OMASK){
533
	case OREAD:
534
		access = 0;
535
		break;
536
	case OWRITE:
537
		access = 1;
538
		break;
539
	case ORDWR:
540
		access = 2;
541
		break;
542
	case OEXEC:
543
		access = 3;
544
		break;
545
	default:
546
		werrstr("%d bad open mode", mode & OMASK);
547
		return -1;
548
		break;
549
	}
550
 
551
	if(mode & DMEXCL == 0)
552
		access |= 0x10;
553
	else
554
		access |= 0x40;
555
 
556
	if((a->fh = CIFS_SMB_opencreate(Sess, a->sp, path, access, attrs,
557
	    action, &result)) == -1)
558
		return -1;
559
 
560
	if(Sess->caps & CAP_NT_SMBS)
561
		rc = T2queryall(Sess, a->sp, mapfile(a->path), fip);
562
	else
563
		rc = T2querystandard(Sess, a->sp, mapfile(a->path), fip);
564
	if(rc == -1){
565
		fprint(2, "internal error: stat of newly open/created file failed\n");
566
		return -1;
567
	}
568
 
569
	if((mode & OEXCL) && (result & 0x8000) == 0){
570
		werrstr("%d bad open mode", mode & OMASK);
571
		return -1;
572
	}
573
	return 0;
574
}
575
 
576
/* Uncle Bill, you have a lot to answer for... */
577
static int
578
ntcreateopen(Aux *a, char *path, int mode, int perm, int is_create,
579
	int is_dir, FInfo *fip)
580
{
581
	int options, result, attrs, flags, access, action, share;
582
 
583
	if(mode & DMAPPEND){
584
		werrstr("CIFSopen, DMAPPEND not supported");
585
		return -1;
586
	}
587
 
588
	if(is_create){
589
		if(mode & OEXCL)
590
			action = FILE_OPEN;
591
		else if(mode & OTRUNC)
592
			action = FILE_CREATE;
593
		else
594
			action = FILE_OVERWRITE_IF;
595
	} else {
596
		if(mode & OTRUNC)
597
			action = FILE_OVERWRITE_IF;
598
		else
599
			action = FILE_OPEN_IF;
600
	}
601
 
602
	flags = 0;		/* FIXME: really not sure */
603
 
604
	if(mode & OEXCL)
605
		share = FILE_NO_SHARE;
606
	else
607
		share = FILE_SHARE_ALL;
608
 
609
	switch (mode & OMASK){
610
	case OREAD:
611
		access = GENERIC_READ;
612
		break;
613
	case OWRITE:
614
		access = GENERIC_WRITE;
615
		break;
616
	case ORDWR:
617
		access = GENERIC_ALL;
618
		break;
619
	case OEXEC:
620
		access = GENERIC_EXECUTE;
621
		break;
622
	default:
623
		werrstr("%d bad open mode", mode & OMASK);
624
		return -1;
625
		break;
626
	}
627
 
628
	if(is_dir){
629
		action = FILE_CREATE;
630
		options = FILE_DIRECTORY_FILE;
631
		if(perm & 0222)
632
			attrs = ATTR_DIRECTORY;
633
		else
634
			attrs = ATTR_DIRECTORY|ATTR_READONLY;
635
	} else {
636
		options = FILE_NON_DIRECTORY_FILE;
637
		if(perm & 0222)
638
			attrs = ATTR_NORMAL;
639
		else
640
			attrs = ATTR_NORMAL|ATTR_READONLY;
641
	}
642
 
643
	if(mode & ORCLOSE){
644
		options |= FILE_DELETE_ON_CLOSE;
645
		attrs |= ATTR_DELETE_ON_CLOSE;
646
	}
647
 
648
	if((a->fh = CIFS_NT_opencreate(Sess, a->sp, path, flags, options,
649
	    attrs, access, share, action, &result, fip)) == -1)
650
		return -1;
651
 
652
	if((mode & OEXCL) && (result & 0x8000) == 0){
653
		werrstr("%d bad open mode", mode & OMASK);
654
		return -1;
655
	}
656
 
657
	return 0;
658
}
659
 
660
static void
661
fscreate(Req *r)
662
{
663
	FInfo fi;
664
	int rc, is_dir;
665
	char *npath;
666
	Aux *a = r->fid->aux;
667
 
668
	a->end = a->off = 0;
669
	a->cache = emalloc9p(max(Sess->mtu, MTU));
670
 
671
	is_dir = (r->ifcall.perm & DMDIR) == DMDIR;
672
	npath = smprint("%s/%s", a->path, r->ifcall.name);
673
 
674
	if(Sess->caps & CAP_NT_SMBS)
675
		rc = ntcreateopen(a, mapfile(npath), r->ifcall.mode,
676
			r->ifcall.perm, 1, is_dir, &fi);
677
	else
678
		rc = smbcreateopen(a, mapfile(npath), r->ifcall.mode,
679
			r->ifcall.perm, 1, is_dir, &fi);
680
	if(rc == -1){
681
		free(npath);
682
		responderrstr(r);
683
		return;
684
	}
685
 
686
	r->fid->qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0);
687
 
688
	r->ofcall.qid = r->fid->qid;
689
	free(a->path);
690
	a->path = npath;
691
 
692
	respond(r, nil);
693
}
694
 
695
static void
696
fsopen(Req *r)
697
{
698
	int rc;
699
	FInfo fi;
700
	Aux *a = r->fid->aux;
701
 
702
	a->end = a->off = 0;
703
	a->cache = emalloc9p(max(Sess->mtu, MTU));
704
 
705
	if(ptype(r->fid->qid.path) == Pinfo){
706
		if(makeinfo(pindex(r->fid->qid.path)) != -1)
707
			respond(r, nil);
708
		else
709
			respond(r, "cannot generate info");
710
		return;
711
	}
712
 
713
	if(r->fid->qid.type & QTDIR){
714
		respond(r, nil);
715
		return;
716
	}
717
 
718
	if(Sess->caps & CAP_NT_SMBS)
719
		rc = ntcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777,
720
			0, 0, &fi);
721
	else
722
		rc = smbcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777,
723
			0, 0, &fi);
724
	if(rc == -1){
725
		responderrstr(r);
726
		return;
727
	}
728
	respond(r, nil);
729
}
730
 
731
static void
732
fswrite(Req *r)
733
{
734
	vlong n, m, got;
735
	Aux *a = r->fid->aux;
736
	vlong len = r->ifcall.count;
737
	vlong off = r->ifcall.offset;
738
	char *buf = r->ifcall.data;
739
 
740
	got = 0;
741
	n = Sess->mtu -OVERHEAD;
742
	do{
743
		if(len - got < n)
744
			n = len - got;
745
		m = CIFSwrite(Sess, a->sp, a->fh, off + got, buf + got, n);
746
		if(m != -1)
747
			got += m;
748
	} while(got < len && m >= n);
749
 
750
	r->ofcall.count = got;
751
	if(m == -1)
752
		responderrstr(r);
753
	else
754
		respond(r, nil);
755
}
756
 
757
static void
758
fsread(Req *r)
759
{
760
	vlong n, m, got;
761
	Aux *a = r->fid->aux;
762
	char *buf = r->ofcall.data;
763
	vlong len = r->ifcall.count;
764
	vlong off = r->ifcall.offset;
765
 
766
	if(ptype(r->fid->qid.path) == Pinfo){
767
		r->ofcall.count = readinfo(pindex(r->fid->qid.path), buf, len,
768
			off);
769
		respond(r, nil);
770
		return;
771
	}
772
 
773
	if(r->fid->qid.type & QTDIR){
774
		dirread9p(r, dirgen, a);
775
		respond(r, nil);
776
		return;
777
	}
778
 
779
	got = 0;
780
	n = Sess->mtu -OVERHEAD;
781
	do{
782
		if(len - got < n)
783
			n = len - got;
784
		m = CIFSread(Sess, a->sp, a->fh, off + got, buf + got, n, len);
785
		if(m != -1)
786
			got += m;
787
	} while(got < len && m >= n);
788
 
789
	r->ofcall.count = got;
790
	if(m == -1)
791
		responderrstr(r);
792
	else
793
		respond(r, nil);
794
}
795
 
796
static void
797
fsdestroyfid(Fid *f)
798
{
799
	Aux *a = f->aux;
800
 
801
	if(ptype(f->qid.path) == Pinfo)
802
		freeinfo(pindex(f->qid.path));
803
	f->omode = -1;
804
	if(! a)
805
		return;
806
	if(a->fh != -1)
807
		if(CIFSclose(Sess, a->sp, a->fh) == -1)
808
			fprint(2, "%s: close failed fh=%d %r\n", argv0, a->fh);
809
	if(a->sh != -1)
810
		if(CIFSfindclose2(Sess, a->sp, a->sh) == -1)
811
			fprint(2, "%s: findclose failed sh=%d %r\n",
812
				argv0, a->sh);
813
	if(a->path)
814
		free(a->path);
815
	if(a->cache)
816
		free(a->cache);
817
 
818
	if(a == Auxroot)
819
		Auxroot = a->next;
820
	a->prev->next = a->next;
821
	a->next->prev = a->prev;
822
	if(a->next == a->prev)
823
		Auxroot = nil;
824
	if(a)
825
		free(a);
826
}
827
 
828
int
829
rdonly(Session *s, Share *sp, char *path, int rdonly)
830
{
831
	int rc;
832
	FInfo fi;
833
 
834
	if(Sess->caps & CAP_NT_SMBS)
835
		rc = T2queryall(s, sp, path, &fi);
836
	else
837
		rc = T2querystandard(s, sp, path, &fi);
838
	if(rc == -1)
839
		return -1;
840
 
841
	if((rdonly && !(fi.attribs & ATTR_READONLY)) ||
842
	    (!rdonly && (fi.attribs & ATTR_READONLY))){
843
		fi.attribs &= ~ATTR_READONLY;
844
		fi.attribs |= rdonly? ATTR_READONLY: 0;
845
		rc = CIFSsetinfo(s, sp, path, &fi);
846
	}
847
	return rc;
848
}
849
 
850
static void
851
fsremove(Req *r)
852
{
853
	int try, rc;
854
	char e[ERRMAX];
855
	Aux *ap, *a = r->fid->aux;
856
 
857
	*e = 0;
858
	if(ptype(r->fid->qid.path) == Proot ||
859
	   ptype(r->fid->qid.path) == Pshare){
860
		respond(r, "illegal operation");
861
		return;
862
	}
863
 
864
	/* close all instences of this file/dir */
865
	if((ap = Auxroot) != nil)
866
		do{
867
			if(strcmp(ap->path, a->path) == 0){
868
				if(ap->sh != -1)
869
					CIFSfindclose2(Sess, ap->sp, ap->sh);
870
				ap->sh = -1;
871
				if(ap->fh != -1)
872
					CIFSclose(Sess, ap->sp, ap->fh);
873
				ap->fh = -1;
874
			}
875
			ap = ap->next;
876
		}while(ap != Auxroot);
877
	try = 0;
878
again:
879
	if(r->fid->qid.type & QTDIR)
880
		rc = CIFSdeletedirectory(Sess, a->sp, mapfile(a->path));
881
	else
882
		rc = CIFSdeletefile(Sess, a->sp, mapfile(a->path));
883
 
884
	rerrstr(e, sizeof(e));
885
	if(rc == -1 && try++ == 0 && strcmp(e, "permission denied") == 0 &&
886
	    rdonly(Sess, a->sp, mapfile(a->path), 0) == 0)
887
		goto again;
888
	if(rc == -1)
889
		responderrstr(r);
890
	else
891
		respond(r, nil);
892
}
893
 
894
static void
895
fswstat(Req *r)
896
{
897
	int fh, result, rc;
898
	FInfo fi, tmpfi;
899
	char *p, *from, *npath;
900
	Aux *a = r->fid->aux;
901
 
902
	if(ptype(r->fid->qid.path) == Proot ||
903
	   ptype(r->fid->qid.path) == Pshare){
904
		respond(r, "illegal operation");
905
		return;
906
	}
907
 
908
	if((r->d.uid && r->d.uid[0]) || (r->d.gid && r->d.gid[0])){
909
		respond(r, "cannot change ownership");
910
		return;
911
	}
912
 
913
	/*
914
	 * get current info
915
	 */
916
	if(Sess->caps & CAP_NT_SMBS)
917
		rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi);
918
	else
919
		rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);
920
	if(rc == -1){
921
		werrstr("(query) - %r");
922
		responderrstr(r);
923
		return;
924
	}
925
 
926
	/*
927
	 * always clear the readonly attribute if set,
928
	 * before trying to set any other fields.
929
	 * wstat() fails if the file/dir is readonly
930
	 * and this function is so full of races - who cares about one more?
931
	 */
932
	rdonly(Sess, a->sp, mapfile(a->path), 0);
933
 
934
	/*
935
	 * rename - one piece of joy, renaming open files
936
	 * is legal (sharing permitting).
937
	 */
938
	if(r->d.name && r->d.name[0]){
939
		if((p = strrchr(a->path, '/')) == nil){
940
			respond(r, "illegal path");
941
			return;
942
		}
943
		npath = emalloc9p((p-a->path)+strlen(r->d.name)+2);
944
		strecpy(npath, npath+(p- a->path)+2, a->path);
945
		strcat(npath, r->d.name);
946
 
947
		from = estrdup9p(mapfile(a->path));
948
		if(CIFSrename(Sess, a->sp, from, mapfile(npath)) == -1){
949
			werrstr("(rename) - %r");
950
			responderrstr(r);
951
			free(npath);
952
			free(from);
953
			return;
954
		}
955
		free(from);
956
		free(a->path);
957
		a->path = npath;
958
	}
959
 
960
	/*
961
	 * set the files length, do this before setting
962
	 * the file times as open() will alter them
963
	 */
964
	if(~r->d.length){
965
		fi.size = r->d.length;
966
 
967
		if(Sess->caps & CAP_NT_SMBS){
968
			if((fh = CIFS_NT_opencreate(Sess, a->sp, mapfile(a->path),
969
			    0, FILE_NON_DIRECTORY_FILE,
970
	    		    ATTR_NORMAL, GENERIC_WRITE, FILE_SHARE_ALL,
971
			    FILE_OPEN_IF, &result, &tmpfi)) == -1){
972
				werrstr("(set length, open) - %r");
973
				responderrstr(r);
974
				return;
975
			}
976
			rc = T2setfilelength(Sess, a->sp, fh, &fi);
977
			CIFSclose(Sess, a->sp, fh);
978
			if(rc == -1){
979
				werrstr("(set length), set) - %r");
980
				responderrstr(r);
981
				return;
982
			}
983
		} else {
984
			if((fh = CIFS_SMB_opencreate(Sess, a->sp, mapfile(a->path),
985
			    1, ATTR_NORMAL, 1, &result)) == -1){
986
				werrstr("(set length, open) failed - %r");
987
				responderrstr(r);
988
				return;
989
			}
990
			rc = CIFSwrite(Sess, a->sp, fh, fi.size, 0, 0);
991
			CIFSclose(Sess, a->sp, fh);
992
			if(rc == -1){
993
				werrstr("(set length, write) - %r");
994
				responderrstr(r);
995
				return;
996
			}
997
		}
998
	}
999
 
1000
	/*
1001
	 * This doesn't appear to set length or
1002
	 * attributes, no idea why, so I do those seperately
1003
	 */
1004
	if(~r->d.mtime || ~r->d.atime){
1005
		if(~r->d.mtime)
1006
			fi.written = r->d.mtime;
1007
		if(~r->d.atime)
1008
			fi.accessed = r->d.atime;
1009
		if(T2setpathinfo(Sess, a->sp, mapfile(a->path), &fi) == -1){
1010
			werrstr("(set path info) - %r");
1011
			responderrstr(r);
1012
			return;
1013
		}
1014
	}
1015
 
1016
	/*
1017
	 * always update the readonly flag as
1018
	 * we may have cleared it above.
1019
	 */
1020
	if(~r->d.mode){
1021
		if(r->d.mode & 0222)
1022
			fi.attribs &= ~ATTR_READONLY;
1023
		else
1024
			fi.attribs |= ATTR_READONLY;
1025
	}
1026
	if(rdonly(Sess, a->sp, mapfile(a->path), fi.attribs & ATTR_READONLY) == -1){
1027
		werrstr("(set info) - %r");
1028
		responderrstr(r);
1029
		return;
1030
	}
1031
 
1032
	/*
1033
	 * Win95 has a broken write-behind cache for metadata
1034
	 * on open files (writes go to the cache, reads bypass
1035
	 * the cache), so we must flush the file.
1036
	 */
1037
	if(r->fid->omode != -1 && CIFSflush(Sess, a->sp, a->fh) == -1){
1038
		werrstr("(flush) %r");
1039
		responderrstr(r);
1040
		return;
1041
	}
1042
	respond(r, nil);
1043
}
1044
 
1045
static void
1046
fsend(Srv *srv)
1047
{
1048
	int i;
1049
	USED(srv);
1050
 
1051
	for(i = 0; i < Nshares; i++)
1052
		CIFStreedisconnect(Sess, Shares+i);
1053
	CIFSlogoff(Sess);
1054
	postnote(PNPROC, Keeppid, "die");
1055
}
1056
 
1057
Srv fs = {
1058
	.destroyfid =	fsdestroyfid,
1059
	.attach=	fsattach,
1060
	.open=		fsopen,
1061
	.create=	fscreate,
1062
	.read=		fsread,
1063
	.write=		fswrite,
1064
	.remove=	fsremove,
1065
	.stat=		fsstat,
1066
	.wstat=		fswstat,
1067
	.clone= 	fsclone,
1068
	.walk1= 	fswalk1,
1069
	.end=		fsend,
1070
};
1071
 
1072
void
1073
usage(void)
1074
{
1075
	fprint(2, "usage: %s [-d name] [-Dvb] [-a auth-method] [-s srvname] "
1076
		"[-n called-name] [-k factotum-params] [-m mntpnt] "
1077
		"host [share...]\n", argv0);
1078
	exits("usage");
1079
}
1080
 
1081
/*
1082
 * SMBecho looks like the function to use for keepalives,
1083
 * sadly the echo packet does not seem to reload the
1084
 * idle timer in Microsoft's servers.  Instead we use
1085
 * "get file system size" on each share until we get one that succeeds.
1086
 */
1087
static void
1088
keepalive(void)
1089
{
1090
	char buf[32];
1091
	uvlong tot, fre;
1092
	int fd, i, slot, rc;
1093
 
1094
	snprint(buf, sizeof buf, "#p/%d/args", getpid());
1095
	if((fd = open(buf, OWRITE)) >= 0){
1096
		fprint(fd, "%s keepalive", Host);
1097
		close(fd);
1098
	}
1099
 
1100
	rc = 0;
1101
	slot = 0;
1102
	do{
1103
		sleep(6000);
1104
		if(Active-- != 0)
1105
			continue;
1106
		for(i = 0; i < Nshares; i++){
1107
			if((rc = T2fssizeinfo(Sess, &Shares[slot], &tot, &fre)) == 0)
1108
				break;
1109
			if(++slot >= Nshares)
1110
				slot = 0;
1111
		}
1112
	}while(rc != -1);
1113
	postnote(PNPROC, Attachpid, "die");
1114
}
1115
 
1116
 
1117
static void
1118
ding(void *u, char *msg)
1119
{
1120
	USED(u);
1121
	if(strstr(msg, "alarm") != nil)
1122
		noted(NCONT);
1123
	noted(NDFLT);
1124
}
1125
 
1126
void
1127
dmpkey(char *s, void *v, int n)
1128
{
1129
	int i;
1130
	unsigned char *p = (unsigned char *)v;
1131
 
1132
	print("%s", s);
1133
	for(i = 0; i < n; i++)
1134
		print("%02ux ", *p++);
1135
	print("\n");
1136
}
1137
 
1138
void
1139
main(int argc, char **argv)
1140
{
1141
	int i, n;
1142
	long svrtime;
1143
	char windom[64], cname[64];
1144
	char *method, *sysname, *keyp, *mtpt, *svs;
1145
	static char *sh[1024];
1146
 
1147
	*cname = 0;
1148
	keyp = "";
1149
	method = nil;
1150
	strcpy(windom, "unknown");
1151
	mtpt = svs = nil;
1152
 
1153
	notify(ding);
1154
 
1155
	ARGBEGIN{
1156
	case 'a':
1157
		method = EARGF(autherr());
1158
		break;
1159
	case 'b':
1160
		Billtrog ^= 1;
1161
		break;
1162
	case 'D':
1163
		chatty9p++;
1164
		break;
1165
	case 'd':
1166
		Debug = EARGF(usage());
1167
		break;
1168
	case 'i':
1169
		Checkcase = 0;
1170
		break;
1171
	case 'k':
1172
		keyp = EARGF(usage());
1173
		break;
1174
	case 'm':
1175
		mtpt = EARGF(usage());
1176
		break;
1177
	case 'n':
1178
		strncpy(cname, EARGF(usage()), sizeof(cname));
1179
		cname[sizeof(cname) - 1] = 0;
1180
		break;
1181
	case 's':
1182
		svs = EARGF(usage());
1183
		break;
1184
	case 't':
1185
		Dfstout = atoi(EARGF(usage()));
1186
		break;
1187
	default:
1188
		usage();
1189
		break;
1190
	}ARGEND
1191
 
1192
	if(argc < 1)
1193
		usage();
1194
 
1195
	Host = argv[0];
1196
 
1197
	if(mtpt == nil && svs == nil)
1198
		mtpt = smprint("/n/%s", Host);
1199
 
1200
	if((sysname = getenv("sysname")) == nil)
1201
		sysname = "unknown";
1202
 
1203
	if(*cname && (Sess = cifsdial(Host, cname, sysname)) != nil)
1204
		goto connected;
1205
 
1206
	if(calledname(Host, cname) == 0 &&
1207
	    (Sess = cifsdial(Host, cname, sysname)) != nil)
1208
		goto connected;
1209
 
1210
	strcpy(cname, Host);
1211
	if((Sess = cifsdial(Host, Host, sysname)) != nil ||
1212
	   (Sess = cifsdial(Host, "*SMBSERVER", sysname)) != nil)
1213
		goto connected;
1214
 
1215
	sysfatal("%s - cannot dial, %r\n", Host);
1216
connected:
1217
	if(CIFSnegotiate(Sess, &svrtime, windom, sizeof windom, cname, sizeof cname) == -1)
1218
		sysfatal("%s - cannot negioate common protocol, %r\n", Host);
1219
 
1220
#ifndef DEBUG_MAC
1221
	Sess->secmode &= ~SECMODE_SIGN_ENABLED;
1222
#endif
1223
 
1224
	Sess->auth = getauth(method, windom, keyp, Sess->secmode, Sess->chal,
1225
		Sess->challen);
1226
 
1227
	if(CIFSsession(Sess) < 0)
1228
		sysfatal("session authentication failed, %r\n");
1229
 
1230
	Sess->slip = svrtime - time(nil);
1231
	Sess->cname = estrdup9p(cname);
1232
 
1233
	if(CIFStreeconnect(Sess, cname, Ipcname, &Ipc) == -1)
1234
		fprint(2, "%s, %r - can't connect\n", Ipcname);
1235
 
1236
	Nshares = 0;
1237
	if(argc == 1){
1238
		Share *sip;
1239
 
1240
		if((n = RAPshareenum(Sess, &Ipc, &sip)) < 1)
1241
			sysfatal("can't enumerate shares: %r - specify share "
1242
				"names on command line\n");
1243
 
1244
		for(i = 0; i < n; i++){
1245
#ifdef NO_HIDDEN_SHARES
1246
			int l = strlen(sip[i].name);
1247
 
1248
			if(l > 1 && sip[i].name[l-1] == '$'){
1249
				free(sip[i].name);
1250
				continue;
1251
			}
1252
#endif
1253
			memcpy(Shares+Nshares, sip+i, sizeof(Share));
1254
			if(CIFStreeconnect(Sess, Sess->cname,
1255
			    Shares[Nshares].name, Shares+Nshares) == -1){
1256
				free(Shares[Nshares].name);
1257
				continue;
1258
			}
1259
			Nshares++;
1260
		}
1261
		free(sip);
1262
	} else
1263
		for(i = 1; i < argc; i++){
1264
			if(CIFStreeconnect(Sess, Sess->cname, argv[i],
1265
			    Shares+Nshares) == -1){
1266
				fprint(2, "%s: %s  %q - can't connect to share"
1267
					", %r\n", argv0, Host, argv[i]);
1268
				continue;
1269
			}
1270
			Shares[Nshares].name = strlwr(estrdup9p(argv[i]));
1271
			Nshares++;
1272
		}
1273
 
1274
	if(Nshares == 0)
1275
		fprint(2, "no available shares\n");
1276
 
1277
	if((Keeppid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFNAMEG)) == 0){
1278
		keepalive();
1279
		exits(nil);
1280
	}
1281
	postmountsrv(&fs, svs, mtpt, MREPL|MCREATE);
1282
	exits(nil);
1283
}