Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature-vt/sys/src/cmd/webfs/fs.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * Web file system.  Conventionally mounted at /mnt/web
3
 *
4
 *	ctl				send control messages (might go away)
5
 *	cookies			list of cookies, editable
6
 *	clone			open and read to obtain new connection
7
 *	n				connection directory
8
 *		ctl				control messages (like get url)
9
 *		body				retrieved data
10
 *		content-type		mime content-type of body
11
 *		postbody			data to be posted
12
 *		parsed			parsed version of url
13
 * 			url				entire url
14
 *			scheme			http, ftp, etc.
15
 *			host				hostname
16
 *			path				path on host
17
 *			query			query after path
18
 *			fragment			#foo anchor reference
19
 *			user				user name (ftp)
20
 *			password			password (ftp)
21
 *			ftptype			transfer mode (ftp)
22
 */
23
 
24
#include <u.h>
25
#include <libc.h>
26
#include <bio.h>
27
#include <ip.h>
28
#include <plumb.h>
29
#include <thread.h>
30
#include <fcall.h>
31
#include <9p.h>
32
#include "dat.h"
33
#include "fns.h"
34
 
35
int fsdebug;
36
 
37
enum
38
{
39
	Qroot,
40
	Qrootctl,
41
	Qclone,
42
	Qcookies,
43
	Qclient,
44
	Qctl,
45
	Qbody,
46
	Qbodyext,
47
	Qcontenttype,
48
	Qpostbody,
49
	Qparsed,
50
	Qurl,
51
	Qscheme,
52
	Qschemedata,
53
	Quser,
54
	Qpasswd,
55
	Qhost,
56
	Qport,
57
	Qpath,
58
	Qquery,
59
	Qfragment,
60
	Qftptype,
61
	Qend,
62
};
63
 
64
#define PATH(type, n)	((type)|((n)<<8))
65
#define TYPE(path)		((int)(path) & 0xFF)
66
#define NUM(path)		((uint)(path)>>8)
67
 
68
Channel *creq;
69
Channel *creqwait;
70
Channel *cclunk;
71
Channel *cclunkwait;
72
 
73
typedef struct Tab Tab;
74
struct Tab
75
{
76
	char *name;
77
	ulong mode;
78
	int offset;
79
};
80
 
81
Tab tab[] =
82
{
83
	"/",			DMDIR|0555,		0,
84
	"ctl",			0666,			0,
85
	"clone",		0666,			0,
86
	"cookies",		0666,			0,
87
	"XXX",		DMDIR|0555,		0,
88
	"ctl",			0666,			0,
89
	"body",		0444,			0,
90
	"XXX",		0444,			0,
91
	"contenttype",	0444,			0,
92
	"postbody",	0666,			0,
93
	"parsed",		DMDIR|0555,		0,
94
	"url",			0444,			offsetof(Url, url),
95
	"scheme",		0444,			offsetof(Url, scheme),
96
	"schemedata",	0444,			offsetof(Url, schemedata),
97
	"user",		0444,			offsetof(Url, user),
98
	"passwd",		0444,			offsetof(Url, passwd),
99
	"host",		0444,			offsetof(Url, host),
100
	"port",		0444,			offsetof(Url, port),
101
	"path",		0444,			offsetof(Url, path),
102
	"query",		0444,			offsetof(Url, query),
103
	"fragment",	0444,			offsetof(Url, fragment),
104
	"ftptype",		0444,			offsetof(Url, ftp.type),
105
};
106
 
107
ulong time0;
108
 
109
static void
110
fillstat(Dir *d, uvlong path, ulong length, char *ext)
111
{
112
	Tab *t;
113
	int type;
114
	char buf[32];
115
 
116
	memset(d, 0, sizeof(*d));
117
	d->uid = estrdup("web");
118
	d->gid = estrdup("web");
119
	d->qid.path = path;
120
	d->atime = d->mtime = time0;
121
	d->length = length;
122
	type = TYPE(path);
123
	t = &tab[type];
124
	if(type == Qbodyext) {
125
		snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
126
		d->name = estrdup(buf);
127
	}
128
	else if(t->name)
129
		d->name = estrdup(t->name);
130
	else{	/* client directory */
131
		snprint(buf, sizeof buf, "%ud", NUM(path));
132
		d->name = estrdup(buf);
133
	}
134
	d->qid.type = t->mode>>24;
135
	d->mode = t->mode;
136
}
137
 
138
static void
139
fsstat(Req *r)
140
{
141
	fillstat(&r->d, r->fid->qid.path, 0, nil);
142
	respond(r, nil);
143
}
144
 
