Browse Source

Plan 9 from Bell Labs 2005-10-07

David du Colombier 15 years ago
parent
commit
8655cf76ba
5 changed files with 708 additions and 52 deletions
  1. 25 48
      dist/replica/_plan9.db
  2. 2 2
      dist/replica/plan9.db
  3. 2 0
      dist/replica/plan9.log
  4. 21 2
      mail/lib/validateattachment
  5. 658 0
      sys/src/cmd/ip/httpfile.c

+ 25 - 48
dist/replica/_plan9.db

@@ -49,7 +49,7 @@
 386/bin/auth/changeuser - 775 sys sys 1125345945 97017
 386/bin/auth/convkeys - 775 sys sys 1117249742 87396
 386/bin/auth/convkeys2 - 775 sys sys 1117249742 87447
-386/bin/auth/cron - 775 sys sys 1125345946 142881
+386/bin/auth/cron - 775 sys sys 1128568172 143960
 386/bin/auth/debug - 775 sys sys 1125345946 101055
 386/bin/auth/disable - 775 sys sys 1020319057 146
 386/bin/auth/enable - 775 sys sys 1020319057 134
@@ -58,9 +58,9 @@
 386/bin/auth/guard.srv - 775 sys sys 1125345947 142841
 386/bin/auth/iam - 775 sys sys 1085076981 50791
 386/bin/auth/keyfs - 775 sys sys 1117249744 115539
-386/bin/auth/login - 775 sys sys 1125345947 103370
-386/bin/auth/newns - 775 sys sys 1125345947 87941
-386/bin/auth/none - 775 sys sys 1125345947 87938
+386/bin/auth/login - 775 sys sys 1128568172 104451
+386/bin/auth/newns - 775 sys sys 1128568172 89687
+386/bin/auth/none - 775 sys sys 1128568172 89021
 386/bin/auth/pemdecode - 775 sys sys 1115950032 61457
 386/bin/auth/pemencode - 775 sys sys 1115950032 59851
 386/bin/auth/printnetkey - 775 sys sys 1115950032 40474
@@ -76,7 +76,7 @@
 386/bin/auth/secuser - 775 sys sys 1127360568 152942
 386/bin/auth/status - 775 sys sys 1020319060 738
 386/bin/auth/uniq - 775 sys sys 1125345951 63059
-386/bin/auth/warning - 775 sys sys 1125345952 99558
+386/bin/auth/warning - 775 sys sys 1128568173 100637
 386/bin/auth/wrkey - 775 sys sys 1117249745 75165
 386/bin/aux - 20000000775 sys sys 1016920817 0
 386/bin/aux/9pcon - 775 sys sys 1127445048 94916
@@ -107,8 +107,8 @@
 386/bin/aux/hardcopy - 775 sys sys 1020319063 1699
 386/bin/aux/isvmware - 775 sys sys 1032486791 23997
 386/bin/aux/lines - 775 sys sys 1125345956 60483
-386/bin/aux/listen - 775 sys sys 1125345956 106887
-386/bin/aux/listen1 - 775 sys sys 1125345956 93410
+386/bin/aux/listen - 775 sys sys 1128568173 107968
+386/bin/aux/listen1 - 775 sys sys 1128568174 94491
 386/bin/aux/lpdaemon - 775 sys sys 1127360569 168275
 386/bin/aux/lpdsend - 775 sys sys 1127360569 155113
 386/bin/aux/lpsend - 775 sys sys 1115950042 52540
@@ -139,7 +139,7 @@
 386/bin/aux/searchfs - 775 sys sys 1115950049 89556
 386/bin/aux/sprog - 775 sys sys 1125345960 79340
 386/bin/aux/ssh_genkey - 775 sys sys 1045537957 194629
-386/bin/aux/sshserve - 775 sys sys 1127360570 251631
+386/bin/aux/sshserve - 775 sys sys 1128568174 252710
 386/bin/aux/stub - 775 sys sys 1124939599 135175
 386/bin/aux/tcpostio - 775 sys sys 1094040084 200808
 386/bin/aux/text2post - 775 sys sys 1104121986 78336
