Browse Source

Plan 9 from Bell Labs 2003-02-19

David du Colombier 19 years ago
parent
commit
4428380291
45 changed files with 11508 additions and 319 deletions
  1. 47 23
      dist/replica/plan9.db
  2. 48 0
      dist/replica/plan9.log
  3. 811 0
      sys/include/nfs3.h
  4. 395 0
      sys/include/sunrpc.h
  5. 146 0
      sys/man/4/nfs
  6. 29 7
      sys/man/8/fs
  7. 1 1
      sys/src/cmd/acme/util.c
  8. 2 1
      sys/src/cmd/aux/mkfile
  9. 334 0
      sys/src/cmd/aux/nfsmount.c
  10. 204 0
      sys/src/cmd/aux/portmap.c
  11. 54 22
      sys/src/cmd/fossil/9.h
  12. 11 4
      sys/src/cmd/fossil/9auth.c
  13. 18 1
      sys/src/cmd/fossil/9fid.c
  14. 11 29
      sys/src/cmd/fossil/9p.c
  15. 536 206
      sys/src/cmd/fossil/9proc.c
  16. 17 10
      sys/src/cmd/fossil/9srv.c
  17. 7 9
      sys/src/cmd/fossil/9user.c
  18. 1 1
      sys/src/cmd/fossil/Ccmd.c
  19. 1 1
      sys/src/cmd/fossil/archive.c
  20. 51 0
      sys/src/cmd/fossil/epoch.c
  21. 2 1
      sys/src/cmd/fossil/fossil.c
  22. 1 1
      sys/src/cmd/fossil/fs.c
  23. 9 0
      sys/src/cmd/fossil/history
  24. 0 1
      sys/src/cmd/fossil/mkfile
  25. 7 0
      sys/src/cmd/fossil/source.c
  26. 1602 0
      sys/src/cmd/nfs.c
  27. 8 0
      sys/src/cmd/page/page.h
  28. 1 1
      sys/src/cmd/vnc/vncs.c
  29. 29 0
      sys/src/libsunrpc/COPYING
  30. 60 0
      sys/src/libsunrpc/authunix.c
  31. 478 0
      sys/src/libsunrpc/client.c
  32. 34 0
      sys/src/libsunrpc/emalloc.c
  33. 37 0
      sys/src/libsunrpc/error.c
  34. 107 0
      sys/src/libsunrpc/fd.c
  35. 64 0
      sys/src/libsunrpc/fmt.c
  36. 29 0
      sys/src/libsunrpc/mkfile
  37. 727 0
      sys/src/libsunrpc/mount3.c
  38. 57 0
      sys/src/libsunrpc/net.c
  39. 4045 0
      sys/src/libsunrpc/nfs3.c
  40. 498 0
      sys/src/libsunrpc/portmap.c
  41. 71 0
      sys/src/libsunrpc/prog.c
  42. 528 0
      sys/src/libsunrpc/rpc.c
  43. 277 0
      sys/src/libsunrpc/server.c
  44. 112 0
      sys/src/libsunrpc/udp.c
  45. 1 0
      sys/src/mkfile

+ 47 - 23
dist/replica/plan9.db

@@ -23,7 +23,7 @@
 386/bin/9660srv - 775 sys sys 1045537932 105209
 386/bin/aan - 775 sys sys 1045537933 128318
 386/bin/acid - 775 sys sys 1045537934 377954
-386/bin/acme - 775 sys sys 1045537935 416883
+386/bin/acme - 775 sys sys 1045546062 416883
 386/bin/ape - 20000000775 sys sys 1016944144 0
 386/bin/ape/basename - 775 sys sys 1038443079 135228
 386/bin/ape/cc - 775 sys sys 1045537935 68930
@@ -298,7 +298,7 @@
 386/bin/nntpfs - 775 sys sys 1045538038 161729
 386/bin/ns - 775 sys sys 1039758580 63830
 386/bin/p - 775 sys sys 1043589869 63785
-386/bin/page - 775 sys sys 1045538038 215658
+386/bin/page - 775 sys sys 1045606971 215429
 386/bin/paqfs - 775 sys sys 1045538039 108076
 386/bin/passwd - 775 sys sys 1039758581 81058
 386/bin/pbd - 775 sys sys 1038443175 4663
@@ -3188,7 +3188,7 @@ sys/doc/venti/venti.pdf - 755 sys sys 1020384352 139090
 sys/doc/venti/venti.ps - 664 sys sys 1019852320 2012620
 sys/games - 20000000775 sys sys 952648872 0
 sys/games/lib - 20000000775 sys sys 952648879 0
-sys/games/lib/fortunes - 664 sys sys 1045504123 238318
+sys/games/lib/fortunes - 664 sys sys 1045543258 238358
 sys/include - 20000000775 sys sys 1016902416 0
 sys/include/9p.h - 664 sys sys 1044836327 4309
 sys/include/String.h - 664 sys sys 1014929061 1133
@@ -3276,6 +3276,7 @@ sys/include/memlayer.h - 664 sys sys 1014929065 1810
 sys/include/mouse.h - 664 sys sys 1035232010 1003
 sys/include/mp.h - 664 sys sys 1014929065 4610
 sys/include/ndb.h - 664 sys sys 1032058245 3781
+sys/include/nfs3.h - 664 sys sys 1045589438 15082
 sys/include/plumb.h - 664 sys sys 1014929065 989
 sys/include/pool.h - 664 sys sys 1032058257 1156
 sys/include/rdbg.h - 664 sys sys 1014929066 95
@@ -3283,6 +3284,7 @@ sys/include/realtime.h - 664 sys sys 1037669237 869
 sys/include/regexp.h - 664 sys sys 1014929066 1308
 sys/include/scribble.h - 664 sys sys 1014929066 679
 sys/include/stdio.h - 664 sys sys 1014929066 4145
+sys/include/sunrpc.h - 664 sys sys 1045589438 7174
 sys/include/thread.h - 664 sys sys 1045504289 3562
 sys/include/venti.h - 664 sys sys 1045504293 6956
 sys/lib - 20000000775 sys sys 1018580948 0
@@ -4748,6 +4750,7 @@ sys/man/4/kfs - 664 sys sys 1019058716 2159
 sys/man/4/lnfs - 664 sys sys 1018973955 1006
 sys/man/4/mntgen - 664 sys sys 1043680799 473
 sys/man/4/namespace - 664 sys sys 1018386777 6916
+sys/man/4/nfs - 664 sys sys 1045589694 2739
 sys/man/4/nntpfs - 664 sys sys 1032632346 2771
 sys/man/4/paqfs - 664 sys sys 1017723482 1541
 sys/man/4/plumber - 664 sys sys 944959699 2748
@@ -4838,7 +4841,7 @@ sys/man/8/cron - 664 sys sys 944959679 1750
 sys/man/8/dhcpd - 664 sys sys 1032654987 5237
 sys/man/8/drawterm - 664 sys sys 958419689 2458
 sys/man/8/fossilcons - 664 sys sys 1044478450 13360
-sys/man/8/fs - 664 sys sys 1045501599 14286
+sys/man/8/fs - 664 sys sys 1045570953 14693
 sys/man/8/fsconfig - 664 sys sys 1045501600 8142
 sys/man/8/httpd - 664 sys sys 1037690024 4516
 sys/man/8/init - 664 sys sys 944959679 1430
@@ -6401,7 +6404,7 @@ sys/src/cmd/acme/rows.c - 664 sys sys 1016833877 14726
 sys/src/cmd/acme/scrl.c - 664 sys sys 1014926095 3072
 sys/src/cmd/acme/text.c - 664 sys sys 1045504840 24133
 sys/src/cmd/acme/time.c - 664 sys sys 1014926095 1783
-sys/src/cmd/acme/util.c - 664 sys sys 1044626080 6433
+sys/src/cmd/acme/util.c - 664 sys sys 1045546057 6435
 sys/src/cmd/acme/wind.c - 664 sys sys 1016833877 11063
 sys/src/cmd/acme/xfid.c - 664 sys sys 1044626081 19191
 sys/src/cmd/ar.c - 664 sys sys 1014926693 23801
@@ -6554,7 +6557,7 @@ sys/src/cmd/aux/lines.c - 664 sys sys 1015008782 546
 sys/src/cmd/aux/lis - 775 sys sys 944960794 45
 sys/src/cmd/aux/listen.c - 664 sys sys 1015008684 8139
 sys/src/cmd/aux/listen1.c - 664 sys sys 1024375142 2016
-sys/src/cmd/aux/mkfile - 664 sys sys 1037575305 962
+sys/src/cmd/aux/mkfile - 664 sys sys 1045589265 956
 sys/src/cmd/aux/mklatinkbd.c - 664 sys sys 953253425 3813
 sys/src/cmd/aux/mnihongo - 20000000775 sys sys 944960789 0
 sys/src/cmd/aux/mnihongo/README - 664 sys sys 944960789 475
@@ -6568,8 +6571,10 @@ sys/src/cmd/aux/na - 20000000775 sys sys 955036627 0
 sys/src/cmd/aux/na/mkfile - 664 sys sys 955036627 121
 sys/src/cmd/aux/na/na.h - 775 sys sys 955036627 267
 sys/src/cmd/aux/na/na.y - 775 sys sys 1015008889 25158
+sys/src/cmd/aux/nfsmount.c - 664 sys sys 1045589268 6202
 sys/src/cmd/aux/olefs.c - 664 sys sys 1014925094 9627
 sys/src/cmd/aux/pcmcia.c - 664 sys sys 1014925094 8223
+sys/src/cmd/aux/portmap.c - 664 sys sys 1045589269 3617
 sys/src/cmd/aux/rdwr.c - 664 sys sys 1016833876 811
 sys/src/cmd/aux/reboot.c - 664 sys sys 1014925091 1411
 sys/src/cmd/aux/searchfs.c - 664 sys sys 1014925091 18176
@@ -6999,23 +7004,23 @@ sys/src/cmd/file.c - 664 sys sys 1044560289 20196
 sys/src/cmd/fmt.c - 664 sys sys 1025298248 3897
 sys/src/cmd/fortune.c - 664 sys sys 1035832953 1674
 sys/src/cmd/fossil - 20000000775 sys sys 1042005512 0
-sys/src/cmd/fossil/9.h - 664 sys sys 1042311685 3412
-sys/src/cmd/fossil/9auth.c - 664 sys sys 1042311686 3023
+sys/src/cmd/fossil/9.h - 664 sys sys 1045576809 3987
+sys/src/cmd/fossil/9auth.c - 664 sys sys 1045600016 3149
 sys/src/cmd/fossil/9dir.c - 664 sys sys 1042005502 1995
 sys/src/cmd/fossil/9excl.c - 664 sys sys 1042005502 1887
-sys/src/cmd/fossil/9fid.c - 664 sys sys 1042005502 5236
+sys/src/cmd/fossil/9fid.c - 664 sys sys 1045600016 5521
 sys/src/cmd/fossil/9fsys.c - 664 sys sys 1044906157 26913
 sys/src/cmd/fossil/9lstn.c - 664 sys sys 1042005503 2865
-sys/src/cmd/fossil/9p.c - 664 sys sys 1042005503 21328
+sys/src/cmd/fossil/9p.c - 664 sys sys 1045600099 21080
 sys/src/cmd/fossil/9ping.c - 664 sys sys 1042005503 1563
-sys/src/cmd/fossil/9proc.c - 664 sys sys 1042005503 7358
-sys/src/cmd/fossil/9srv.c - 664 sys sys 1042005504 3215
-sys/src/cmd/fossil/9user.c - 664 sys sys 1042311687 17473
+sys/src/cmd/fossil/9proc.c - 664 sys sys 1045576808 13707
+sys/src/cmd/fossil/9srv.c - 664 sys sys 1045600018 3291
+sys/src/cmd/fossil/9user.c - 664 sys sys 1045600019 17457
 sys/src/cmd/fossil/Ccli.c - 664 sys sys 1042005504 1624
-sys/src/cmd/fossil/Ccmd.c - 664 sys sys 1042005504 7169
+sys/src/cmd/fossil/Ccmd.c - 664 sys sys 1045600019 7160
 sys/src/cmd/fossil/Ccons.c - 664 sys sys 1042005504 6524
 sys/src/cmd/fossil/Clog.c - 664 sys sys 1042005505 591
-sys/src/cmd/fossil/archive.c - 664 sys sys 1042005505 9083
+sys/src/cmd/fossil/archive.c - 664 sys sys 1045600020 9112
 sys/src/cmd/fossil/build - 664 sys sys 1042005505 449
 sys/src/cmd/fossil/buildsh - 775 sys sys 1042005505 561
 sys/src/cmd/fossil/bwatch.c - 664 sys sys 1042005505 6754