145
static int
146
rootgen(int i, Dir *d, void*)
147
{
148
	char buf[32];
149
 
150
	i += Qroot+1;
151
	if(i < Qclient){
152
		fillstat(d, i, 0, nil);
153
		return 0;
154
	}
155
	i -= Qclient;
156
	if(i < nclient){
157
		fillstat(d, PATH(Qclient, i), 0, nil);
158
		snprint(buf, sizeof buf, "%d", i);
159
		free(d->name);
160
		d->name = estrdup(buf);
161
		return 0;
162
	}
163
	return -1;
164
}
165
 
166
static int
167
clientgen(int i, Dir *d, void *aux)
168
{
169
	Client *c;
170
 
171
	c = aux;
172
	i += Qclient+1;
173
	if(i <= Qparsed){
174
		fillstat(d, PATH(i, c->num), 0, c->ext);
175
		return 0;
176
	}
177
	return -1;
178
}
179
 
180
static int
181
parsedgen(int i, Dir *d, void *aux)
182
{
183
	Client *c;
184
 
185
	c = aux;
186
	i += Qparsed+1;
187
	if(i < Qend){
188
		fillstat(d, PATH(i, c->num), 0, nil);
189
		return 0;
190
	}
191
	return -1;
192
}
193
 
194
static void
195
fsread(Req *r)
196
{
197
	char *s;
198
	char e[ERRMAX];
199
	Client *c;
200
	ulong path;
201
 
202
	path = r->fid->qid.path;
203
	switch(TYPE(path)){
204
	default:
205
		snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
206
		respond(r, e);
207
		break;
208
 
209
	case Qroot:
210
		dirread9p(r, rootgen, nil);
211
		respond(r, nil);
212
		break;
213
 
214
	case Qrootctl:
215
		globalctlread(r);
216
		break;
217
 
218
	case Qcookies:
219
		cookieread(r);
220
		break;
221
 
222
	case Qclient:
223
		dirread9p(r, clientgen, client[NUM(path)]);
224
		respond(r, nil);
225
		break;
226
 
227
	case Qctl:
228
		ctlread(r, client[NUM(path)]);
229
		break;
230
 
231
	case Qcontenttype:
232
		c = client[NUM(path)];
233
		if(c->contenttype == nil)
234
			r->ofcall.count = 0;
235
		else
236
			readstr(r, c->contenttype);
237
		respond(r, nil);
238
		break;
239
 
240
	case Qpostbody:
241
		c = client[NUM(path)];
242
		readbuf(r, c->postbody, c->npostbody);
243
		respond(r, nil);
244
		break;
245
 
246
	case Qbody:
247
	case Qbodyext:
248
		c = client[NUM(path)];
249
		if(c->iobusy){
250
			respond(r, "already have i/o pending");
251
			break;
252
		}
253
		c->iobusy = 1;
254
		sendp(c->creq, r);
255
		break;
256
 
257
	case Qparsed:
258
		dirread9p(r, parsedgen, client[NUM(path)]);
259
		respond(r, nil);
260
		break;
261
 
262
	case Qurl:
263
	case Qscheme:
264
	case Qschemedata:
265
	case Quser:
266
	case Qpasswd:
267
	case Qhost:
268
	case Qport:
269
	case Qpath:
270
	case Qquery:
271
	case Qfragment:
272
	case Qftptype:
273
		c = client[NUM(path)];
274
		r->ofcall.count = 0;
275
		if(c->url != nil
276
		&& (s = *(char**)((uintptr)c->url+tab[TYPE(path)].offset)) != nil)
277
			readstr(r, s);
278
		respond(r, nil);
279
		break;
280
	}
281
}
282
 