@@ -184,7 +184,7 @@
 386/bin/con - 775 sys sys 1115950058 78211
 386/bin/cp - 775 sys sys 1115950058 63215
 386/bin/cpp - 775 sys sys 1116903731 149799
-386/bin/cpu - 775 sys sys 1125345964 138019
+386/bin/cpu - 775 sys sys 1128568175 139098
 386/bin/crop - 775 sys sys 1115950059 116008
 386/bin/date - 775 sys sys 1115950059 43261
 386/bin/db - 775 sys sys 1125345965 336317
@@ -219,7 +219,7 @@
 386/bin/ed - 775 sys sys 1125345970 93025
 386/bin/eqn - 775 sys sys 1127360572 243030
 386/bin/execnet - 775 sys sys 1124939601 172009
-386/bin/exportfs - 775 sys sys 1125345970 161162
+386/bin/exportfs - 775 sys sys 1128568175 162241
 386/bin/ext2srv - 775 sys sys 1125345971 173998
 386/bin/faces - 775 sys sys 1115950068 193388
 386/bin/factor - 775 sys sys 1125345971 61466
@@ -274,18 +274,18 @@
 386/bin/ico - 775 sys sys 1125345981 161421
 386/bin/iconv - 775 sys sys 1115950080 113265
 386/bin/idiff - 775 sys sys 1125345981 76103
-386/bin/import - 775 sys sys 1125345982 100290
+386/bin/import - 775 sys sys 1128568175 101371
 386/bin/iostats - 775 sys sys 1127445049 98431
 386/bin/ip - 20000000775 sys sys 1016920851 0
 386/bin/ip/dhcpclient - 775 sys sys 1116126317 94096
 386/bin/ip/dhcpd - 775 sys sys 1125345983 149123
 386/bin/ip/dhcpleases - 775 sys sys 1116126317 86215
-386/bin/ip/ftpd - 775 sys sys 1125345983 169218
+386/bin/ip/ftpd - 775 sys sys 1128568176 170299
 386/bin/ip/gizzard - 775 sys sys 1081480408 101521
 386/bin/ip/gping - 775 sys sys 1116126318 182819
 386/bin/ip/hogports - 775 sys sys 1118632064 42883
 386/bin/ip/httpd - 20000000775 sys sys 1016920846 0
-386/bin/ip/httpd/httpd - 775 sys sys 1127790501 291589
+386/bin/ip/httpd/httpd - 775 sys sys 1128568177 292364
 386/bin/ip/httpd/imagemap - 775 sys sys 1127790501 114798
 386/bin/ip/httpd/man2html - 775 sys sys 1127790501 123085
 386/bin/ip/httpd/netlib_find - 775 sys sys 1127790502 115643
@@ -293,19 +293,19 @@
 386/bin/ip/httpd/save - 775 sys sys 1127790502 131094
 386/bin/ip/httpd/webls - 775 sys sys 1127790503 131239
 386/bin/ip/httpd/wikipost - 775 sys sys 1127790503 112677
-386/bin/ip/imap4d - 775 sys sys 1125345987 236488
+386/bin/ip/imap4d - 775 sys sys 1128568177 237569
 386/bin/ip/ipconfig - 775 sys sys 1126148865 137742
 386/bin/ip/ping - 775 sys sys 1116126319 77010
-386/bin/ip/ppp - 775 sys sys 1125544173 218875
+386/bin/ip/ppp - 775 sys sys 1128568178 219956
 386/bin/ip/pppoe - 775 sys sys 1125544173 77548
 386/bin/ip/pptp - 775 sys sys 1116126320 126658
 386/bin/ip/pptpd - 775 sys sys 1125345988 125824
 386/bin/ip/rarpd - 775 sys sys 1125345990 111572
-386/bin/ip/rexexec - 775 sys sys 1125345991 88894
+386/bin/ip/rexexec - 775 sys sys 1128568178 89977
 386/bin/ip/rip - 775 sys sys 1125345991 91223
 386/bin/ip/rlogind - 775 sys sys 1115950088 67402