@@ -7024,6 +7029,7 @@ sys/src/cmd/fossil/dat.h - 664 sys sys 1042311689 7790
 sys/src/cmd/fossil/deadlock - 775 sys sys 1042005506 413
 sys/src/cmd/fossil/disk.c - 664 sys sys 1042497769 5736
 sys/src/cmd/fossil/dump.c - 664 sys sys 1042005506 1340
+sys/src/cmd/fossil/epoch.c - 664 sys sys 1045600021 997
 sys/src/cmd/fossil/error.c - 664 sys sys 1042005507 1367
 sys/src/cmd/fossil/error.h - 664 sys sys 1042005507 744
 sys/src/cmd/fossil/file.c - 664 sys sys 1044906157 27633
@@ -7032,16 +7038,16 @@ sys/src/cmd/fossil/flfmt.c - 664 sys sys 1042005507 10314
 sys/src/cmd/fossil/flproto - 664 sys sys 1042005508 210
 sys/src/cmd/fossil/fns.h - 664 sys sys 1042311690 2958
 sys/src/cmd/fossil/fossil-acid - 664 sys sys 1042005508 3965
-sys/src/cmd/fossil/fossil.c - 664 sys sys 1042005508 1256
-sys/src/cmd/fossil/fs.c - 664 sys sys 1042311691 17030
+sys/src/cmd/fossil/fossil.c - 664 sys sys 1045600029 1267
+sys/src/cmd/fossil/fs.c - 664 sys sys 1045600030 17059
 sys/src/cmd/fossil/fs.h - 664 sys sys 1042005509 1222
-sys/src/cmd/fossil/history - 664 sys sys 1042311694 881
+sys/src/cmd/fossil/history - 664 sys sys 1045600133 1150
 sys/src/cmd/fossil/invariants - 664 sys sys 1042005509 4073
-sys/src/cmd/fossil/mkfile - 664 sys sys 1042005509 1589
+sys/src/cmd/fossil/mkfile - 664 sys sys 1045576809 1567
 sys/src/cmd/fossil/nobwatch.c - 664 sys sys 1042005509 329
 sys/src/cmd/fossil/pack.c - 664 sys sys 1042005510 4683
 sys/src/cmd/fossil/periodic.c - 664 sys sys 1042005510 1091
-sys/src/cmd/fossil/source.c - 664 sys sys 1042311692 18406
+sys/src/cmd/fossil/source.c - 664 sys sys 1045576810 18668
 sys/src/cmd/fossil/srcload.c - 664 sys sys 1042005510 4178
 sys/src/cmd/fossil/stdinc.h - 664 sys sys 1042005510 155
 sys/src/cmd/fossil/trunc.c - 664 sys sys 1042005511 280
@@ -9212,6 +9218,7 @@ sys/src/cmd/ndb/query.c - 664 sys sys 957402054 1133
 sys/src/cmd/ndb/time.c - 664 sys sys 957402055 321
 sys/src/cmd/netstat.c - 664 sys sys 1038237140 3796
 sys/src/cmd/news.c - 664 sys sys 1014926614 3778
+sys/src/cmd/nfs.c - 664 sys sys 1045589249 30446
 sys/src/cmd/nm.c - 664 sys sys 1014926696 4908
 sys/src/cmd/nntpfs.c - 664 sys sys 1037404000 18808
 sys/src/cmd/ns.c - 664 sys sys 984717934 3558
@@ -9223,7 +9230,7 @@ sys/src/cmd/page/gs.c - 664 sys sys 1038517813 6544
 sys/src/cmd/page/mkfile - 664 sys sys 1035998247 411
 sys/src/cmd/page/nrotate.c - 664 sys sys 944961365 5806
 sys/src/cmd/page/page.c - 664 sys sys 1035695486 4292
-sys/src/cmd/page/page.h - 664 sys sys 1038517816 1641
+sys/src/cmd/page/page.h - 664 sys sys 1045606937 2000
 sys/src/cmd/page/pdf.c - 664 sys sys 1038517814 2884
 sys/src/cmd/page/pdfprolog.ps - 664 sys sys 1035695487 699
 sys/src/cmd/page/ps.c - 664 sys sys 1045505467 9176
@@ -10504,7 +10511,7 @@ sys/src/cmd/vnc/rre.c - 664 sys sys 1044880750 11496
 sys/src/cmd/vnc/screen.c - 664 sys sys 1044880750 7537
 sys/src/cmd/vnc/screen.h - 664 sys sys 1044880750 880
 sys/src/cmd/vnc/vnc.h - 664 sys sys 1044880750 2626
-sys/src/cmd/vnc/vncs.c - 664 sys sys 1044880750 20915
+sys/src/cmd/vnc/vncs.c - 664 sys sys 1045597638 20914
 sys/src/cmd/vnc/vncs.h - 664 sys sys 1044880750 890
 sys/src/cmd/vnc/vncv.c - 664 sys sys 1044880750 3093
 sys/src/cmd/vnc/vncv.h - 664 sys sys 1044880750 643
@@ -11661,6 +11668,23 @@ sys/src/libstdio/vfscanf.c - 664 sys sys 944961754 9042
 sys/src/libstdio/vprintf.c - 664 sys sys 944961754 134
 sys/src/libstdio/vsnprintf.c - 664 sys sys 1022112161 256
 sys/src/libstdio/vsprintf.c - 664 sys sys 1022112162 246
+sys/src/libsunrpc - 20000000775 sys sys 1045589225 0
+sys/src/libsunrpc/COPYING - 664 sys sys 1045589219 1269
+sys/src/libsunrpc/authunix.c - 664 sys sys 1045589219 1477
+sys/src/libsunrpc/client.c - 664 sys sys 1045589220 8843
+sys/src/libsunrpc/emalloc.c - 664 sys sys 1045589220 397
+sys/src/libsunrpc/error.c - 664 sys sys 1045589221 819
+sys/src/libsunrpc/fd.c - 664 sys sys 1045589221 1734
+sys/src/libsunrpc/fmt.c - 664 sys sys 1045589222 1146
+sys/src/libsunrpc/mkfile - 664 sys sys 1045589222 318
+sys/src/libsunrpc/mount3.c - 664 sys sys 1045589223 15217
+sys/src/libsunrpc/net.c - 664 sys sys 1045589223 792
+sys/src/libsunrpc/nfs3.c - 664 sys sys 1045589224 93376
+sys/src/libsunrpc/portmap.c - 664 sys sys 1045589224 9758
+sys/src/libsunrpc/prog.c - 664 sys sys 1045589225 1586
+sys/src/libsunrpc/rpc.c - 664 sys sys 1045589225 9235
+sys/src/libsunrpc/server.c - 664 sys sys 1045589225 5187
+sys/src/libsunrpc/udp.c - 664 sys sys 1045589225 2158
 sys/src/libthread - 20000000775 sys sys 1014928161 0
 sys/src/libthread/386.c - 664 sys sys 1014928156 448
 sys/src/libthread/alpha.c - 664 sys sys 1014928156 664
@@ -11722,7 +11746,7 @@ sys/src/libventi/session.h - 664 sys sys 1036470162 913
 sys/src/libventi/strdup.c - 664 sys sys 1045502096 203
 sys/src/libventi/venti.txt - 664 sys sys 1045502097 4347
 sys/src/libventi/zero.c - 664 sys sys 1045502097 1509
-sys/src/mkfile - 664 sys sys 1032061508 980
+sys/src/mkfile - 664 sys sys 1045589241 992
 sys/src/mkfile.proto - 664 sys sys 1045502130 269
 tmp - 20000000555 sys sys 953999902 0
 usr - 20000000775 sys sys 953406542 0

+ 48 - 0
dist/replica/plan9.log

@@ -17872,3 +17872,51 @@
 1045538237 308 c sys/man/1/INDEX - 664 sys sys 1045538129 2900
 1045538237 309 c sys/man/2/INDEX - 664 sys sys 1045538129 20216
 1045538237 310 c sys/man/8/INDEX - 664 sys sys 1045538129 2512
+1045544479 0 c sys/games/lib/fortunes - 664 sys sys 1045543258 238358
+1045546278 0 c 386/bin/acme - 775 sys sys 1045546062 416883
+1045546278 1 c sys/src/cmd/acme/util.c - 664 sys sys 1045546057 6435
+1045571408 0 c sys/man/8/fs - 664 sys sys 1045570953 14693
+1045576810 0 c sys/src/cmd/fossil/9.h - 664 sys sys 1045576809 3987
+1045576810 1 c sys/src/cmd/fossil/9proc.c - 664 sys sys 1045576808 13707
+1045576810 2 c sys/src/cmd/fossil/history - 664 sys sys 1045576810 1085
+1045576810 3 c sys/src/cmd/fossil/mkfile - 664 sys sys 1045576809 1567
+1045576810 4 c sys/src/cmd/fossil/source.c - 664 sys sys 1045576810 18668
+1045589416 0 c sys/src/cmd/aux/mkfile - 664 sys sys 1045589265 956
+1045589416 1 a sys/src/cmd/aux/nfsmount.c - 664 sys sys 1045589268 6202
+1045589416 2 a sys/src/cmd/aux/portmap.c - 664 sys sys 1045589269 3617
+1045589416 3 a sys/src/cmd/nfs.c - 664 sys sys 1045589249 30446
+1045589416 4 a sys/src/libsunrpc - 20000000775 sys sys 1045589225 0
+1045589416 5 a sys/src/libsunrpc/COPYING - 664 sys sys 1045589219 1269
+1045589416 6 a sys/src/libsunrpc/authunix.c - 664 sys sys 1045589219 1477
+1045589416 7 a sys/src/libsunrpc/client.c - 664 sys sys 1045589220 8843
+1045589416 8 a sys/src/libsunrpc/emalloc.c - 664 sys sys 1045589220 397
+1045589416 9 a sys/src/libsunrpc/error.c - 664 sys sys 1045589221 819
+1045589416 10 a sys/src/libsunrpc/fd.c - 664 sys sys 1045589221 1734
+1045589416 11 a sys/src/libsunrpc/fmt.c - 664 sys sys 1045589222 1146
+1045589416 12 a sys/src/libsunrpc/mkfile - 664 sys sys 1045589222 318
+1045589416 13 a sys/src/libsunrpc/mount3.c - 664 sys sys 1045589223 15217
+1045589416 14 a sys/src/libsunrpc/net.c - 664 sys sys 1045589223 792
+1045589416 15 a sys/src/libsunrpc/nfs3.c - 664 sys sys 1045589224 93376
+1045589416 16 a sys/src/libsunrpc/portmap.c - 664 sys sys 1045589224 9758
+1045589416 17 a sys/src/libsunrpc/prog.c - 664 sys sys 1045589225 1586
+1045589416 18 a sys/src/libsunrpc/rpc.c - 664 sys sys 1045589225 9235
+1045589416 19 a sys/src/libsunrpc/server.c - 664 sys sys 1045589225 5187
+1045589416 20 a sys/src/libsunrpc/udp.c - 664 sys sys 1045589225 2158
+1045589416 21 c sys/src/mkfile - 664 sys sys 1045589241 992
+1045591217 0 a sys/include/nfs3.h - 664 sys sys 1045589438 15082
+1045591217 1 a sys/include/sunrpc.h - 664 sys sys 1045589438 7174
+1045591217 2 a sys/man/4/nfs - 664 sys sys 1045589694 2739
+1045598426 0 c sys/src/cmd/vnc/vncs.c - 664 sys sys 1045597638 20914
+1045600227 0 c sys/src/cmd/fossil/9auth.c - 664 sys sys 1045600016 3149
+1045600227 1 c sys/src/cmd/fossil/9fid.c - 664 sys sys 1045600016 5521
+1045600227 2 c sys/src/cmd/fossil/9p.c - 664 sys sys 1045600099 21080
+1045600227 3 c sys/src/cmd/fossil/9srv.c - 664 sys sys 1045600018 3291
+1045600227 4 c sys/src/cmd/fossil/9user.c - 664 sys sys 1045600019 17457
+1045600227 5 c sys/src/cmd/fossil/Ccmd.c - 664 sys sys 1045600019 7160
+1045600227 6 c sys/src/cmd/fossil/archive.c - 664 sys sys 1045600020 9112
+1045600227 7 a sys/src/cmd/fossil/epoch.c - 664 sys sys 1045600021 997
+1045600227 8 c sys/src/cmd/fossil/fossil.c - 664 sys sys 1045600029 1267
+1045600227 9 c sys/src/cmd/fossil/fs.c - 664 sys sys 1045600030 17059
+1045600227 10 c sys/src/cmd/fossil/history - 664 sys sys 1045600133 1150
+1045607430 0 c 386/bin/page - 775 sys sys 1045606971 215429
+1045607430 1 c sys/src/cmd/page/page.h - 664 sys sys 1045606937 2000