283
static void
284
fswrite(Req *r)
285
{
286
	int m;
287
	ulong path;
288
	char e[ERRMAX], *buf, *cmd, *arg;
289
	Client *c;
290
 
291
	path = r->fid->qid.path;
292
	switch(TYPE(path)){
293
	default:
294
		snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
295
		respond(r, e);
296
		break;
297
 
298
	case Qcookies:
299
		cookiewrite(r);
300
		break;
301
 
302
	case Qrootctl:
303
	case Qctl:
304
		if(r->ifcall.count >= 1024){
305
			respond(r, "ctl message too long");
306
			return;
307
		}
308
		buf = estredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count);
309
		cmd = buf;
310
		arg = strpbrk(cmd, "\t ");
311
		if(arg){
312
			*arg++ = '\0';
313
			arg += strspn(arg, "\t ");
314
		}else
315
			arg = "";
316
		r->ofcall.count = r->ifcall.count;
317
		if(TYPE(path)==Qrootctl){
318
			if(!ctlwrite(r, &globalctl, cmd, arg)
319
			&& !globalctlwrite(r, cmd, arg))
320
				respond(r, "unknown control command");
321
		}else{
322
			c = client[NUM(path)];
323
			if(!ctlwrite(r, &c->ctl, cmd, arg)
324
			&& !clientctlwrite(r, c, cmd, arg))
325
				respond(r, "unknown control command");
326
		}
327
		free(buf);
328
		break;
329
 
330
	case Qpostbody:
331
		c = client[NUM(path)];
332
		if(c->bodyopened){
333
			respond(r, "cannot write postbody after opening body");
334
			break;
335
		}
336
		if(r->ifcall.offset >= 128*1024*1024){	/* >128MB is probably a mistake */
337
			respond(r, "offset too large");
338
			break;
339
		}
340
		m = r->ifcall.offset + r->ifcall.count;
341
		if(c->npostbody < m){
342
			c->postbody = erealloc(c->postbody, m);
343
			memset(c->postbody+c->npostbody, 0, m-c->npostbody);
344
			c->npostbody = m;
345
		}
346
		memmove(c->postbody+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
347
		r->ofcall.count = r->ifcall.count;
348
		respond(r, nil);
349
		break;
350
	}
351
}
352
 
353
static void
354
fsopen(Req *r)
355
{
356
	static int need[4] = { 4, 2, 6, 1 };
357
	ulong path;
358
	int n;
359
	Client *c;
360
	Tab *t;
361
 
362
	/*
363
	 * lib9p already handles the blatantly obvious.
364
	 * we just have to enforce the permissions we have set.
365
	 */
366
	path = r->fid->qid.path;
367
	t = &tab[TYPE(path)];
368
	n = need[r->ifcall.mode&3];
369
	if((n&t->mode) != n){
370
		respond(r, "permission denied");
371
		return;
372
	}
373
 
374
	switch(TYPE(path)){
375
	case Qcookies:
376
		cookieopen(r);
377
		break;
378
 
379
	case Qpostbody:
380
		c = client[NUM(path)];
381
		c->havepostbody++;
382
		c->ref++;
383
		respond(r, nil);
384
		break;
385
 
386
	case Qbody:
387
	case Qbodyext:
388
		c = client[NUM(path)];
389
		if(c->url == nil){
390
			respond(r, "url is not yet set");
391
			break;
392
		}
393
		c->bodyopened = 1;
394
		c->ref++;
395
		sendp(c->creq, r);
396
		break;
397
 
398
	case Qclone:
399
		n = newclient(0);
400
		path = PATH(Qctl, n);
401
		r->fid->qid.path = path;
402
		r->ofcall.qid.path = path;
403
		if(fsdebug)
404
			fprint(2, "open clone => path=%lux\n", path);
405
		t = &tab[Qctl];
406
		/* fall through */
407
	default:
408
		if(t-tab >= Qclient)
409
			client[NUM(path)]->ref++;
410
		respond(r, nil);
411
		break;
412
	}
413
}
414
 
415
static void
416
fsdestroyfid(Fid *fid)
417
{
418
	sendp(cclunk, fid);
419
	recvp(cclunkwait);
420
}
421
 
422
static void
423
fsattach(Req *r)
424
{
425
	if(r->ifcall.aname && r->ifcall.aname[0]){
426
		respond(r, "invalid attach specifier");
427
		return;
428
	}
429
	r->fid->qid.path = PATH(Qroot, 0);
430
	r->fid->qid.type = QTDIR;
431
	r->fid->qid.vers = 0;
432
	r->ofcall.qid = r->fid->qid;
433
	respond(r, nil);
434
}
435
 