-386/bin/ip/telnetd - 775 sys sys 1127531310 121988
-386/bin/ip/tftpd - 775 sys sys 1125345992 129480
+386/bin/ip/telnetd - 775 sys sys 1128568178 123067
+386/bin/ip/tftpd - 775 sys sys 1128568179 130567
 386/bin/ip/traceroute - 775 sys sys 1115950089 73497
 386/bin/ip/udpecho - 775 sys sys 1115950089 43159
 386/bin/join - 775 sys sys 1115950090 114091
@@ -455,14 +455,14 @@
 386/bin/upas/msgcat - 775 sys sys 1064598353 38
 386/bin/upas/msgtok - 775 sys sys 1064598354 75149
 386/bin/upas/nedmail - 775 sys sys 1125346029 156582
-386/bin/upas/pop3 - 775 sys sys 1127360610 261106
+386/bin/upas/pop3 - 775 sys sys 1128568180 262187
 386/bin/upas/qer - 775 sys sys 1125346030 100118
 386/bin/upas/ratfs - 775 sys sys 1125346030 111349
 386/bin/upas/runq - 775 sys sys 1125346030 113769
 386/bin/upas/scanmail - 775 sys sys 1125346031 129036
-386/bin/upas/send - 775 sys sys 1125346032 191814
+386/bin/upas/send - 775 sys sys 1128568180 192895
 386/bin/upas/smtp - 775 sys sys 1127445051 275242
-386/bin/upas/smtpd - 775 sys sys 1127360612 330126
+386/bin/upas/smtpd - 775 sys sys 1128568181 331205
 386/bin/upas/spam - 775 sys sys 1064598366 36
 386/bin/upas/testscan - 775 sys sys 1125346034 83659
 386/bin/upas/token - 775 sys sys 1115950132 77554
@@ -519,7 +519,7 @@
 386/include/ape/ureg.h - 664 sys sys 944946041 812
 386/include/u.h - 664 sys sys 1115924095 1481
 386/include/ureg.h - 664 sys sys 944946012 523
-386/init - 775 sys sys 1125346042 100101
+386/init - 775 sys sys 1128568181 101180
 386/ld.com - 775 sys sys 1109598589 72076
 386/lib - 20000000775 sys sys 1016826328 0
 386/lib/ape - 20000000775 sys sys 944969312 0
@@ -535,7 +535,7 @@
 386/lib/ape/libv.a - 664 sys sys 1122001338 19668
 386/lib/lib9p.a - 664 sys sys 1124939608 79582
 386/lib/libString.a - 664 sys sys 1115950150 22184
-386/lib/libauth.a - 664 sys sys 1115950150 56134
+386/lib/libauth.a - 664 sys sys 1128568181 58648
 386/lib/libauthsrv.a - 664 sys sys 1117249746 34098
 386/lib/libbin.a - 664 sys sys 1115950150 2534
 386/lib/libbio.a - 664 sys sys 1125346045 26064
@@ -5250,7 +5250,7 @@ mail/lib/smtpd.conf - 664 upas upas 961114164 536
 mail/lib/spam.rc - 775 upas upas 1063950954 400
 mail/lib/unspam.rc - 775 upas upas 1063950954 400
 mail/lib/validateaddress - 775 upas upas 1098803974 48
-mail/lib/validateattachment - 775 upas upas 1124885063 680
+mail/lib/validateattachment - 775 upas upas 1128571809 1144
 mail/lib/validatesender - 775 upas upas 1106233741 1128
 mail/lib/white.starter - 664 upas upas 1067739606 326
 mail/queue - 20000000777 upas upas 953302652 0
@@ -11949,7 +11949,7 @@ sys/src/cmd/ip/httpd/sendfd.c - 664 sys sys 1017679317 12134
 sys/src/cmd/ip/httpd/webls.c - 664 sys sys 1120737502 7658
 sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3
 sys/src/cmd/ip/httpd/wikipost.c - 664 sys sys 1083771074 5913
-sys/src/cmd/ip/httpfile.c - 600 sys sys 1128556975 10037
+sys/src/cmd/ip/httpfile.c - 664 sys sys 1128556975 10037
 sys/src/cmd/ip/imap4d - 20000000775 sys sys 988249981 0
 sys/src/cmd/ip/imap4d/auth.c - 664 sys sys 1015013075 3510
 sys/src/cmd/ip/imap4d/copy.c - 664 sys sys 1062298855 4597