+ 811 - 0
sys/include/nfs3.h

@@ -0,0 +1,811 @@
+/* 
+ * NFS mounter V3;  see RFC 1813
+ */
+
+#pragma lib "libsunrpc.a"
+#pragma src "/sys/src/libsunrpc"
+
+enum {
+	NfsMount1HandleSize = 32,
+	NfsMount3MaxPathSize = 1024,
+	NfsMount3MaxNameSize = 255,
+	NfsMount3MaxHandleSize = 64,
+	NfsMount3Program = 100005,
+	NfsMount3Version = 3,
+	NfsMount1Program = 100005,
+	NfsMount1Version = 1
+};
+typedef struct NfsMount3TNull NfsMount3TNull;
+typedef struct NfsMount3RNull NfsMount3RNull;
+typedef struct NfsMount3TMnt NfsMount3TMnt;
+typedef struct NfsMount3RMnt NfsMount3RMnt;
+typedef struct NfsMount3TDump NfsMount3TDump;
+typedef struct NfsMount3Entry NfsMount3Entry;
+typedef struct NfsMount3RDump NfsMount3RDump;
+typedef struct NfsMount3TUmnt NfsMount3TUmnt;
+typedef struct NfsMount3RUmnt NfsMount3RUmnt;
+typedef struct NfsMount3Export NfsMount3Export;
+typedef struct NfsMount3TUmntall NfsMount3TUmntall;
+typedef struct NfsMount3RUmntall NfsMount3RUmntall;
+typedef struct NfsMount3TExport NfsMount3TExport;
+typedef struct NfsMount3RExport NfsMount3RExport;
+
+typedef enum
+{
+	NfsMount3CallTNull,
+	NfsMount3CallRNull,
+	NfsMount3CallTMnt,
+	NfsMount3CallRMnt,
+	NfsMount3CallTDump,
+	NfsMount3CallRDump,
+	NfsMount3CallTUmnt,
+	NfsMount3CallRUmnt,
+	NfsMount3CallTUmntall,
+	NfsMount3CallRUmntall,
+	NfsMount3CallTExport,
+	NfsMount3CallRExport
+} NfsMount3CallType;
+
+typedef struct NfsMount3Call NfsMount3Call;
+struct NfsMount3Call {
+	SunRpc rpc;
+	NfsMount3CallType type;
+};
+
+struct NfsMount3TNull {
+	NfsMount3Call call;
+};
+
+struct NfsMount3RNull {
+	NfsMount3Call call;
+};
+
+struct NfsMount3TMnt {
+	NfsMount3Call call;
+	char *path;
+};
+
+struct NfsMount3RMnt {
+	NfsMount3Call call;
+	uint status;
+	uchar *handle;
+	uint len;
+	u32int *auth;
+	u32int nauth;
+};
+
+struct NfsMount3TDump {
+	NfsMount3Call call;
+};
+
+struct NfsMount3Entry {
+	char *host;
+	char *path;
+};
+
+struct NfsMount3RDump {
+	NfsMount3Call call;
+	uchar *data;
+	u32int count;
+};
+
+struct NfsMount3TUmnt {
+	NfsMount3Call call;
+	char *path;
+};
+
+struct NfsMount3RUmnt {
+	NfsMount3Call call;
+};
+
+struct NfsMount3Export {
+	char *path;
+	char **g;
+	u32int ng;
+};
+
+struct NfsMount3TUmntall {
+	NfsMount3Call call;
+};
+
+struct NfsMount3RUmntall {
+	NfsMount3Call call;
+};
+
+struct NfsMount3TExport {
+	NfsMount3Call call;
+};
+
+struct NfsMount3RExport {
+	NfsMount3Call call;
+	uchar *data;
+	u32int count;
+};
+
+uint nfsMount3ExportGroupSize(uchar*);
+uint nfsMount3ExportSize(NfsMount3Export*);
+int nfsMount3ExportPack(uchar*, uchar*, uchar**, NfsMount3Export*);
+int nfsMount3ExportUnpack(uchar*, uchar*, uchar**, char**, char***, NfsMount3Export*);
+int nfsMount3EntryPack(uchar*, uchar*, uchar**, NfsMount3Entry*);
+int nfsMount3EntryUnpack(uchar*, uchar*, uchar**, NfsMount3Entry*);
+uint nfsMount3EntrySize(NfsMount3Entry*);
+
+extern SunProg nfsMount3Prog;
+
+/*
+ * NFS V3; see RFC 1813
+ */
+enum {
+	Nfs3MaxHandleSize = 64,
+	Nfs3CookieVerfSize = 8,
+	Nfs3CreateVerfSize = 8,
+	Nfs3WriteVerfSize = 8,
+	Nfs3AccessRead = 1,
+	Nfs3AccessLookup = 2,
+	Nfs3AccessModify = 4,
+	Nfs3AccessExtend = 8,
+	Nfs3AccessDelete = 16,
+	Nfs3AccessExecute = 32,
+	Nfs3FsHasLinks = 1,
+	Nfs3FsHasSymlinks = 2,
+	Nfs3FsHomogeneous = 8,
+	Nfs3FsCanSetTime = 16,
+
+	Nfs3Version = 3,	
+	Nfs3Program = 100003,
+};
+typedef enum
+{
+	Nfs3Ok = 0,
+	Nfs3ErrNotOwner = 1,
+	Nfs3ErrNoEnt = 2,
+	Nfs3ErrIo = 5,
+	Nfs3ErrNxio = 6,
+	Nfs3ErrNoMem = 12,
+	Nfs3ErrAcces = 13,
+	Nfs3ErrExist = 17,
+	Nfs3ErrXDev = 18,
+	Nfs3ErrNoDev = 19,
+	Nfs3ErrNotDir = 20,
+	Nfs3ErrIsDir = 21,
+	Nfs3ErrInval = 22,
+	Nfs3ErrFbig = 27,
+	Nfs3ErrNoSpc = 28,
+	Nfs3ErrRoFs = 30,
+	Nfs3ErrMLink = 31,
+	Nfs3ErrNameTooLong = 63,
+	Nfs3ErrNotEmpty = 66,
+	Nfs3ErrDQuot = 69,
+	Nfs3ErrStale = 70,
+	Nfs3ErrRemote = 71,
+	Nfs3ErrBadHandle = 10001,
+	Nfs3ErrNotSync = 10002,
+	Nfs3ErrBadCookie = 10003,
+	Nfs3ErrNotSupp = 10004,
+	Nfs3ErrTooSmall = 10005,
+	Nfs3ErrServerFault = 10006,
+	Nfs3ErrBadType = 10007,
+	Nfs3ErrJukebox = 10008,
+	Nfs3ErrFprintNotFound = 10009,
+	Nfs3ErrAborted = 10010,
+} Nfs3Status;
+
+void nfs3Errstr(Nfs3Status);
+
+typedef enum
+{
+	Nfs3FileReg = 1,
+	Nfs3FileDir = 2,
+	Nfs3FileBlock = 3,
+	Nfs3FileChar = 4,
+	Nfs3FileSymlink = 5,
+	Nfs3FileSocket = 6,
+	Nfs3FileFifo = 7,
+} Nfs3FileType;
+
+enum
+{
+	Nfs3ModeSetUid = 0x800,
+	Nfs3ModeSetGid = 0x400,
+	Nfs3ModeSticky = 0x200,
+};
+
+typedef enum
+{
+	Nfs3CallTNull,
+	Nfs3CallRNull,
+	Nfs3CallTGetattr,
+	Nfs3CallRGetattr,
+	Nfs3CallTSetattr,
+	Nfs3CallRSetattr,
+	Nfs3CallTLookup,
+	Nfs3CallRLookup,
+	Nfs3CallTAccess,
+	Nfs3CallRAccess,
+	Nfs3CallTReadlink,
+	Nfs3CallRReadlink,
+	Nfs3CallTRead,
+	Nfs3CallRRead,
+	Nfs3CallTWrite,
+	Nfs3CallRWrite,
+	Nfs3CallTCreate,
+	Nfs3CallRCreate,
+	Nfs3CallTMkdir,
+	Nfs3CallRMkdir,
+	Nfs3CallTSymlink,
+	Nfs3CallRSymlink,
+	Nfs3CallTMknod,
+	Nfs3CallRMknod,
+	Nfs3CallTRemove,
+	Nfs3CallRRemove,
+	Nfs3CallTRmdir,
+	Nfs3CallRRmdir,
+	Nfs3CallTRename,
+	Nfs3CallRRename,
+	Nfs3CallTLink,
+	Nfs3CallRLink,
+	Nfs3CallTReadDir,
+	Nfs3CallRReadDir,
+	Nfs3CallTReadDirPlus,
+	Nfs3CallRReadDirPlus,
+	Nfs3CallTFsStat,
+	Nfs3CallRFsStat,
+	Nfs3CallTFsInfo,
+	Nfs3CallRFsInfo,
+	Nfs3CallTPathconf,
+	Nfs3CallRPathconf,
+	Nfs3CallTCommit,
+	Nfs3CallRCommit,
+} Nfs3CallType;
+
+typedef struct Nfs3Call Nfs3Call;
+typedef struct Nfs3Handle Nfs3Handle;
+typedef struct Nfs3Time Nfs3Time;
+typedef struct Nfs3Attr Nfs3Attr;
+typedef struct Nfs3WccAttr Nfs3WccAttr;
+typedef struct Nfs3Wcc Nfs3Wcc;
+typedef enum
+{
+	Nfs3SetTimeDont = 0,
+	Nfs3SetTimeServer = 1,
+	Nfs3SetTimeClient = 2,
+} Nfs3SetTime;
+
+typedef struct Nfs3SetAttr Nfs3SetAttr;
+typedef struct Nfs3TNull Nfs3TNull;
+typedef struct Nfs3RNull Nfs3RNull;
+typedef struct Nfs3TGetattr Nfs3TGetattr;
+typedef struct Nfs3RGetattr Nfs3RGetattr;
+typedef struct Nfs3TSetattr Nfs3TSetattr;
+typedef struct Nfs3RSetattr Nfs3RSetattr;
+typedef struct Nfs3TLookup Nfs3TLookup;
+typedef struct Nfs3RLookup Nfs3RLookup;
+typedef struct Nfs3TAccess Nfs3TAccess;
+typedef struct Nfs3RAccess Nfs3RAccess;
+typedef struct Nfs3TReadlink Nfs3TReadlink;
+typedef struct Nfs3RReadlink Nfs3RReadlink;
+typedef struct Nfs3TRead Nfs3TRead;
+typedef struct Nfs3RRead Nfs3RRead;
+typedef enum
+{
+	Nfs3SyncNone = 0,
+	Nfs3SyncData = 1,
+	Nfs3SyncFile = 2,
+} Nfs3Sync;
+
+typedef struct Nfs3TWrite Nfs3TWrite;
+typedef struct Nfs3RWrite Nfs3RWrite;
+typedef enum
+{
+	Nfs3CreateUnchecked = 0,
+	Nfs3CreateGuarded = 1,
+	Nfs3CreateExclusive = 2,
+} Nfs3Create;
+
+typedef struct Nfs3TCreate Nfs3TCreate;
+typedef struct Nfs3RCreate Nfs3RCreate;
+typedef struct Nfs3TMkdir Nfs3TMkdir;
+typedef struct Nfs3RMkdir Nfs3RMkdir;
+typedef struct Nfs3TSymlink Nfs3TSymlink;
+typedef struct Nfs3RSymlink Nfs3RSymlink;
+typedef struct Nfs3TMknod Nfs3TMknod;
+typedef struct Nfs3RMknod Nfs3RMknod;
+typedef struct Nfs3TRemove Nfs3TRemove;
+typedef struct Nfs3RRemove Nfs3RRemove;
+typedef struct Nfs3TRmdir Nfs3TRmdir;
+typedef struct Nfs3RRmdir Nfs3RRmdir;
+typedef struct Nfs3TRename Nfs3TRename;
+typedef struct Nfs3RRename Nfs3RRename;
+typedef struct Nfs3TLink Nfs3TLink;
+typedef struct Nfs3RLink Nfs3RLink;
+typedef struct Nfs3TReadDir Nfs3TReadDir;
+typedef struct Nfs3Entry Nfs3Entry;
+typedef struct Nfs3RReadDir Nfs3RReadDir;
+typedef struct Nfs3TReadDirPlus Nfs3TReadDirPlus;
+typedef struct Nfs3EntryPlus Nfs3EntryPlus;
+typedef struct Nfs3RReadDirPlus Nfs3RReadDirPlus;
+typedef struct Nfs3TFsStat Nfs3TFsStat;
+typedef struct Nfs3RFsStat Nfs3RFsStat;
+typedef struct Nfs3TFsInfo Nfs3TFsInfo;
+typedef struct Nfs3RFsInfo Nfs3RFsInfo;
+typedef struct Nfs3TPathconf Nfs3TPathconf;
+typedef struct Nfs3RPathconf Nfs3RPathconf;
+typedef struct Nfs3TCommit Nfs3TCommit;
+typedef struct Nfs3RCommit Nfs3RCommit;
+
+struct Nfs3Call {
+	SunRpc rpc;
+	Nfs3CallType type;
+};
+
+struct Nfs3Handle {
+	uchar h[Nfs3MaxHandleSize];
+	u32int len;
+};
+
+struct Nfs3Time {
+	u32int sec;
+	u32int nsec;
+};
+
+struct Nfs3Attr {
+	Nfs3FileType type;
+	u32int mode;
+	u32int nlink;
+	u32int uid;
+	u32int gid;
+	u64int size;
+	u64int used;
+	u32int major;
+	u32int minor;
+	u64int fsid;
+	u64int fileid;
+	Nfs3Time atime;
+	Nfs3Time mtime;
+	Nfs3Time ctime;
+};
+
+struct Nfs3WccAttr {
+	u64int size;
+	Nfs3Time mtime;
+	Nfs3Time ctime;
+};
+
+struct Nfs3Wcc {
+	u1int haveWccAttr;
+	Nfs3WccAttr wccAttr;
+	u1int haveAttr;
+	Nfs3Attr attr;
+};
+
+struct Nfs3SetAttr {
+	u1int setMode;
+	u32int mode;
+	u1int setUid;
+	u32int uid;
+	u1int setGid;
+	u32int gid;
+	u1int setSize;
+	u64int size;
+	Nfs3SetTime setAtime;
+	Nfs3Time atime;
+	Nfs3SetTime setMtime;
+	Nfs3Time mtime;
+};
+
+struct Nfs3TNull {
+	Nfs3Call call;
+};
+
+struct Nfs3RNull {
+	Nfs3Call call;
+};
+
+struct Nfs3TGetattr {
+	Nfs3Call call;
+	Nfs3Handle handle;
+};
+
+struct Nfs3RGetattr {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Attr attr;
+};
+
+struct Nfs3TSetattr {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	Nfs3SetAttr attr;
+	u1int checkCtime;
+	Nfs3Time ctime;
+};
+
+struct Nfs3RSetattr {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Wcc wcc;
+};
+
+struct Nfs3TLookup {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	char *name;
+};
+
+struct Nfs3RLookup {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Handle handle;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	u1int haveDirAttr;
+	Nfs3Attr dirAttr;
+};
+
+struct Nfs3TAccess {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	u32int access;
+};
+
+struct Nfs3RAccess {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	u32int access;
+};
+
+struct Nfs3TReadlink {
+	Nfs3Call call;
+	Nfs3Handle handle;
+};
+
+struct Nfs3RReadlink {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	char *data;
+};
+
+struct Nfs3TRead {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	u64int offset;
+	u32int count;
+};
+
+struct Nfs3RRead {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	u32int count;
+	u1int eof;
+	uchar *data;
+	u32int ndata;
+};
+
+struct Nfs3TWrite {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	u64int offset;
+	u32int count;
+	Nfs3Sync stable;
+	uchar *data;
+	u32int ndata;
+};
+
+struct Nfs3RWrite {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Wcc wcc;
+	u32int count;
+	Nfs3Sync committed;
+	uchar verf[Nfs3WriteVerfSize];
+};
+
+struct Nfs3TCreate {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	char *name;
+	Nfs3Create mode;
+	Nfs3SetAttr attr;
+	uchar verf[Nfs3CreateVerfSize];
+};
+
+struct Nfs3RCreate {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveHandle;
+	Nfs3Handle handle;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	Nfs3Wcc dirWcc;
+};
+
+struct Nfs3TMkdir {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	char *name;
+	Nfs3SetAttr attr;
+};
+
+struct Nfs3RMkdir {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveHandle;
+	Nfs3Handle handle;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	Nfs3Wcc dirWcc;
+};
+
+struct Nfs3TSymlink {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	char *name;
+	Nfs3SetAttr attr;
+	char *data;
+};
+
+struct Nfs3RSymlink {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveHandle;
+	Nfs3Handle handle;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	Nfs3Wcc dirWcc;
+};
+
+struct Nfs3TMknod {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	char *name;
+	Nfs3FileType type;
+	Nfs3SetAttr attr;
+	u32int major;
+	u32int minor;
+};
+
+struct Nfs3RMknod {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveHandle;
+	Nfs3Handle handle;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	Nfs3Wcc dirWcc;
+};
+
+struct Nfs3TRemove {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	char *name;
+};
+
+struct Nfs3RRemove {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Wcc wcc;
+};
+
+struct Nfs3TRmdir {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	char *name;
+};
+
+struct Nfs3RRmdir {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Wcc wcc;
+};
+
+struct Nfs3TRename {
+	Nfs3Call call;
+	struct {
+		Nfs3Handle handle;
+		char *name;
+	} from;
+	struct {
+		Nfs3Handle handle;
+		char *name;
+	} to;
+};
+
+struct Nfs3RRename {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Wcc fromWcc;
+	Nfs3Wcc toWcc;
+};
+
+struct Nfs3TLink {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	struct {
+		Nfs3Handle handle;
+		char *name;
+	} link;
+};
+
+struct Nfs3RLink {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	Nfs3Wcc dirWcc;
+};
+
+struct Nfs3TReadDir {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	u64int cookie;
+	uchar verf[Nfs3CookieVerfSize];
+	u32int count;
+};
+
+struct Nfs3RReadDir {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	uchar verf[Nfs3CookieVerfSize];
+	uchar *data;
+	u32int count;
+	u1int eof;
+};
+
+struct Nfs3TReadDirPlus {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	u64int cookie;
+	uchar verf[Nfs3CookieVerfSize];
+	u32int dirCount;
+	u32int maxCount;
+};
+
+struct Nfs3Entry {
+	u64int fileid;
+	char *name;
+	u64int cookie;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	u1int haveHandle;
+	Nfs3Handle handle;
+};
+
+struct Nfs3RReadDirPlus {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	uchar verf[Nfs3CookieVerfSize];
+	uchar *data;
+	u32int count;
+	u1int eof;
+};
+
+struct Nfs3TFsStat {
+	Nfs3Call call;
+	Nfs3Handle handle;
+};
+
+struct Nfs3RFsStat {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	u64int totalBytes;
+	u64int freeBytes;
+	u64int availBytes;
+	u64int totalFiles;
+	u64int freeFiles;
+	u64int availFiles;
+	u32int invarSec;
+};
+
+struct Nfs3TFsInfo {
+	Nfs3Call call;
+	Nfs3Handle handle;
+};
+
+struct Nfs3RFsInfo {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	u32int readMax;
+	u32int readPref;
+	u32int readMult;
+	u32int writeMax;
+	u32int writePref;
+	u32int writeMult;
+	u32int readDirPref;
+	u64int maxFileSize;
+	Nfs3Time timePrec;
+	u32int flags;
+};
+
+struct Nfs3TPathconf {
+	Nfs3Call call;
+	Nfs3Handle handle;
+};
+
+struct Nfs3RPathconf {
+	Nfs3Call call;
+	Nfs3Status status;
+	u1int haveAttr;
+	Nfs3Attr attr;
+	u32int maxLink;
+	u32int maxName;
+	u1int noTrunc;
+	u1int chownRestricted;
+	u1int caseInsensitive;
+	u1int casePreserving;
+};
+
+struct Nfs3TCommit {
+	Nfs3Call call;
+	Nfs3Handle handle;
+	u64int offset;
+	u32int count;
+};
+
+struct Nfs3RCommit {
+	Nfs3Call call;
+	Nfs3Status status;
+	Nfs3Wcc wcc;
+	uchar verf[Nfs3WriteVerfSize];
+};
+
+char *nfs3StatusStr(Nfs3Status);
+char *nfs3TypeStr(Nfs3CallType);
+char *nfs3SetTimeStr(Nfs3SetTime);
+char *nfs3SyncStr(Nfs3Sync);
+
+void nfs3HandlePrint(Fmt*, Nfs3Handle*);
+u32int nfs3HandleSize(Nfs3Handle*);
+int nfs3HandlePack(uchar*, uchar*, uchar**, Nfs3Handle*);
+int nfs3HandleUnpack(uchar*, uchar*, uchar**, Nfs3Handle*);
+
+void nfs3TimePrint(Fmt*, Nfs3Time*);
+u32int nfs3TimeSize(Nfs3Time*);
+int nfs3TimePack(uchar*, uchar*, uchar**, Nfs3Time*);
+int nfs3TimeUnpack(uchar*, uchar*, uchar**, Nfs3Time*);
+
+void nfs3AttrPrint(Fmt*, Nfs3Attr*);
+u32int nfs3AttrSize(Nfs3Attr*);
+int nfs3AttrPack(uchar*, uchar*, uchar**, Nfs3Attr*);
+int nfs3AttrUnpack(uchar*, uchar*, uchar**, Nfs3Attr*);
+
+void nfs3WccAttrPrint(Fmt*, Nfs3WccAttr*);
+u32int nfs3WccAttrSize(Nfs3WccAttr*);
+int nfs3WccAttrPack(uchar*, uchar*, uchar**, Nfs3WccAttr*);
+int nfs3WccAttrUnpack(uchar*, uchar*, uchar**, Nfs3WccAttr*);
+
+void nfs3WccPrint(Fmt*, Nfs3Wcc*);
+u32int nfs3WccSize(Nfs3Wcc*);
+int nfs3WccPack(uchar*, uchar*, uchar**, Nfs3Wcc*);
+int nfs3WccUnpack(uchar*, uchar*, uchar**, Nfs3Wcc*);
+
+void nfs3SetAttrPrint(Fmt*, Nfs3SetAttr*);
+u32int nfs3SetAttrSize(Nfs3SetAttr*);
+int nfs3SetAttrPack(uchar*, uchar*, uchar**, Nfs3SetAttr*);
+int nfs3SetAttrUnpack(uchar*, uchar*, uchar**, Nfs3SetAttr*);
+
+extern SunProg nfs3Prog;
+
+void nfs3EntryPrint(Fmt*, Nfs3Entry*);
+u32int nfs3EntrySize(Nfs3Entry*);
+int nfs3EntryPack(uchar*, uchar*, uchar**, Nfs3Entry*);
+int nfs3EntryUnpack(uchar*, uchar*, uchar**, Nfs3Entry*);
+
+void nfs3EntryPlusPrint(Fmt*, Nfs3Entry*);
+u32int nfs3EntryPlusSize(Nfs3Entry*);
+int nfs3EntryPlusPack(uchar*, uchar*, uchar**, Nfs3Entry*);
+int nfs3EntryPlusUnpack(uchar*, uchar*, uchar**, Nfs3Entry*);
+