436
static char*
437
fswalk1(Fid *fid, char *name, Qid *qid)
438
{
439
	int i, n;
440
	ulong path;
441
	char buf[32], *ext;
442
 
443
	path = fid->qid.path;
444
	if(!(fid->qid.type&QTDIR))
445
		return "walk in non-directory";
446
 
447
	if(strcmp(name, "..") == 0){
448
		switch(TYPE(path)){
449
		case Qparsed:
450
			qid->path = PATH(Qclient, NUM(path));
451
			qid->type = tab[Qclient].mode>>24;
452
			return nil;
453
		case Qclient:
454
		case Qroot:
455
			qid->path = PATH(Qroot, 0);
456
			qid->type = tab[Qroot].mode>>24;
457
			return nil;
458
		default:
459
			return "bug in fswalk1";
460
		}
461
	}
462
 
463
	i = TYPE(path)+1;
464
	for(; i<nelem(tab); i++){
465
		if(i==Qclient){
466
			n = atoi(name);
467
			snprint(buf, sizeof buf, "%d", n);
468
			if(n < nclient && strcmp(buf, name) == 0){
469
				qid->path = PATH(i, n);
470
				qid->type = tab[i].mode>>24;
471
				return nil;
472
			}
473
			break;
474
		}
475
		if(i==Qbodyext){
476
			ext = client[NUM(path)]->ext;
477
			snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
478
			if(strcmp(buf, name) == 0){
479
				qid->path = PATH(i, NUM(path));
480
				qid->type = tab[i].mode>>24;
481
				return nil;
482
			}
483
		}
484
		else if(strcmp(name, tab[i].name) == 0){
485
			qid->path = PATH(i, NUM(path));
486
			qid->type = tab[i].mode>>24;
487
			return nil;
488
		}
489
		if(tab[i].mode&DMDIR)
490
			break;
491
	}
492
	return "directory entry not found";
493
}
494
 
495
static void
496
fsflush(Req *r)
497
{
498
	Req *or;
499
	int t;
500
	Client *c;
501
	ulong path;
502
 
503
	or=r;
504
	while(or->ifcall.type==Tflush)
505
		or = or->oldreq;
506
 
507
	if(or->ifcall.type != Tread && or->ifcall.type != Topen)
508
		abort();
509
 
510
	path = or->fid->qid.path;
511
	t = TYPE(path);
512
	if(t != Qbody && t != Qbodyext)
513
		abort();
514
 
515
	c = client[NUM(path)];
516
	sendp(c->creq, r);
517
	iointerrupt(c->io);
518
}
519
 
520
static void
521
fsthread(void*)
522
{
523
	ulong path;
524
	Alt a[3];
525
	Fid *fid;
526
	Req *r;
527
 
528
	threadsetname("fsthread");
529
	plumbstart();
530
 
531
	a[0].op = CHANRCV;
532
	a[0].c = cclunk;
533
	a[0].v = &fid;
534
	a[1].op = CHANRCV;
535
	a[1].c = creq;
536
	a[1].v = &r;
537
	a[2].op = CHANEND;
538
 
539
	for(;;){
540
		switch(alt(a)){
541
		case 0:
542
			path = fid->qid.path;
543
			if(TYPE(path)==Qcookies)
544
				cookieclunk(fid);
545
			if(fid->omode != -1 && TYPE(path) >= Qclient)
546
				closeclient(client[NUM(path)]);
547
			sendp(cclunkwait, nil);
548
			break;
549
		case 1:
550
			switch(r->ifcall.type){
551
			case Tattach:
552
				fsattach(r);
553
				break;
554
			case Topen:
555
				fsopen(r);
556
				break;
557
			case Tread:
558
				fsread(r);
559
				break;
560
			case Twrite:
561
				fswrite(r);
562
				break;
563
			case Tstat:
564
				fsstat(r);
565
				break;
566
			case Tflush:
567
				fsflush(r);
568
				break;
569
			default:
570
				respond(r, "bug in fsthread");
571
				break;
572
			}
573
			sendp(creqwait, 0);
574
			break;
575
		}
576
	}
577
}
578
 
579
static void
580
fssend(Req *r)
581
{
582
	sendp(creq, r);
583
	recvp(creqwait);	/* avoids need to deal with spurious flushes */
584
}
585
 
586
void
587
initfs(void)
588
{
589
	time0 = time(0);
590
	creq = chancreate(sizeof(void*), 0);
591
	creqwait = chancreate(sizeof(void*), 0);
592
	cclunk = chancreate(sizeof(void*), 0);
593
	cclunkwait = chancreate(sizeof(void*), 0);
594
	procrfork(fsthread, nil, STACK, RFNAMEG);
595
}
596
 
597
void
598
takedown(Srv*)
599
{
600
	closecookies();
601
	threadexitsall("done");
602
}
603
 
604
Srv fs = 
605
{
606
.attach=		fssend,
607
.destroyfid=	fsdestroyfid,
608
.walk1=		fswalk1,
609
.open=		fssend,
610
.read=		fssend,
611
.write=		fssend,
612
.stat=		fssend,
613
.flush=		fssend,
614
.end=		takedown,
615
};
616