@@ -14967,26 +14967,3 @@ usr/glenda/lib/profile - 664 glenda glenda 1105128663 890
 usr/glenda/readme.acme - 664 glenda glenda 1019860628 4753
 usr/glenda/readme.rio - 664 glenda glenda 1019860628 6370
 usr/glenda/tmp - 20000000775 glenda glenda 1018802620 0
-386/bin/cpu - 775 sys sys 1128568175 139098
-386/bin/exportfs - 775 sys sys 1128568175 162241
-386/bin/import - 775 sys sys 1128568175 101371
-386/bin/auth/cron - 775 sys sys 1128568172 143960
-386/bin/auth/login - 775 sys sys 1128568172 104451
-386/bin/auth/newns - 775 sys sys 1128568172 89687
-386/bin/auth/none - 775 sys sys 1128568172 89021
-386/bin/auth/warning - 775 sys sys 1128568173 100637
-386/bin/aux/listen - 775 sys sys 1128568173 107968
-386/bin/aux/listen1 - 775 sys sys 1128568174 94491
-386/bin/aux/sshserve - 775 sys sys 1128568174 252710
-386/bin/ip/ftpd - 775 sys sys 1128568176 170299
-386/bin/ip/httpd/httpd - 775 sys sys 1128568177 292364
-386/bin/ip/imap4d - 775 sys sys 1128568177 237569
-386/bin/ip/ppp - 775 sys sys 1128568178 219956
-386/bin/ip/rexexec - 775 sys sys 1128568178 89977
-386/bin/ip/telnetd - 775 sys sys 1128568178 123067
-386/bin/ip/tftpd - 775 sys sys 1128568179 130567
-386/bin/upas/pop3 - 775 sys sys 1128568180 262187
-386/bin/upas/send - 775 sys sys 1128568180 192895
-386/bin/upas/smtpd - 775 sys sys 1128568181 331205
-386/init - 775 sys sys 1128568181 101180
-386/lib/libauth.a - 664 sys sys 1128568181 58648

+ 2 - 2
dist/replica/plan9.db

@@ -5250,7 +5250,7 @@ mail/lib/smtpd.conf - 664 upas upas 961114164 536
 mail/lib/spam.rc - 775 upas upas 1063950954 400
 mail/lib/unspam.rc - 775 upas upas 1063950954 400
 mail/lib/validateaddress - 775 upas upas 1098803974 48
-mail/lib/validateattachment - 775 upas upas 1124885063 680
+mail/lib/validateattachment - 775 upas upas 1128571809 1144
 mail/lib/validatesender - 775 upas upas 1106233741 1128
 mail/lib/white.starter - 664 upas upas 1067739606 326
 mail/queue - 20000000777 upas upas 953302652 0
@@ -11949,7 +11949,7 @@ sys/src/cmd/ip/httpd/sendfd.c - 664 sys sys 1017679317 12134
 sys/src/cmd/ip/httpd/webls.c - 664 sys sys 1120737502 7658
 sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3
 sys/src/cmd/ip/httpd/wikipost.c - 664 sys sys 1083771074 5913
-sys/src/cmd/ip/httpfile.c - 600 sys sys 1128556975 10037
+sys/src/cmd/ip/httpfile.c - 664 sys sys 1128556975 10037
 sys/src/cmd/ip/imap4d - 20000000775 sys sys 988249981 0
 sys/src/cmd/ip/imap4d/auth.c - 664 sys sys 1015013075 3510
 sys/src/cmd/ip/imap4d/copy.c - 664 sys sys 1062298855 4597

+ 2 - 0
dist/replica/plan9.log

@@ -21586,3 +21586,5 @@
 1128569448 20 c 386/bin/upas/smtpd - 775 sys sys 1128568181 331205
 1128569448 21 c 386/init - 775 sys sys 1128568181 101180
 1128569448 22 c 386/lib/libauth.a - 664 sys sys 1128568181 58648
+1128573048 0 c mail/lib/validateattachment - 775 upas upas 1128571809 1144
+1128607256 0 m sys/src/cmd/ip/httpfile.c - 664 sys sys 1128556975 10037

+ 21 - 2
mail/lib/validateattachment

@@ -1,4 +1,5 @@
 #!/bin/rc