+ 395 - 0
sys/include/sunrpc.h

@@ -0,0 +1,395 @@
+/*
+ * Sun RPC; see RFC 1057
+ */
+
+#pragma lib "libsunrpc.a"
+#pragma src "/sys/src/libsunrpc"
+
+typedef uchar u1int;
+
+typedef struct SunAuthInfo SunAuthInfo;
+typedef struct SunAuthUnix SunAuthUnix;
+typedef struct SunRpc SunRpc;
+typedef struct SunCall SunCall;
+
+enum
+{
+	/* Authinfo.flavor */
+	SunAuthNone = 0,
+	SunAuthSys,
+	SunAuthShort,
+	SunAuthDes,
+};
+
+typedef enum {
+	SunAcceptError = 0x10000,
+	SunRejectError = 0x20000,
+	SunAuthError = 0x40000,
+
+	/* Reply.status */
+	SunSuccess = 0,
+
+	SunProgUnavail = SunAcceptError | 1,
+	SunProgMismatch,
+	SunProcUnavail,
+	SunGarbageArgs,
+	SunSystemErr,
+
+	SunRpcMismatch = SunRejectError | 0,
+
+	SunAuthBadCred = SunAuthError | 1,
+	SunAuthRejectedCred,
+	SunAuthBadVerf,
+	SunAuthRejectedVerf,
+	SunAuthTooWeak,
+	SunAuthInvalidResp,
+	SunAuthFailed,
+} SunStatus;
+
+struct SunAuthInfo
+{
+	uint flavor;
+	uchar *data;
+	uint ndata;
+};
+
+struct SunAuthUnix
+{
+	u32int stamp;
+	char *sysname;
+	u32int uid;
+	u32int gid;
+	u32int g[16];
+	u32int ng;
+};
+
+struct SunRpc
+{
+	u32int xid;
+	uint iscall;
+
+	/*
+	 * only sent on wire in call
+	 * caller fills in for the reply unpackers.
+	 */
+	u32int proc;
+
+	/* call */
+	// uint proc;
+	u32int prog, vers;
+	SunAuthInfo cred;
+	SunAuthInfo verf;
+	uchar *data;
+	uint ndata;
+
+	/* reply */
+	u32int status;
+	// SunAuthInfo verf;
+	u32int low, high;
+	// uchar *data;
+	// uint ndata;
+};
+
+typedef enum
+{
+	SunCallTypeTNull,
+	SunCallTypeRNull,
+} SunCallType;
+
+struct SunCall
+{
+	SunRpc rpc;
+	SunCallType type;
+};
+
+void sunErrstr(SunStatus);
+
+void sunRpcPrint(Fmt*, SunRpc*);
+uint sunRpcSize(SunRpc*);
+SunStatus sunRpcPack(uchar*, uchar*, uchar**, SunRpc*);
+SunStatus sunRpcUnpack(uchar*, uchar*, uchar**, SunRpc*);
+
+void sunAuthInfoPrint(Fmt*, SunAuthInfo*);
+uint sunAuthInfoSize(SunAuthInfo*);
+int sunAuthInfoPack(uchar*, uchar*, uchar**, SunAuthInfo*);
+int sunAuthInfoUnpack(uchar*, uchar*, uchar**, SunAuthInfo*);
+
+void sunAuthUnixPrint(Fmt*, SunAuthUnix*);
+uint sunAuthUnixSize(SunAuthUnix*);
+int sunAuthUnixPack(uchar*, uchar*, uchar**, SunAuthUnix*);
+int sunAuthUnixUnpack(uchar*, uchar*, uchar**, SunAuthUnix*);
+
+int sunEnumPack(uchar*, uchar*, uchar**, int*);
+int sunEnumUnpack(uchar*, uchar*, uchar**, int*);
+int sunUint1Pack(uchar*, uchar*, uchar**, u1int*);
+int sunUint1Unpack(uchar*, uchar*, uchar**, u1int*);
+
+int sunStringPack(uchar*, uchar*, uchar**, char**, u32int);
+int sunStringUnpack(uchar*, uchar*, uchar**, char**, u32int);
+uint sunStringSize(char*);
+
+int sunUint32Pack(uchar*, uchar*, uchar**, u32int*);
+int sunUint32Unpack(uchar*, uchar*, uchar**, u32int*);
+int sunUint64Pack(uchar*, uchar*, uchar**, u64int*);
+int sunUint64Unpack(uchar*, uchar*, uchar**, u64int*);
+
+int sunVarOpaquePack(uchar*, uchar*, uchar**, uchar**, u32int*, u32int);
+int sunVarOpaqueUnpack(uchar*, uchar*, uchar**, uchar**, u32int*, u32int);
+uint sunVarOpaqueSize(u32int);
+
+int sunFixedOpaquePack(uchar*, uchar*, uchar**, uchar*, u32int);
+int sunFixedOpaqueUnpack(uchar*, uchar*, uchar**, uchar*, u32int);
+uint sunFixedOpaqueSize(u32int);
+
+/*
+ * Sun RPC Program
+ */
+typedef struct SunProc SunProc;
+typedef struct SunProg SunProg;
+struct SunProg
+{
+	uint prog;
+	uint vers;
+	SunProc *proc;
+	int nproc;
+};
+
+struct SunProc
+{
+	int (*pack)(uchar*, uchar*, uchar**, SunCall*);
+	int (*unpack)(uchar*, uchar*, uchar**, SunCall*);
+	uint (*size)(SunCall*);
+	void (*fmt)(Fmt*, SunCall*);
+	uint sizeoftype;
+};
+
+SunStatus sunCallPack(SunProg*, uchar*, uchar*, uchar**, SunCall*);
+SunStatus sunCallUnpack(SunProg*, uchar*, uchar*, uchar**, SunCall*);
+SunStatus sunCallUnpackAlloc(SunProg*, SunCallType, uchar*, uchar*, uchar**, SunCall**);
+uint sunCallSize(SunProg*, SunCall*);
+
+/*
+ * Formatting
+ */
+#pragma varargck type "B" SunRpc*
+#pragma varargck type "C" SunCall*
+
+int	sunRpcFmt(Fmt*);
+int	sunCallFmt(Fmt*);
+void	sunFmtInstall(SunProg*);
+
+
+/*
+ * Sun RPC Server
+ */
+typedef struct SunMsg SunMsg;
+typedef struct SunSrv SunSrv;
+
+enum
+{
+	SunStackSize = 8192,
+};
+
+struct SunMsg
+{
+	uchar *data;
+	int count;
+	SunSrv *srv;
+	SunRpc rpc;
+	SunProg *pg;
+	SunCall *call;
+	Channel *creply;	/* chan(SunMsg*) */
+};
+
+struct SunSrv
+{
+	int chatty;
+	int cacheReplies;
+	int alwaysReject;
+	SunProg **map;
+	Channel *crequest;
+
+/* implementation use only */
+	Channel **cdispatch;
+	SunProg **prog;
+	int nprog;
+	void *cache;
+	Channel *creply;
+	Channel *cthread;
+};
+
+SunSrv *sunSrv(void);
+
+void	sunSrvProg(SunSrv *srv, SunProg *prog, Channel *c);
+int	sunSrvAnnounce(SunSrv *srv, char *address);
+int	sunSrvUdp(SunSrv *srv, char *address);
+int	sunSrvNet(SunSrv *srv, char *address);
+int	sunSrvFd(SunSrv *srv, int fd);
+void	sunSrvThreadCreate(SunSrv *srv, void (*fn)(void*), void*);
+void	sunSrvClose(SunSrv*);
+
+int	sunMsgReply(SunMsg*, SunCall*);
+int	sunMsgDrop(SunMsg*);
+int	sunMsgReplyError(SunMsg*, SunStatus);
+
+/*
+ * Sun RPC Client
+ */
+typedef struct SunClient SunClient;
+
+struct SunClient
+{
+	int		fd;
+	int		chatty;
+	int		needcount;
+	ulong	maxwait;
+	ulong	xidgen;
+	int		nsend;
+	int		nresend;
+	struct {
+		ulong min;
+		ulong max;
+		ulong avg;
+	} rtt;
+	Channel	*dying;
+	Channel	*rpcchan;
+	Channel	*timerchan;
+	Channel	*flushchan;
+	Channel	*readchan;
+	SunProg	**prog;
+	int		nprog;
+	int 		timertid;
+	int 		nettid;
+};
+
+SunClient	*sunDial(char*);
+
+int	sunClientRpc(SunClient*, ulong, SunCall*, SunCall*, uchar**);
+void	sunClientClose(SunClient*);
+void	sunClientFlushRpc(SunClient*, ulong);
+void	sunClientProg(SunClient*, SunProg*);
+
+
+/*
+ * Provided by callers.
+ * Should remove dependence on this, but hard.
+ */
+void	*emalloc(ulong);
+void *erealloc(void*, ulong);
+
+
+/*
+ * Sun RPC port mapper; see RFC 1057 Appendix A
+ */
+
+typedef struct PortMap PortMap;
+typedef struct PortTNull PortTNull;
+typedef struct PortRNull PortRNull;
+typedef struct PortTSet PortTSet;
+typedef struct PortRSet PortRSet;
+typedef struct PortTUnset PortTUnset;
+typedef struct PortRUnset PortRUnset;
+typedef struct PortTGetport PortTGetport;
+typedef struct PortRGetport PortRGetport;
+typedef struct PortTDump PortTDump;
+typedef struct PortRDump PortRDump;
+typedef struct PortTCallit PortTCallit;
+typedef struct PortRCallit PortRCallit;
+
+typedef enum
+{
+	PortCallTNull,
+	PortCallRNull,
+	PortCallTSet,
+	PortCallRSet,
+	PortCallTUnset,
+	PortCallRUnset,
+	PortCallTGetport,
+	PortCallRGetport,
+	PortCallTDump,
+	PortCallRDump,
+	PortCallTCallit,
+	PortCallRCallit,
+} PortCallType;
+
+enum
+{
+	PortProgram	= 100000,
+	PortVersion	= 2,
+
+	PortProtoTcp	= 6,	/* protocol number for TCP/IP */
+	PortProtoUdp	= 17	/* protocol number for UDP/IP */
+};
+
+struct PortMap {
+	u32int prog;
+	u32int vers;
+	u32int prot;
+	u32int port;
+};
+
+struct PortTNull {
+	SunCall call;
+};
+
+struct PortRNull {
+	SunCall call;
+};
+
+struct PortTSet {
+	SunCall call;
+	PortMap map;
+};
+
+struct PortRSet {
+	SunCall call;
+	u1int b;
+};
+
+struct PortTUnset {
+	SunCall call;
+	PortMap map;
+};
+
+struct PortRUnset {
+	SunCall call;
+	u1int b;
+};
+
+struct PortTGetport {
+	SunCall call;
+	PortMap map;
+};
+
+struct PortRGetport {
+	SunCall call;
+	u32int port;
+};
+
+struct PortTDump {
+	SunCall call;
+};
+
+struct PortRDump {
+	SunCall call;
+	PortMap *map;
+	int nmap;
+};
+
+struct PortTCallit {
+	SunCall call;
+	u32int prog;
+	u32int vers;
+	u32int proc;
+	uchar *data;
+	u32int count;
+};
+
+struct PortRCallit {
+	SunCall call;
+	u32int port;
+	uchar *data;
+	u32int count;
+};
+
+extern SunProg portProg;

+ 146 - 0
sys/man/4/nfs

@@ -0,0 +1,146 @@
+.TH NFS 4
+.SH NAME
+nfs \- Sun network file system client
+.SH SYNOPSIS
+.B nfs
+[
+.B -DRv
+]
+[
+.B -p
+.I perm
+]
+[
+.B -s
+.I srvname
+]
+[
+.B -u
+.I passwd
+.I group
+]
+.I addr1
+[
+.I addr2
+]
+.PP
+.B aux/portmap
+[
+.B -R
+]
+.I host
+.I cmd
+...
+.PP
+.B aux/nfsmonut
+[
+.B -R
+]
+.I host
+.I cmd
+...
+.SH DESCRIPTION
+.I Nfs
+translates between the Sun network file system protocol (NFS)
+and 9P, allowing 9P clients to mount file systems on NFS servers.
+NFS servers comprise two separate services: a mount service used to
+obtain the initial file handle, and a file service used to perform
+actual file system operations.
+The Sun port mapper service is typically used to find these two services.
+If one address is given, it is taken to be the address of a port mapper service;
+.I nfs
+queries the port mapper to find the addresses
+of the NFS mount service and file service.
+If two addresses are given, the port mapper is bypasswd;
+.I addr1
+is used as the address of the NFS mount service,
+and
+.I addr2
+is used as the address of the file service.
+.PP
+The options are:
+.TP
+.B -D
+print all 9P messages.
+.TP
+.B -R
+print all NFS messages.
+.TP
+.B -v
+print verbose information about session startup.
+.TP
+.B -p \fIperm
+set the posted service file to have mode
+.IR perm ,
+which is assumed to be octal;
+the default is
+.BR 600 .
+.TP
+.B -s \fIsrvname
+post the service as
+.BI /srv/ srvname \fR;
+the default is
+.BI /srv/ addr1 \fR.
+.TP
+.B -u \fIpasswd\fR \fIgroup
+translate user and group names using the 
+.I passwd
+and
+.I group
+files, which are in the traditional Unix format.
+The translation is used to present names for
+user and group in
+.I stat (5)
+and
+.I wstat
+messages.
+The translation is also used to
+choose the user and group credentials
+to present for a user.
+Without this option, users and groups are presented
+as decimal numbers, and everyone attaches as uid \-1
+.RB ( nobody
+on most Unix systems).
+.PD
+.I Portmap
+and
+.I nfsmount
+are test programs to perform port mapper and NFS mount RPCs.
+Run
+.B aux/portmap
+.B -?
+and
+.B aux/nfsmount
+.B -?
+for lists of commands.
+.SH EXAMPLE
+We use this in our
+.B /rc/bin/9fs
+script to mount all the home directories served by
+.IR bopp :
+.IP
+.EX
+case bopp
+	if(! test -f /srv/bopp)
+		nfs -p 666 -u /lib/ndb/1127.passwd /lib/ndb/1127.group bopp
+	unmount /n/bopp >[2]/dev/null
+	for(i in u0 u1 u2 u3 u4 u5 u6 u7 u8 u9)
+		mount -a /srv/bopp /n/bopp /$i
+.EE
+.SH SOURCE
+.B /sys/src/cmd/nfs.c
+.br
+.B /sys/src/libsunrpc
+.SH "SEE ALSO
+.IR nfsserver (8),
+.IR srv (4)
+.SH BUGS
+The authentication employed by NFS is laughable.
+The server simply trusts the uid, gid, and group list
+presented by the client.
+.PP
+.I Nfs
+speaks only NFS version 3.
+Older operating systems typically
+have reasonable NFS version 2 servers
+but crash when serving version 3.