+rfork n
 
 # exit status matching:
 #
@@ -13,23 +14,40 @@ if(! ~ $#* 1){
 }
 
 echo validating >[1=2]
+9fs other
+fn save {
+	d=`{date -n}
+	cp body /n/other/upas/tmp/$d.$1
+	cp raw /n/other/upas/tmp/$d.$1.raw
+	whatis x >/n/other/upas/tmp/$d.$1.file
+}
 
 upas/fs -f $1
 cd /mail/fs/mbox/1
 
-x=`{file body}
+x=`{file body | sed s/body://}
 x=$"x
 switch($x){
 case *Ascii* *text* *'c program'* *'rc executable'* 
+	save accept
 	exit accept
 
 case *'zip archive'*
-	if(unzip -tsf body | grep -si '      |\.(scr|exe|pif|bat|com)$'){
+	# >[2=1] because sometimes we get zip files we can't parse
+	# but the errors look like
+	# unzip: reading data for philw.doc.scr failed: ...
+	# so we can still catch these.
+	if(unzip -tsf body >[2=1] | grep -si '      |\.(scr|exe|pif|bat|com)$'){
 		echo executables inside zip file!
 		exit discard
 	}
 
+case jpeg 'PNG image' bmp 'GIF image' *'plan 9 image'
+	save accept
+	exit accept
+
 case *Microsoft* *Office*
+	save wrap
 	exit wrap
 
 case *MSDOS*
@@ -38,6 +56,7 @@ case *MSDOS*
 	exit discard
 }
 
+save wrap
 exit wrap
 
 

+ 658 - 0
sys/src/cmd/ip/httpfile.c

@@ -0,0 +1,658 @@
+/* contributed by 20h@r-36.net, September 2005 */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ndb.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include <mp.h>
+#include <libsec.h>
+
+enum
+{
+	Blocksize = 64*1024,
+	Stacksize = 8192,
+};
+
+char *host;
+char *file;
+char *port;
+char *url;
+char *get;
+char *user;
+char *net = "net";
+
+vlong size;
+int usetls;
+int debug;
+int ncache;
+int mcache;
+
+void
+usage(void)
+{
+	fprint(2, "usage: httpfile [-Dd] [-b count] [-f file] [-m mtpt] [-s srvname] [-x net] url\n");
+	exits("usage");
+}
+
+enum
+{
+	Qroot,
+	Qfile,
+};
+
+#define PATH(type, n)		((type)|((n)<<8))
+#define TYPE(path)			((int)(path) & 0xFF)
+#define NUM(path)			((uint)(path)>>8)
+
+Channel *reqchan;
+Channel *httpchan;
+Channel *finishchan;
+ulong time0;
+
+typedef struct Block Block;
+struct Block
+{
+	uchar *p;
+	vlong off;
+	vlong len;
+	Block *link;
+	long lastuse;
+	Req *rq;
+	Req **erq;
+};
+
+typedef struct Blocklist Blocklist;
+struct Blocklist
+{
+	Block *first;
+	Block **end;
+};
+
+Blocklist cache;
+Blocklist inprogress;
+
+void
+queuereq(Block *b, Req *r)
+{
+	if(b->rq==nil)
+		b->erq = &b->rq;
+	*b->erq = r;
+	r->aux = nil;
+	b->erq = (Req**)&r->aux;
+}
+
+void
+addblock(Blocklist *l, Block *b)
+{
+	if(debug)
+		print("adding: %p %lld\n", b, b->off);
+
+	if(l->first == nil)
+		l->end = &l->first;
+	*l->end = b;
+	b->link = nil;
+	l->end = &b->link;
+	b->lastuse = time(0);
+}
+
+void
+delreq(Block *b, Req *r)
+{
+	Req **l;
+
+	for(l = &b->rq; *l; l = (Req**)&(*l)->aux){
+		if(*l == r){
+			*l = r->aux;
+			if(*l == nil)
+				b->erq = l;
+			free(r);
+			return;
+		}
+	}
+}
+
+void
+evictblock(Blocklist *cache)
+{
+	Block **l, **oldest, *b;
+
+	if(cache->first == nil)
+		return;
+
+	oldest = nil;
+	for(l=&cache->first; *l; l=&(*l)->link)
+		if(oldest == nil || (*oldest)->lastuse > (*l)->lastuse)
+			oldest = l;
+
+	b = *oldest;
+	*oldest = (*oldest)->link;
+	if(*oldest == nil)
+		cache->end = oldest;
+	free(b->p);
+	free(b);
+	ncache--;
+}
+
+Block *
+findblock(Blocklist *s, vlong off)
+{
+	Block *b;
+
+	for(b = s->first; b != nil; b = b->link){
+		if(b->off <= off && off < b->off + Blocksize){
+			if(debug)
+				print("found: %lld -> %lld\n", off, b->off);
+			b->lastuse = time(0);
+			return b;
+		}
+	}
+
+	return nil;
+}
+
+void
+readfrom(Req *r, Block *b)
+{
+	int d, n;
+
+	b->lastuse = time(0);
+
+	n = r->ifcall.count;
+	d = r->ifcall.offset - b->off;
+	if(b->off + d + n > b->off + b->len)
+		n = b->len - d;
+	if(debug)
+		print("Reading from: %p %d %d\n", b->p, d, n);
+	memmove(r->ofcall.data, b->p + d, n);
+	r->ofcall.count = n;
+
+	respond(r, nil);
+}
+
+void
+hangupclient(Srv*)
+{
+	if(debug)
+		print("Hangup.\n");
+
+	threadexitsall("done");
+}
+
+int
+dotls(int fd)
+{
+	TLSconn conn;
+
+	if((fd=tlsClient(fd, &conn)) < 0)
+		sysfatal("tlsclient: %r");
+
+	if(conn.cert != nil)
+		free(conn.cert);
+
+	return fd;
+}
+
+char*
+nocr(char *s)
+{
+	char *r, *w;
+
+	for(r=w=s; *r; r++)
+		if(*r != '\r')
+			*w++ = *r;
+	*w = 0;
+	return s;
+}
+
+char*
+readhttphdr(Biobuf *netbio, vlong *size)
+{
+	char *s, *stat;
+
+	stat = nil;
+	while((s = Brdstr(netbio, '\n', 1)) != nil && s[0] != '\r'
+			&& s[0] != '\0'){
+		if(stat == nil)
+			stat = estrdup9p(s);
+		if(strncmp(s, "Content-Length: ", 16) == 0 && size != nil)
+			*size = atoll(s + 16);
+		free(s);
+	}
+	if(stat)
+		nocr(stat);
+
+	return stat;
+}
+
+int
+dialhttp(Biobuf *netbio)
+{
+	int netfd;
+
+	netfd = dial(netmkaddr(host, net, port), 0, 0, 0);
+	if(netfd < 0)
+		sysfatal("dial: %r");
+	if(usetls)
+		netfd = dotls(netfd);
+	Binit(netbio, netfd, OREAD);
+
+	return netfd;
+}
+
+uchar*
+getrange(Block *b)
+{
+	uchar *data;
+	char *status;
+	int netfd;
+	static Biobuf netbio;
+
+	b->len = Blocksize;
+	if(b->off + b->len > size)
+		b->len = size - b->off;
+
+	if(debug)
+		print("getrange: %lld %lld\n", b->off, b->len);
+
+	netfd = dialhttp(&netbio);
+
+	fprint(netfd, 
+		"GET %s HTTP/1.1\r\n"
+		"Host: %s\r\n"
+		"Accept-Encoding:\r\n"
+		"Range: bytes=%lld-%lld\r\n"
+		"\r\n",
+		get, host, b->off, b->off+b->len);
+	Bflush(&netbio);
+
+	status = readhttphdr(&netbio, nil);
+	if(status == nil)
+		return nil;
+
+	/*
+	 * Some servers (e.g., www.google.com) return 200 OK
+	 * when you ask for the entire page in one range.
+	 */
+	if(strstr(status, "206 Partial Content")==nil
+	&& (b->off!=0 || b->len!=size || strstr(status, "200 OK")==nil)){
+		free(status);
+		close(netfd);
+		werrstr("did not get requested range");
+		return nil;
+	}
+	free(status);
+
+	data = emalloc9p(b->len);
+	if(Bread(&netbio, data, b->len) != b->len){
+		free(data);
+		close(netfd);
+		werrstr("not enough bytes read");
+		return nil;
+	}
+
+	b->p = data;
+
+	close(netfd);
+	return data;
+}
+
+void
+httpfilereadproc(void*)
+{
+	Block *b;
+
+	threadsetname("httpfilereadproc");
+
+	for(;;){
+		b = recvp(httpchan);
+		if(b == nil)
+			continue;
+		if(getrange(b) == nil)
+			sysfatal("getrange: %r");
+		sendp(finishchan, b);
+	}
+}
+
+typedef struct Tab Tab;
+struct Tab
+{
+	char *name;
+	ulong mode;
+};
+
+Tab tab[] =
+{
+	"/",		DMDIR|0555,
+	nil,		0444,
+};
+
+static void
+fillstat(Dir *d, uvlong path)
+{
+	Tab *t;
+
+	memset(d, 0, sizeof(*d));
+	d->uid = estrdup9p(user);
+	d->gid = estrdup9p(user);
+	d->qid.path = path;
+	d->atime = d->mtime = time0;
+	t = &tab[TYPE(path)];
+	d->name = estrdup9p(t->name);
+	d->length = size;
+	d->qid.type = t->mode>>24;
+	d->mode = t->mode;
+}
+
+static void
+fsattach(Req *r)
+{
+	if(r->ifcall.aname && r->ifcall.aname[0]){
+		respond(r, "invalid attach specifier");
+		return;
+	}
+	r->fid->qid.path = PATH(Qroot, 0);
+	r->fid->qid.type = QTDIR;
+	r->fid->qid.vers = 0;
+	r->ofcall.qid = r->fid->qid;
+	respond(r, nil);
+}
+
+static void
+fsstat(Req *r)
+{
+	fillstat(&r->d, r->fid->qid.path);
+	respond(r, nil);
+}
+
+static int
+rootgen(int i, Dir *d, void*)
+{
+	i += Qroot + 1;
+	if(i <= Qfile){
+		fillstat(d, i);
+		return 0;
+	}
+	return -1;
+}
+
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+	int i;
+	ulong path;
+
+	path = fid->qid.path;
+	if(!(fid->qid.type & QTDIR))
+		return "walk in non-directory";
+
+	if(strcmp(name, "..") == 0){
+		switch(TYPE(path)){
+		case Qroot:
+			return nil;
+		default:
+			return "bug in fswalk1";
+		}
+	}
+
+	i = TYPE(path) + 1;
+	while(i < nelem(tab)){
+		if(strcmp(name, tab[i].name) == 0){
+			qid->path = PATH(i, NUM(path));
+			qid->type = tab[i].mode>>24;
+			return nil;
+		}
+		if(tab[i].mode & DMDIR)
+			break;
+		i++;
+	}
+	return "directory entry not found";
+}
+
+vlong
+getfilesize(void)
+{
+	char *status;
+	vlong size;
+	int netfd;
+	static Biobuf netbio;
+
+	netfd = dialhttp(&netbio);
+
+	fprint(netfd, 
+		"HEAD %s HTTP/1.1\r\n"
+		"Host: %s\r\n"
+		"Accept-Encoding:\r\n"
+		"\r\n",
+		get, host);
+
+	status = readhttphdr(&netbio, &size);
+	if(strstr(status, "200 OK") == nil){
+		werrstr("%s", status);
+		size = -1;
+	}
+	free(status);
+
+	close(netfd);
+	return size;
+}
+
+void
+fileread(Req *r)
+{
+	Block *b;
+
+	if(r->ifcall.offset > size){
+		respond(r, nil);
+		return;
+	}
+
+	if((b = findblock(&cache, r->ifcall.offset)) != nil){
+		readfrom(r, b);
+		return;
+	}
+	if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
+		b = emalloc9p(sizeof(Block));
+		b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
+		addblock(&inprogress, b);
+		if(inprogress.first == b)
+			sendp(httpchan, b);
+	}
+	queuereq(b, r);
+}
+
+static void
+fsopen(Req *r)
+{
+	if(r->ifcall.mode != OREAD){
+		respond(r, "permission denied");
+		return;
+	}
+	respond(r, nil);
+}
+
+void
+finishthread(void*)
+{
+	Block *b;
+	Req *r, *nextr;
+
+	threadsetname("finishthread");
+
+	for(;;){
+		b = recvp(finishchan);
+		assert(b == inprogress.first);
+		inprogress.first = b->link;
+		ncache++;
+		if(ncache >= mcache)
+			evictblock(&cache);
+		addblock(&cache, b);
+		for(r=b->rq; r; r=nextr){
+			nextr = r->aux;
+			readfrom(r, b);
+		}
+		b->rq = nil;
+		if(inprogress.first)
+			sendp(httpchan, inprogress.first);
+	}
+}
+
+void
+fsnetproc(void*)
+{
+	Req *r;
+	Block *b;
+
+	threadcreate(finishthread, nil, 8192);
+
+	threadsetname("fsnetproc");
+
+	for(;;){
+		r = recvp(reqchan);
+		switch(r->ifcall.type){
+		case Tflush:
+			b = findblock(&inprogress, r->ifcall.offset);
+			delreq(b, r->oldreq);
+			respond(r->oldreq, "interrupted");
+			respond(r, nil);
+			break;
+		case Tread:
+			fileread(r);
+			break;
+		default:
+			respond(r, "bug in fsthread");
+			break;
+		}
+	}
+}
+
+static void
+fsflush(Req *r)
+{
+	sendp(reqchan, r);
+}
+
+static void
+fsread(Req *r)
+{
+	char e[ERRMAX];
+	ulong path;
+
+	path = r->fid->qid.path;
+	switch(TYPE(path)){
+	case Qroot:
+		dirread9p(r, rootgen, nil);
+		respond(r, nil);
+		break;
+	case Qfile:
+		sendp(reqchan, r);
+		break;
+	default:
+		snprint(e, sizeof(e), "bug in fsread path=%lux", path);
+		respond(r, e);
+		break;
+	}
+}
+
+Srv fs = 
+{
+.attach=		fsattach,
+.walk1=		fswalk1,
+.open=		fsopen,
+.read=		fsread,
+.stat=		fsstat,
+.flush=		fsflush,
+.end=		hangupclient,
+};
+
+void
+threadmain(int argc, char **argv)
+{
+	char *defport, *mtpt, *srvname, *p;
+
+	mtpt = nil;
+	srvname = nil;
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 'd':
+		debug++;
+		break;
+	case 's':
+		srvname = EARGF(usage());
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 'c':
+		mcache = atoi(EARGF(usage()));
+		break;
+	case 'f':
+		file = EARGF(usage());
+		break;
+	case 'x':
+		net = smprint("%s/net", EARGF(usage()));
+		break;
+	default:
+		usage();
+	}ARGEND;
+
+	if(srvname == nil && mtpt == nil)
+		mtpt = ".";
+
+	if(argc < 1)
+		usage();
+	if(mcache <= 0)
+		mcache = 32;
+
+	time0 = time(0);
+	host = url = estrdup9p(argv[0]);
+
+	defport = nil;
+	if(!cistrncmp(url, "https://", 8)){
+		host += 8;
+		usetls = 1;
+		defport = "https";
+	}else if(!cistrncmp(url, "http://", 7)){
+		host += 7;
+		defport = "http";
+	}else
+		sysfatal("unsupported url: %s", url);
+
+	if((p = strchr(host, '/')) != nil){
+		get = estrdup9p(p);
+		*p = '\0';
+	}else
+		get = "/";
+
+	port = strchr(host, ':');
+	if(port != nil)
+		*port++ = '\0';
+	else
+		port = defport;
+
+	if(file == nil){
+		file = strrchr(get, '/')+1;
+		if(*file == 0)
+			file = "index";
+	}
+
+	tab[Qfile].name = file;
+	user = getuser();
+	size = getfilesize();
+	if(size < 0)
+		sysfatal("getfilesize: %r");
+
+	reqchan = chancreate(sizeof(Req*), 0);
+	httpchan = chancreate(sizeof(Block*), 0);
+	finishchan = chancreate(sizeof(Block*), 0);
+
+	procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
+	procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
+
+	threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);
+	threadexits(0);
+}