+ 29 - 7
sys/man/8/fs

@@ -124,6 +124,14 @@ fs, exsort \- file server maintenance
 [
 .I tunit
 ]
+.PP
+.B wormoffline
+.I drive
+.PP
+.B wormonline
+.I drive
+.PP
+.B wormreset
 .PD
 .PP
 .B disk/exsort
@@ -742,13 +750,23 @@ are given the output selects connections owned by those users.
 .I Wormeject
 moves the WORM disk in slot
 .I tunit
-to the output shelf.
+of the first jukebox to the output shelf.
 .PP
 .I Wormingest
-moves the WORM disk from the input shelf to
-slot
+moves the WORM disk from the input shelf of the first jukebox to slot
 .IR tunit .
 .PP
+.I Wormoffline
+takes
+.I drive
+of the first jukebox out of service;
+.I wormonline
+puts it back in service.
+.PP
+.I Wormreset
+put discs back where the jukebox thinks they belong,
+and does this for all jukeboxes.
+.PP
 When the file server boots, it prints the message
 .IP
 .EX
@@ -778,12 +796,16 @@ With option
 .B -w
 it writes the sorted data back to
 .IR file .
-.SH SOURCE
-.B /sys/src/fs
-.br
-.B /sys/src/cmd/disk/exsort.c
 .SH SEE ALSO
 .IR fs (4)
 .br
 Ken Thompson,
 ``The Plan 9 File Server''.
+.SH SOURCE
+.B /sys/src/fs
+.br
+.B /sys/src/cmd/disk/exsort.c
+.SH BUGS
+The
+.B worm*
+commands should accept an argument identifying a jukebox.

+ 1 - 1
sys/src/cmd/acme/util.c

@@ -201,7 +201,7 @@ runetobyte(Rune *r, int n)
 {
 	char *s;
 
-	if(n == 0)
+	if(r == nil)
 		return nil;
 	s = emalloc(n*UTFmax+1);
 	setmalloctag(s, getcallerpc(&r));

+ 2 - 1
sys/src/cmd/aux/mkfile

@@ -19,8 +19,10 @@ TARG=\
 	ms2\
 	mswordstrings\
 	mouse\
+	nfsmount\
 	olefs\
 	pcmcia\
+	portmap\
 	rdwr\
 	reboot\
 	searchfs\
@@ -38,7 +40,6 @@ UPDATE=\
 	$HFILES\
 	${OFILES:%.$O=%.c}\
 	${TARG:%=%.c}\
-	${TARG:%=/386/bin/aux/%}\
 
 </sys/src/cmd/mkmany
 

+ 334 - 0
sys/src/cmd/aux/nfsmount.c

@@ -0,0 +1,334 @@
+/* Copyright © 2003 Russ Cox, MIT; see /sys/src/libsunrpc/COPYING */
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <sunrpc.h>
+#include <nfs3.h>
+
+int chatty;
+SunClient *client;
+
+void
+usage(void)
+{
+	fprint(2, "usage: nfsmount address [cmd]\n"
+		"cmd is one of:\n"
+		"\tnull\n"
+		"\tmnt path\n"
+		"\tdump\n"
+		"\tumnt path\n"
+		"\tumntall\n"
+		"\texport (default)\n");
+	threadexitsall("usage");
+}
+
+void
+portCall(SunCall *c, PortCallType type)
+{
+	c->rpc.prog = PortProgram;
+	c->rpc.vers = PortVersion;
+	c->rpc.proc = type>>1;
+	c->rpc.iscall = !(type&1);
+	c->type = type;
+}
+
+int
+getport(SunClient *client, uint prog, uint vers, uint prot, uint *port)
+{
+	PortTGetport tx;
+	PortRGetport rx;
+
+	memset(&tx, 0, sizeof tx);
+	portCall(&tx.call, PortCallTGetport);
+	tx.map.prog = prog;
+	tx.map.vers = vers;
+	tx.map.prot = prot;
+
+	memset(&rx, 0, sizeof rx);
+	portCall(&rx.call, PortCallRGetport);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		return -1;
+	*port = rx.port;
+	return 0;
+}
+
+uchar unixauth[] = {
+	0x12, 0x23, 0x34, 0x45,	/* stamp */
+	0x00, 0x00, 0x00, 0x04,	/* gnot */
+	0x67, 0x6e, 0x6f, 0x74,
+	0x00, 0x00, 0x03, 0xE9,	/* 1001 */
+	0x00, 0x00, 0x03, 0xE9,	/* 1001 */
+	0x00, 0x00, 0x00, 0x00,	/* gid list */
+};
+void
+mountCall(SunCall *c, NfsMount3CallType type)
+{
+	c->rpc.prog = NfsMount3Program;
+	c->rpc.vers = NfsMount3Version;
+	c->rpc.proc = type>>1;
+	c->rpc.iscall = !(type&1);
+	if(c->rpc.iscall){
+		c->rpc.cred.flavor = SunAuthSys;
+		c->rpc.cred.data = unixauth;
+		c->rpc.cred.ndata = sizeof unixauth;
+	}
+	c->type = type;
+}
+
+void
+tnull(char **argv)
+{
+	NfsMount3TNull tx;
+	NfsMount3RNull rx;
+
+	USED(argv);
+
+	memset(&tx, 0, sizeof tx);
+	mountCall(&tx.call, NfsMount3CallTNull);
+
+	memset(&rx, 0, sizeof rx);
+	mountCall(&rx.call, NfsMount3CallRNull);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+}
+
+void
+tmnt(char **argv)
+{
+	int i;
+	NfsMount3TMnt tx;
+	NfsMount3RMnt rx;
+
+	memset(&tx, 0, sizeof tx);
+	mountCall(&tx.call, NfsMount3CallTMnt);
+	tx.path = argv[0];
+
+	memset(&rx, 0, sizeof rx);
+	mountCall(&rx.call, NfsMount3CallRMnt);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+
+	if(rx.status != 0){
+		nfs3Errstr(rx.status);
+		sysfatal("mnt: %r");
+	}
+
+	print("handle %.*H\n", rx.len, rx.handle);
+	print("auth:");
+	for(i=0; i<rx.nauth; i++)
+		print(" %ud", (uint)rx.auth[i]);
+	print("\n");
+}
+
+void
+tdump(char **argv)
+{
+	uchar *p, *ep;
+	NfsMount3TDump tx;
+	NfsMount3RDump rx;
+	NfsMount3Entry e;
+
+	memset(&tx, 0, sizeof tx);
+	mountCall(&tx.call, NfsMount3CallTDump);
+	USED(argv);
+
+	memset(&rx, 0, sizeof rx);
+	mountCall(&rx.call, NfsMount3CallRDump);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+
+	p = rx.data;
+	ep = p+rx.count;
+	while(p < ep){
+		if(nfsMount3EntryUnpack(p, ep, &p, &e) < 0)
+			sysfatal("unpack entry structure failed");
+		print("%s %s\n", e.host, e.path);
+	}
+}
+
+void
+tumnt(char **argv)
+{
+	NfsMount3TUmnt tx;
+	NfsMount3RUmnt rx;
+
+	memset(&tx, 0, sizeof tx);
+	mountCall(&tx.call, NfsMount3CallTUmnt);
+	tx.path = argv[0];
+
+	memset(&rx, 0, sizeof rx);
+	mountCall(&rx.call, NfsMount3CallRUmnt);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+
+	print("\n");
+}
+
+void
+tumntall(char **argv)
+{
+	NfsMount3TUmntall tx;
+	NfsMount3RUmntall rx;
+
+	memset(&tx, 0, sizeof tx);
+	mountCall(&tx.call, NfsMount3CallTUmntall);
+	USED(argv);
+
+	memset(&rx, 0, sizeof rx);
+	mountCall(&rx.call, NfsMount3CallRUmntall);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+
+	print("\n");
+}
+
+void
+texport(char **argv)
+{
+	uchar *p, *ep, *tofree;
+	char **g, **gg;
+	int ng, i, n;
+	NfsMount3TDump tx;
+	NfsMount3RDump rx;
+	NfsMount3Export e;
+
+	memset(&tx, 0, sizeof tx);
+	mountCall(&tx.call, NfsMount3CallTExport);
+	USED(argv);
+
+	memset(&rx, 0, sizeof rx);
+	mountCall(&rx.call, NfsMount3CallRExport);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, &tofree) < 0)
+		sysfatal("rpc: %r");
+
+	p = rx.data;
+	ep = p+rx.count;
+	g = nil;
+	ng = 0;
+	while(p < ep){
+		n = nfsMount3ExportGroupSize(p);
+		if(n > ng){
+			ng = n;
+			g = erealloc(g, sizeof(g[0])*ng);
+		}
+		if(nfsMount3ExportUnpack(p, ep, &p, g, &gg, &e) < 0)
+			sysfatal("unpack export structure failed");
+		print("%s", e.path);
+		for(i=0; i<e.ng; i++)
+			print(" %s", e.g[i]);
+		print("\n");
+	}
+	free(tofree);
+}
+
+static struct {
+	char *cmd;
+	int narg;
+	void (*fn)(char**);
+} tab[] = {
+	"null",	0,	tnull,
+	"mnt",	1,	tmnt,
+	"dump",	0,	tdump,
+	"umnt",	1,	tumnt,
+	"umntall",	1,	tumntall,
+	"export",	0,	texport,
+};
+
+char*
+netchangeport(char *addr, char *port)
+{
+	static char buf[256];
+	char *r;
+
+	strecpy(buf, buf+sizeof buf, addr);
+	r = strrchr(buf, '!');
+	if(r == nil)
+		return nil;
+	r++;
+	strecpy(r, buf+sizeof buf, port);
+	return buf;
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	char *dflt[] = { "export", };
+	char *addr, *cmd;
+	int i, proto;
+	uint port;
+	char buf[32];
+	int mapit;
+
+	mapit = 1;
+	ARGBEGIN{
+	case 'R':
+		chatty++;
+		break;
+	case 'm':
+		mapit = 0;
+		break;
+	}ARGEND
+
+	if(argc < 1)
+		usage();
+
+	fmtinstall('B', sunRpcFmt);
+	fmtinstall('C', sunCallFmt);
+	fmtinstall('H', encodefmt);
+	sunFmtInstall(&portProg);
+	sunFmtInstall(&nfsMount3Prog);
+
+	addr = netmkaddr(argv[0], "udp", "portmap");
+	if(mapit){
+		/* translate with port mapper */
+		fprint(2, "connecting to %s\n", addr);
+		if((client = sunDial(addr)) == nil)
+			sysfatal("dial %s: %r", addr);
+		client->chatty = chatty;
+		sunClientProg(client, &portProg);
+		if(strstr(addr, "udp!"))
+			proto = PortProtoUdp;
+		else
+			proto = PortProtoTcp;
+		if(getport(client, NfsMount3Program, NfsMount3Version, proto, &port) < 0)
+			sysfatal("getport: %r");
+		snprint(buf, sizeof buf, "%ud!r", port);
+		addr = netchangeport(addr, buf);
+		sunClientClose(client);
+	}
+
+	fprint(2, "connecting to %s\n", addr);
+	if((client = sunDial(addr)) == nil)
+		sysfatal("dial %s: %r", addr);
+
+	client->chatty = chatty;
+	sunClientProg(client, &nfsMount3Prog);
+
+	argv++;
+	argc--;
+
+	if(argc == 0){
+		argc = 1;
+		argv = dflt;
+	}
+	cmd = argv[0];
+	argv++;
+	argc--;
+
+	for(i=0; i<nelem(tab); i++){
+		if(strcmp(tab[i].cmd, cmd) == 0){
+			if(tab[i].narg != argc)
+				usage();
+			(*tab[i].fn)(argv);
+			threadexitsall(nil);
+		}
+	}
+	usage();
+}

+ 204 - 0
sys/src/cmd/aux/portmap.c

@@ -0,0 +1,204 @@
+/* Copyright © 2003 Russ Cox, MIT; see /sys/src/libsunrpc/COPYING */
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <sunrpc.h>
+
+int chatty;
+SunClient *client;
+
+void
+usage(void)
+{
+	fprint(2, "usage: portmap address [cmd]\n"
+		"cmd is one of:\n"
+		"\tnull\n"
+		"\tset prog vers proto port\n"
+		"\tunset prog vers proto port\n"
+		"\tgetport prog vers proto\n"
+		"\tdump (default)\n");
+	threadexitsall("usage");
+}
+
+void
+portCall(SunCall *c, PortCallType type)
+{
+	c->rpc.prog = PortProgram;
+	c->rpc.vers = PortVersion;
+	c->rpc.proc = type>>1;
+	c->rpc.iscall = !(type&1);
+	c->type = type;
+}
+
+void
+tnull(char **argv)
+{
+	PortTNull tx;
+	PortRNull rx;
+
+	USED(argv);
+
+	memset(&tx, 0, sizeof tx);
+	portCall(&tx.call, PortCallTNull);
+
+	memset(&rx, 0, sizeof rx);
+	portCall(&rx.call, PortCallRNull);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+}
+
+void
+tset(char **argv)
+{
+	PortTSet tx;
+	PortRSet rx;
+
+	memset(&tx, 0, sizeof tx);
+	portCall(&tx.call, PortCallTSet);
+	tx.map.prog = strtol(argv[0], 0, 0);
+	tx.map.vers = strtol(argv[1], 0, 0);
+	tx.map.prot = strtol(argv[2], 0, 0);
+	tx.map.port = strtol(argv[3], 0, 0);
+
+	memset(&rx, 0, sizeof rx);
+	portCall(&rx.call, PortCallRSet);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+
+	if(rx.b == 0)
+		print("rejected\n");
+}
+
+void
+tunset(char **argv)
+{
+	PortTUnset tx;
+	PortRUnset rx;
+
+	memset(&tx, 0, sizeof tx);
+	portCall(&tx.call, PortCallTUnset);
+	tx.map.prog = strtol(argv[0], 0, 0);
+	tx.map.vers = strtol(argv[1], 0, 0);
+	tx.map.prot = strtol(argv[2], 0, 0);
+	tx.map.port = strtol(argv[3], 0, 0);
+
+	memset(&rx, 0, sizeof rx);
+	portCall(&rx.call, PortCallRUnset);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+
+	if(rx.b == 0)
+		print("rejected\n");
+}
+
+void
+tgetport(char **argv)
+{
+	PortTGetport tx;
+	PortRGetport rx;
+
+	memset(&tx, 0, sizeof tx);
+	portCall(&tx.call, PortCallTGetport);
+	tx.map.prog = strtol(argv[0], 0, 0);
+	tx.map.vers = strtol(argv[1], 0, 0);
+	tx.map.prot = strtol(argv[2], 0, 0);
+
+	memset(&rx, 0, sizeof rx);
+	portCall(&rx.call, PortCallRGetport);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
+		sysfatal("rpc: %r");
+
+	print("%d\n", rx.port);
+}
+
+void
+tdump(char **argv)
+{
+	int i;
+	uchar *p;
+	PortTDump tx;
+	PortRDump rx;
+	PortMap *m;
+
+	USED(argv);
+
+	memset(&tx, 0, sizeof tx);
+	portCall(&tx.call, PortCallTDump);
+
+	memset(&rx, 0, sizeof rx);
+	portCall(&rx.call, PortCallRDump);
+
+	if(sunClientRpc(client, 0, &tx.call, &rx.call, &p) < 0)
+		sysfatal("rpc: %r");
+
+	for(i=0, m=rx.map; i<rx.nmap; i++, m++)
+		print("%ud %ud %ud %ud\n", (uint)m->prog, (uint)m->vers, (uint)m->prot, (uint)m->port);
+
+	free(p);
+}
+
+static struct {
+	char *cmd;
+	int narg;
+	void (*fn)(char**);
+} tab[] = {
+	"null",	0,	tnull,
+	"set",	4,	tset,
+	"unset",	4,	tunset,
+	"getport",	3,	tgetport,
+	"dump",	0,	tdump,
+};
+
+void
+threadmain(int argc, char **argv)
+{
+	char *dflt[] = { "dump", };
+	char *addr, *cmd;
+	int i;
+
+	ARGBEGIN{
+	case 'R':
+		chatty++;
+		break;
+	}ARGEND
+
+	if(argc < 2)
+		usage();
+
+	fmtinstall('B', sunRpcFmt);
+	fmtinstall('C', sunCallFmt);
+	fmtinstall('H', encodefmt);
+	sunFmtInstall(&portProg);
+
+	addr = netmkaddr(argv[0], "udp", "portmap");
+	if((client = sunDial(addr)) == nil)
+		sysfatal("dial %s: %r", addr);
+
+	client->chatty = chatty;
+	sunClientProg(client, &portProg);
+
+	argv++;
+	argc--;
+
+	if(argc == 0){
+		argc = 1;
+		argv = dflt;
+	}
+	cmd = argv[0];
+	argv++;
+	argc--;
+
+	for(i=0; i<nelem(tab); i++){
+		if(strcmp(tab[i].cmd, cmd) == 0){
+			if(tab[i].narg != argc)
+				usage();
+			(*tab[i].fn)(argv);
+			threadexitsall(nil);
+		}
+	}
+	usage();
+}

+ 54 - 22
sys/src/cmd/fossil/9.h

@@ -18,27 +18,56 @@ struct Msg {
 	Fcall	t;
 	Fcall	r;
 	Con*	con;
-	int	flush;
 
-	Msg*	next;
-	Msg*	prev;
+	Msg*	anext;			/* alloc */
+
+	Msg*	mnext;			/* Con */
+	Msg* 	mprev;
+
+	Msg*	rwnext;			/* proc read/write */
+
+	int	state;
+
+	Msg*	fnext;			/* proc flush */
+	Msg*	fprev;
+};
+
+enum {
+	MsgN		= 0,
+	MsgR		= 1,
+	Msg9		= 2,
+	MsgW		= 3,
+	MsgF		= 4,
 };
 
 struct Con {
-	VtLock*	lock;
-	int	fd;
 	char*	name;
 	uchar*	data;			/* max, not negotiated */
-	u32int	msize;			/* negotiated with Tversion */
+	int	isconsole;		/* immutable */
+
+	VtLock*	lock;
 	int	state;
-	int	aok;
+	int	fd;
 	Msg*	version;
-	int	isconsole;
+	u32int	msize;			/* negotiated with Tversion */
+	VtRendez* rendez;
+
+	Con*	anext;			/* alloc */
+	Con*	cnext;			/* in use */
+	Con*	cprev;
+
+	VtLock*	alock;
+	int	aok;			/* authentication done */
 
-	Msg*	mhead;			/* active queue */
+	VtLock*	mlock;
+	Msg*	mhead;			/* all Msgs on this connection */
 	Msg*	mtail;
-	VtRendez* active;
-	int	nmsg;
+	VtRendez* mrendez;
+
+	VtLock*	wlock;
+	Msg*	whead;			/* write queue */
+	Msg*	wtail;
+	VtRendez* wrendez;
 
 	VtLock*	fidlock;		/* */
 	Fid*	fidhash[NFidHash];
@@ -48,12 +77,12 @@ struct Con {
 };
 
 enum {
-	CsDead,
-	CsNew,
-	CsDown,
-	CsInit,
-	CsUp,
-	CsMoribund,
+	ConDead		= 0,
+	ConNew		= 1,
+	ConDown		= 2,
+	ConInit		= 3,
+	ConUp		= 4,
+	ConMoribund	= 5,
 };
 
 struct Fid {
@@ -64,6 +93,7 @@ struct Fid {
 	int	flags;
 
 	int	open;
+	Fsys*	fsys;
 	File*	file;
 	Qid	qid;
 	char*	uid;
@@ -73,7 +103,6 @@ struct Fid {
 
 	VtLock*	alock;			/* Tauth/Tattach */
 	AuthRpc* rpc;
-	Fsys*	fsys;
 	char*	cuname;
 
 	Fid*	hash;			/* lookup by fidno */
@@ -125,6 +154,7 @@ extern int exclUpdate(Fid*);
  * 9fid.c
  */
 extern void fidClunk(Fid*);
+extern void fidClunkAll(Con*);
 extern Fid* fidGet(Con*, u32int, int);
 extern void fidInit(void);
 extern void fidPut(Fid*);
@@ -132,10 +162,11 @@ extern void fidPut(Fid*);
 /*
  * 9fsys.c
  */
-extern Fsys* fsysGet(char*);
-extern Fs* fsysGetFs(Fsys*);
 extern void fsysFsRlock(Fsys*);
 extern void fsysFsRUnlock(Fsys*);
+extern Fs* fsysGetFs(Fsys*);
+extern Fsys* fsysGet(char*);
+extern char* fsysGetName(Fsys*);
 extern File* fsysGetRoot(Fsys*, char*);
 extern Fsys* fsysIncRef(Fsys*);
 extern int fsysInit(void);
@@ -143,7 +174,6 @@ extern int fsysNoAuthCheck(Fsys*);
 extern int fsysNoPermCheck(Fsys*);
 extern void fsysPut(Fsys*);
 extern int fsysWstatAllow(Fsys*);
-extern char* fsysGetName(Fsys*);
 
 /*
  * 9lstn.c
@@ -154,7 +184,9 @@ extern int lstnInit(void);
  * 9proc.c
  */
 extern Con* conAlloc(int, char*);
-extern void procInit(void);
+extern void conInit(void);
+extern void msgFlush(Msg*);
+extern void msgInit(void);
 
 /*
  * 9srv.c

+ 11 - 4
sys/src/cmd/fossil/9auth.c

@@ -53,6 +53,7 @@ authWrite(Fid* afid, void* data, int count)
 int
 authCheck(Fcall* t, Fid* fid, Fs* fsys)
 {
+	Con *con;
 	Fid *afid;
 	uchar buf[1];
 
@@ -61,6 +62,7 @@ authCheck(Fcall* t, Fid* fid, Fs* fsys)
 	 * protocol to do. Use a separate lock to protect altering
 	 * the auth information inside afid.
 	 */
+	con = fid->con;
 	if(t->afid == NOFID){
 		/*
 		 * If no authentication is asked for, allow
@@ -70,11 +72,14 @@ authCheck(Fcall* t, Fid* fid, Fs* fsys)
 		 * The console is allowed to attach without
 		 * authentication.
 		 */
-		if(!fid->con->isconsole &&
-		(strcmp(fid->uname, unamenone) != 0 || !fid->con->aok)){
+		vtRLock(con->alock);
+		if(!con->isconsole &&
+		(strcmp(fid->uname, unamenone) != 0 || !con->aok)){
+			vtRUnlock(con->alock);
 			consPrint("attach %s as %s: connection not authenticated, not console\n", fsysGetName(fsys), fid->uname);
 			return 0;
 		}
+		vtRUnlock(con->alock);
 
 		if((fid->uid = uidByUname(fid->uname)) == nil){
 			consPrint("attach %s as %s: unknown uname\n", fsysGetName(fsys), fid->uname);
@@ -83,7 +88,7 @@ authCheck(Fcall* t, Fid* fid, Fs* fsys)
 		return 1;
 	}
 
-	if((afid = fidGet(fid->con, t->afid, 0)) == nil){
+	if((afid = fidGet(con, t->afid, 0)) == nil){
 		consPrint("attach %s as %s: bad afid\n", fsysGetName(fsys), fid->uname);
 		return 0;
 	}
@@ -128,7 +133,9 @@ authCheck(Fcall* t, Fid* fid, Fs* fsys)
 	/*
 	 * Allow "none" once the connection has been authenticated.
 	 */
-	fid->con->aok = 1;
+	vtLock(con->alock);
+	con->aok = 1;
+	vtUnlock(con->alock);
 
 	return 1;
 }

+ 18 - 1
sys/src/cmd/fossil/9fid.c

@@ -77,6 +77,7 @@ fidAlloc(void)
 	fid->ref = 0;
 	fid->flags = 0;
 	fid->open = FidOCreate;
+	assert(fid->fsys == nil);
 	assert(fid->file == nil);
 	fid->qid = (Qid){0, 0, 0};
 	assert(fid->uid == nil);
@@ -84,7 +85,6 @@ fidAlloc(void)
 	assert(fid->db == nil);
 	assert(fid->excl == nil);
 	assert(fid->rpc == nil);
-	assert(fid->fsys == nil);
 	assert(fid->cuname == nil);
 	fid->hash = fid->next = fid->prev = nil;
 
@@ -279,6 +279,23 @@ fidClunk(Fid* fid)
 	fidFree(fid);
 }
 
+void
+fidClunkAll(Con* con)
+{
+	Fid *fid;
+	u32int fidno;
+
+	vtLock(con->fidlock);
+	while(con->fhead != nil){
+		fidno = con->fhead->fidno;
+		vtUnlock(con->fidlock);
+		if((fid = fidGet(con, fidno, FidFWlock)) != nil)
+			fidClunk(fid);
+		vtLock(con->fidlock);
+	}
+	vtUnlock(con->fidlock);
+}
+
 void
 fidInit(void)
 {

+ 11 - 29
sys/src/cmd/fossil/9p.c

@@ -627,6 +627,8 @@ rTcreate(Msg* m)
 	}
 	fileDecRef(fid->file);
 
+	fid->qid.vers = fileGetMcount(file);
+	fid->qid.path = fileGetId(file);
 	fid->file = file;
 	mode = fileGetMode(fid->file);
 	if(mode & ModeDir)
@@ -639,8 +641,6 @@ rTcreate(Msg* m)
 		fid->qid.type |= QTEXCL;
 		assert(exclAlloc(fid) != 0);
 	}
-	fid->qid.vers = fileGetMcount(file);
-	fid->qid.path = fileGetId(file);
 	if(m->t.mode & ORCLOSE)
 		open |= FidORclose;
 	fid->open = open;
@@ -883,23 +883,8 @@ rTwalk(Msg* m)
 static int
 rTflush(Msg* m)
 {
-	Msg *mp;
-	Con *con;
-	u32int oldtag;
-
-	if((oldtag = m->t.oldtag) == NOTAG)
-		return 1;
-
-	con = m->con;
-	vtLock(con->lock);
-	for(mp = con->mhead; mp != nil; mp = mp->next){
-		if(mp->t.tag == oldtag){
-			mp->flush = 1;
-			break;
-		}
-	}
-	vtUnlock(con->lock);
-
+	if(m->t.oldtag != NOTAG)
+		msgFlush(m);
 	return 1;
 }
 
@@ -1038,7 +1023,6 @@ rTversion(Msg* m)
 {
 	int v;
 	Con *con;
-	Fid *fid;
 	Fcall *r, *t;
 
 	t = &m->t;
@@ -1046,21 +1030,19 @@ rTversion(Msg* m)
 	con = m->con;
 
 	vtLock(con->lock);
-	if(con->state != CsInit){
+	if(con->state != ConInit){
 		vtUnlock(con->lock);
 		vtSetError("Tversion: down");
 		return 0;
 	}
-	con->state = CsNew;
+	con->state = ConNew;
 
 	/*
 	 * Release the karma of past lives and suffering.
+	 * Should this be done before or after checking the
+	 * validity of the Tversion?
 	 */
-	while(con->fhead != nil){
-		fid = fidGet(con, con->fhead->fidno, FidFWlock);
-		assert(fid == con->fhead);
-		fidClunk(fid);
-	}
+	fidClunkAll(con);
 
 	if(t->tag != NOTAG){
 		vtUnlock(con->lock);
@@ -1088,12 +1070,12 @@ rTversion(Msg* m)
 		if(v >= 2000){
 			r->version = VERSION9P;
 			con->msize = r->msize;
-			con->state = CsUp;
+			con->state = ConUp;
 		}
 		else if(strcmp(t->version, "9PEoF") == 0){
 			r->version = "9PEoF";
 			con->msize = r->msize;
-			con->state = CsMoribund;
+			con->state = ConMoribund;
 		}
 	}
 	vtUnlock(con->lock);

+ 536 - 206
sys/src/cmd/fossil/9proc.c

@@ -12,125 +12,332 @@ enum {
 };
 
 static struct {
-	VtLock*	lock;
-	Con**	con;			/* arena */
-	int	ncon;			/* how many in arena */
-	int	hi;			/* high watermark */
-	int	cur;			/* hint for allocation */
-
-	u32int	msize;
-} cbox;
-
-static struct {
-	VtLock*	lock;
-
-	Msg*	free;
-	VtRendez* alloc;
-
-	Msg*	head;
-	Msg*	tail;
-	VtRendez* work;
+	VtLock*	alock;			/* alloc */
+	Msg*	ahead;
+	VtRendez* arendez;
 
 	int	maxmsg;
 	int	nmsg;
+	int	nmsgstarve;
+
+	VtLock*	rlock;			/* read */
+	Msg*	rhead;
+	Msg*	rtail;
+	VtRendez* rrendez;
+
 	int	maxproc;
 	int	nproc;
+	int	nprocstarve;
 
 	u32int	msize;			/* immutable */
 } mbox;
 
+static struct {
+	VtLock*	alock;			/* alloc */
+	Con*	ahead;
+	VtRendez* arendez;
+
+	VtLock*	clock;
+	Con*	chead;
+	Con*	ctail;
+
+	int	maxcon;
+	int	ncon;
+	int	nconstarve;
+
+	u32int	msize;
+} cbox;
+
+static void
+conFree(Con* con)
+{
+	assert(con->version == nil);
+	assert(con->mhead == nil);
+	assert(con->whead == nil);
+	assert(con->nfid == 0);
+	assert(con->state == ConMoribund);
+
+	if(con->fd >= 0){
+		close(con->fd);
+		con->fd = -1;
+	}
+	con->state = ConDead;
+
+	vtLock(cbox.alock);
+	if(con->cprev != nil)
+		con->cprev->cnext = con->cnext;
+	else
+		cbox.chead = con->cnext;
+	if(con->cnext != nil)
+		con->cnext->cprev = con->cprev;
+	else
+		cbox.ctail = con->cprev;
+	con->cprev = con->cnext = nil;
+
+	if(cbox.ncon > cbox.maxcon){
+		if(con->name != nil)
+			vtMemFree(con->name);
+		vtLockFree(con->fidlock);
+		vtMemFree(con->data);
+		vtRendezAlloc(con->wrendez);
+		vtLockFree(con->wlock);
+		vtRendezAlloc(con->mrendez);
+		vtLockFree(con->mlock);
+		vtRendezAlloc(con->rendez);
+		vtLockFree(con->lock);
+		vtMemFree(con);
+		cbox.ncon--;
+		vtUnlock(cbox.alock);
+		return;
+	}
+	con->anext = cbox.ahead;
+	cbox.ahead = con;
+	if(con->anext == nil)
+		vtWakeup(cbox.arendez);
+	vtUnlock(cbox.alock);
+}
+
 static void
 msgFree(Msg* m)
 {
-	vtLock(mbox.lock);
+	assert(m->rwnext == nil);
+	assert(m->fnext == nil && m->fprev == nil);
+
+	vtLock(mbox.alock);
 	if(mbox.nmsg > mbox.maxmsg){
 		vtMemFree(m->data);
 		vtMemFree(m);
 		mbox.nmsg--;
-		vtUnlock(mbox.lock);
+		vtUnlock(mbox.alock);
 		return;
 	}
-	m->next = mbox.free;
-	mbox.free = m;
-	if(m->next == nil)
-		vtWakeup(mbox.alloc);
-	vtUnlock(mbox.lock);
+	m->anext = mbox.ahead;
+	mbox.ahead = m;
+	if(m->anext == nil)
+		vtWakeup(mbox.arendez);
+	vtUnlock(mbox.alock);
+}
+
+static Msg*
+msgAlloc(Con* con)
+{
+	Msg *m;
+
+	vtLock(mbox.alock);
+	while(mbox.ahead == nil){
+		if(mbox.nmsg >= mbox.maxmsg){
+			mbox.nmsgstarve++;
+			vtSleep(mbox.arendez);
+			continue;
+		}
+		m = vtMemAllocZ(sizeof(Msg));
+		m->data = vtMemAlloc(mbox.msize);
+		m->msize = mbox.msize;
+		mbox.nmsg++;
+		mbox.ahead = m;
+		break;
+	}
+	m = mbox.ahead;
+	mbox.ahead = m->anext;
+	m->anext = nil;
+	vtUnlock(mbox.alock);
+
+	m->con = con;
+	m->state = MsgR;
+
+	return m;
 }
 
 static void
-conFree(Con* con)
+msgMunlink(Msg* m)
 {
-	if(con->fd >= 0){
-		close(con->fd);
-		con->fd = -1;
+	Con *con;
+
+	con = m->con;
+
+	if(m->mprev != nil)
+		m->mprev->mnext = m->mnext;
+	else
+		con->mhead = m->mnext;
+	if(m->mnext != nil)
+		m->mnext->mprev = m->mprev;
+	else
+		con->mtail = m->mprev;
+	m->mprev = m->mnext = nil;
+}
+
+static void
+msgUnlinkUnlockAndFree(Msg* m)
+{
+	/*
+	 * Unlink the message from the flush and message queues,
+	 * unlock the connection message lock and free the message.
+	 * Called with con->mlock locked.
+	 */
+	if(m->fprev != nil)
+		m->fprev->fnext = m->fnext;
+	if(m->fnext != nil)
+		m->fnext->fprev = m->fprev;
+	m->fprev = m->fnext = nil;
+
+	msgMunlink(m);
+	vtUnlock(m->con->mlock);
+	msgFree(m);
+}
+
+void
+msgFlush(Msg* m)
+{
+	Msg *old;
+	Con *con;
+
+	con = m->con;
+
+	/*
+	 * Look for the message to be flushed in the
+	 * queue of all messages still on this connection.
+	 */
+	vtLock(con->mlock);
+	for(old = con->mhead; old != nil; old = old->mnext)
+		if(old->t.tag == m->t.oldtag)
+			break;
+	if(old == nil){
+		if(Dflag)
+			fprint(2, "msgFlush: cannot find %d\n", m->t.oldtag);
+		vtUnlock(con->mlock);
+		return;
 	}
 
-	assert(con->version == nil);
-	assert(con->mhead == nil);
-	assert(con->nmsg == 0);
-	assert(con->nfid == 0);
-	assert(con->state == CsMoribund);
+	/*
+	 * Found it.
+	 *
+	 * Easy case is no 9P processing done yet,
+	 * message is on the read queue.
+	 * Mark the message as flushed and let the read
+	 * process throw it away after after pulling
+	 * it off the read queue.
+	 */
+	if(old->state == MsgR){
+		old->state = MsgF;
+		if(Dflag)
+			fprint(2, "msgFlush: change %d from MsgR to MsgF\n", m->t.oldtag);
+		vtUnlock(con->mlock);
+		return;
+	}
 
-	con->state = CsDead;
+	/*
+	 * Flushing flushes.
+	 * Since they don't affect the server state, flushes
+	 * can be deleted when in Msg9 or MsgW state.
+	 */
+	if(old->t.type == Tflush){
+		/*
+		 * For Msg9 state, the old message may
+		 * or may not be on the write queue.
+		 * Mark the message as flushed and let
+		 * the write process throw it away after
+		 * after pulling it off the write queue.
+		 */
+		if(old->state == Msg9){
+			old->state = MsgF;
+			if(Dflag)
+				fprint(2, "msgFlush: change %d from Msg9 to MsgF\n", m->t.oldtag);
+			vtUnlock(con->mlock);
+			return;
+		}
+		assert(old->state == MsgW);
+
+		/*
+		 * A flush in MsgW state implies it is waiting
+		 * for its corresponding old message to be written,
+		 * so it can be deleted right here, right now...
+		 * right here, right now... right here, right now...
+		 * right about now... the funk soul brother.
+		 */
+		if(Dflag)
+			fprint(2, "msgFlush: delete pending flush %F\n", &old->t);
+		msgUnlinkUnlockAndFree(old);
+		return;
+	}
+
+	/*
+	 * Must wait for the old message to be written.
+	 * Add m to old's flush queue.
+	 * Old is the head of its own flush queue.
+	 */
+	m->fprev = old;
+	m->fnext = old->fnext;
+	if(m->fnext)
+		m->fnext->fprev = m;
+	old->fnext = m;
+	if(Dflag)
+		fprint(2, "msgFlush: add %d to %d queue\n", m->t.tag, old->t.tag);
+	vtUnlock(con->mlock);
 }
 
 static void
 msgProc(void*)
 {
-	int n;
 	Msg *m;
 	char *e;
 	Con *con;
 
-	vtThreadSetName("msg");
+	vtThreadSetName("msgProc");
 
-	vtLock(mbox.lock);
-	while(mbox.nproc <= mbox.maxproc){
-		while(mbox.head == nil)
-			vtSleep(mbox.work);
-		m = mbox.head;
-		mbox.head = m->next;
-		m->next = nil;
+	for(;;){
+		/*
+		 * If surplus to requirements, exit.
+		 * If not, wait for and pull a message off
+		 * the read queue.
+		 */
+		vtLock(mbox.rlock);
+		if(mbox.nproc > mbox.maxproc){
+			mbox.nproc--;
+			vtUnlock(mbox.rlock);
+			break;
+		}
+		while(mbox.rhead == nil)
+			vtSleep(mbox.rrendez);
+		m = mbox.rhead;
+		mbox.rhead = m->rwnext;
+		m->rwnext = nil;