Browse Source

Plan 9 from Bell Labs 2007-05-22

David du Colombier 14 years ago
parent
commit
08f044643a
14 changed files with 1568 additions and 150 deletions
  1. 16 9
      dist/replica/_plan9.db
  2. 16 10
      dist/replica/plan9.db
  3. 22 0
      dist/replica/plan9.log
  4. 8 2
      rc/bin/cpurc
  5. 6 0
      rc/bin/termrc
  6. 67 10
      sys/man/1/du
  7. 141 0
      sys/man/1/ecp
  8. 88 0
      sys/man/8/6in4
  9. 8 12
      sys/src/9/port/devfs.c
  10. 207 96
      sys/src/cmd/du.c
  11. 617 0
      sys/src/cmd/ecp.c
  12. 44 10
      sys/src/cmd/import.c
  13. 327 0
      sys/src/cmd/ip/6in4.c
  14. 1 1
      sys/src/cmd/ip/traceroute.c

+ 16 - 9
dist/replica/_plan9.db

@@ -221,8 +221,9 @@
 386/bin/disk/prep - 775 sys sys 1168402303 96167
 386/bin/disk/sacfs - 775 sys sys 1020319075 79882
 386/bin/dossrv - 775 sys sys 1178568267 136537
-386/bin/du - 775 sys sys 1168402304 74244
+386/bin/du - 775 sys sys 1179777650 76275
 386/bin/echo - 775 sys sys 1168402304 57592
+386/bin/ecp - 775 sys sys 1179777651 72588
 386/bin/ed - 775 sys sys 1179548024 93463
 386/bin/eqn - 775 sys sys 1173754555 244858
 386/bin/execnet - 775 sys sys 1179548024 176802
@@ -282,9 +283,10 @@
 386/bin/ico - 775 sys sys 1179372092 162180
 386/bin/iconv - 775 sys sys 1168402316 113629
 386/bin/idiff - 775 sys sys 1168402317 76342
-386/bin/import - 775 sys sys 1179372092 101830
+386/bin/import - 775 sys sys 1179777651 102499
 386/bin/iostats - 775 sys sys 1178568280 98906
 386/bin/ip - 20000000775 sys sys 1016920851 0
+386/bin/ip/6in4 - 775 sys sys 1179777651 88513
 386/bin/ip/dhcpclient - 775 sys sys 1178568281 92992
 386/bin/ip/dhcpd - 775 sys sys 1179458669 149879
 386/bin/ip/dhcpleases - 775 sys sys 1178568281 85069
@@ -5482,7 +5484,7 @@ rc/bin/b: - 775 sys sys 1015089510 204
 rc/bin/broke - 775 sys sys 1143389260 142
 rc/bin/bundle - 775 sys sys 945617206 173
 rc/bin/c: - 775 sys sys 1015089511 86
-rc/bin/cpurc - 775 sys sys 1176444833 1884
+rc/bin/cpurc - 775 sys sys 1179769494 2009
 rc/bin/cpurc.local - 775 sys sys 1176827268 367
 rc/bin/delkey - 775 sys sys 1109429137 643
 rc/bin/dial - 20000000775 sys sys 1059180057 0
@@ -5606,7 +5608,7 @@ rc/bin/start - 775 sys sys 945617209 120
 rc/bin/stock - 775 sys sys 1143126371 292
 rc/bin/stop - 775 sys sys 945617209 110
 rc/bin/tel - 775 sys sys 1161209756 128
-rc/bin/termrc - 775 sys sys 1177705963 2281
+rc/bin/termrc - 775 sys sys 1179769653 2479
 rc/bin/termrc.local - 775 sys sys 1176500067 425
 rc/bin/thesaurus - 775 sys sys 1068054167 246
 rc/bin/tlsclienttunnel - 775 sys sys 1024375633 153
@@ -7358,8 +7360,9 @@ sys/man/1/deroff - 664 sys sys 1113743324 1826
 sys/man/1/diff - 664 sys sys 1140694865 3089
 sys/man/1/doc2txt - 664 sys sys 1166761437 2304
 sys/man/1/doctype - 664 sys sys 1113743325 860
-sys/man/1/du - 664 sys sys 1134556965 1358
+sys/man/1/du - 664 sys sys 1179726832 2281
 sys/man/1/echo - 664 sys sys 1079969656 430
+sys/man/1/ecp - 664 sys sys 1179725473 2576
 sys/man/1/ed - 664 sys sys 961259286 13916
 sys/man/1/emacs - 664 sys sys 944959673 199
 sys/man/1/eqn - 664 sys sys 1134592617 5655
@@ -7764,6 +7767,7 @@ sys/man/7/playlistfs - 664 sys sys 1103794042 3831
 sys/man/7/scat - 664 sys sys 970069855 8904
 sys/man/8 - 20000000775 sys sys 1162240005 0
 sys/man/8/0intro - 664 sys sys 944959679 247
+sys/man/8/6in4 - 664 sys sys 1179779762 1655
 sys/man/8/9load - 664 sys sys 1094676489 9170
 sys/man/8/9pcon - 664 sys sys 1145881850 2234
 sys/man/8/INDEX - 664 sys sys 1178249215 2888
@@ -8164,7 +8168,7 @@ sys/src/9/port/devcons.c - 664 sys sys 1176658321 22943
 sys/src/9/port/devdraw.c - 664 sys sys 1147023550 44447
 sys/src/9/port/devdup.c - 664 sys sys 1014931172 2332
 sys/src/9/port/devenv.c - 664 sys sys 1169498893 7015
-sys/src/9/port/devfs.c - 664 sys sys 1179356387 12406
+sys/src/9/port/devfs.c - 664 sys sys 1179726929 12395
 sys/src/9/port/devkbmap.c - 664 sys sys 1130763846 3064
 sys/src/9/port/devkprof.c - 664 sys sys 1014931173 3111
 sys/src/9/port/devloopback.c - 664 sys sys 1138458368 14579
@@ -10224,8 +10228,9 @@ sys/src/cmd/dossrv/lock.c - 664 sys sys 954305577 504
 sys/src/cmd/dossrv/mkfile - 664 sys sys 1032057270 310
 sys/src/cmd/dossrv/xfile.c - 664 sys sys 1017679315 4391
 sys/src/cmd/dossrv/xfssrv.c - 664 sys sys 1018323479 3397
-sys/src/cmd/du.c - 664 sys sys 1134659785 3421
+sys/src/cmd/du.c - 664 sys sys 1179726818 5425
 sys/src/cmd/echo.c - 664 sys sys 1081736344 581
+sys/src/cmd/ecp.c - 664 sys sys 1179725473 14664
 sys/src/cmd/ed.c - 664 sys sys 1143695286 21933
 sys/src/cmd/eqn - 20000000775 sys sys 1055698950 0
 sys/src/cmd/eqn/diacrit.c - 664 sys sys 944960989 2302
@@ -12450,7 +12455,7 @@ sys/src/cmd/htmlroff/t9.c - 664 sys sys 1138396082 47
 sys/src/cmd/htmlroff/util.c - 664 sys sys 1138458817 1625
 sys/src/cmd/iconv.c - 664 sys sys 1039753035 1801
 sys/src/cmd/idiff.c - 664 sys sys 1014926695 6896
-sys/src/cmd/import.c - 664 sys sys 1174944074 7118
+sys/src/cmd/import.c - 664 sys sys 1179763017 7949
 sys/src/cmd/init.c - 664 sys sys 1121977160 4462
 sys/src/cmd/iostats - 20000000775 sys sys 1055699098 0
 sys/src/cmd/iostats/iostats.c - 664 sys sys 1140099908 10209
@@ -12458,6 +12463,7 @@ sys/src/cmd/iostats/mkfile - 664 sys sys 1014925727 122
 sys/src/cmd/iostats/statfs.h - 664 sys sys 1140099908 2626
 sys/src/cmd/iostats/statsrv.c - 664 sys sys 1166824341 10773
 sys/src/cmd/ip - 20000000775 sys sys 1128605981 0
+sys/src/cmd/ip/6in4.c - 664 sys sys 1179783180 7341
 sys/src/cmd/ip/arp.h - 664 sys sys 944961005 751
 sys/src/cmd/ip/dhcp.h - 664 sys sys 1178483074 3419
 sys/src/cmd/ip/dhcpclient.c - 664 sys sys 1178482836 11969
@@ -12603,7 +12609,7 @@ sys/src/cmd/ip/telnet.c - 664 sys sys 1162416789 8621
 sys/src/cmd/ip/telnet.h - 664 sys sys 1015090250 5902
 sys/src/cmd/ip/telnetd.c - 664 sys sys 1135487948 11772
 sys/src/cmd/ip/tftpd.c - 664 sys sys 1106943821 7646
-sys/src/cmd/ip/traceroute.c - 664 sys sys 1169162086 8637
+sys/src/cmd/ip/traceroute.c - 664 sys sys 1179779560 8645
 sys/src/cmd/ip/udpecho.c - 664 sys sys 1178483004 789
 sys/src/cmd/join.c - 664 sys sys 1158251357 7550
 sys/src/cmd/jpg - 20000000775 sys sys 1132458749 0
@@ -15832,3 +15838,4 @@ 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/ip/traceroute - 775 sys sys 1179804021 72750

+ 16 - 10
dist/replica/plan9.db

@@ -221,8 +221,9 @@
 386/bin/disk/prep - 775 sys sys 1168402303 96167
 386/bin/disk/sacfs - 775 sys sys 1020319075 79882
 386/bin/dossrv - 775 sys sys 1178568267 136537
-386/bin/du - 775 sys sys 1168402304 74244
+386/bin/du - 775 sys sys 1179777650 76275
 386/bin/echo - 775 sys sys 1168402304 57592
+386/bin/ecp - 775 sys sys 1179777651 72588
 386/bin/ed - 775 sys sys 1179548024 93463
 386/bin/eqn - 775 sys sys 1173754555 244858
 386/bin/execnet - 775 sys sys 1179548024 176802
@@ -282,9 +283,10 @@
 386/bin/ico - 775 sys sys 1179372092 162180
 386/bin/iconv - 775 sys sys 1168402316 113629
 386/bin/idiff - 775 sys sys 1168402317 76342
-386/bin/import - 775 sys sys 1179372092 101830
+386/bin/import - 775 sys sys 1179777651 102499
 386/bin/iostats - 775 sys sys 1178568280 98906
 386/bin/ip - 20000000775 sys sys 1016920851 0
+386/bin/ip/6in4 - 775 sys sys 1179777651 88513
 386/bin/ip/dhcpclient - 775 sys sys 1178568281 92992
 386/bin/ip/dhcpd - 775 sys sys 1179458669 149879
 386/bin/ip/dhcpleases - 775 sys sys 1178568281 85069
@@ -315,7 +317,7 @@
 386/bin/ip/rlogind - 775 sys sys 1178568290 66192
 386/bin/ip/telnetd - 775 sys sys 1179372096 121990
 386/bin/ip/tftpd - 775 sys sys 1179372096 130740
-386/bin/ip/traceroute - 775 sys sys 1178568291 72721
+386/bin/ip/traceroute - 775 sys sys 1179804021 72750
 386/bin/ip/udpecho - 775 sys sys 1178508500 43095
 386/bin/join - 775 sys sys 1168402330 114382
 386/bin/jpg - 775 sys sys 1179372097 174971
@@ -5482,7 +5484,7 @@ rc/bin/b: - 775 sys sys 1015089510 204
 rc/bin/broke - 775 sys sys 1143389260 142
 rc/bin/bundle - 775 sys sys 945617206 173
 rc/bin/c: - 775 sys sys 1015089511 86
-rc/bin/cpurc - 775 sys sys 1176444833 1884
+rc/bin/cpurc - 775 sys sys 1179769494 2009
 rc/bin/cpurc.local - 775 sys sys 1176827268 367
 rc/bin/delkey - 775 sys sys 1109429137 643
 rc/bin/dial - 20000000775 sys sys 1059180057 0
@@ -5606,7 +5608,7 @@ rc/bin/start - 775 sys sys 945617209 120
 rc/bin/stock - 775 sys sys 1143126371 292
 rc/bin/stop - 775 sys sys 945617209 110
 rc/bin/tel - 775 sys sys 1161209756 128
-rc/bin/termrc - 775 sys sys 1177705963 2281
+rc/bin/termrc - 775 sys sys 1179769653 2479
 rc/bin/termrc.local - 775 sys sys 1176500067 425
 rc/bin/thesaurus - 775 sys sys 1068054167 246
 rc/bin/tlsclienttunnel - 775 sys sys 1024375633 153
@@ -7358,8 +7360,9 @@ sys/man/1/deroff - 664 sys sys 1113743324 1826
 sys/man/1/diff - 664 sys sys 1140694865 3089
 sys/man/1/doc2txt - 664 sys sys 1166761437 2304
 sys/man/1/doctype - 664 sys sys 1113743325 860
-sys/man/1/du - 664 sys sys 1134556965 1358
+sys/man/1/du - 664 sys sys 1179726832 2281
 sys/man/1/echo - 664 sys sys 1079969656 430
+sys/man/1/ecp - 664 sys sys 1179725473 2576
 sys/man/1/ed - 664 sys sys 961259286 13916
 sys/man/1/emacs - 664 sys sys 944959673 199
 sys/man/1/eqn - 664 sys sys 1134592617 5655
@@ -7764,6 +7767,7 @@ sys/man/7/playlistfs - 664 sys sys 1103794042 3831
 sys/man/7/scat - 664 sys sys 970069855 8904
 sys/man/8 - 20000000775 sys sys 1162240005 0
 sys/man/8/0intro - 664 sys sys 944959679 247
+sys/man/8/6in4 - 664 sys sys 1179779762 1655
 sys/man/8/9load - 664 sys sys 1094676489 9170
 sys/man/8/9pcon - 664 sys sys 1145881850 2234
 sys/man/8/INDEX - 664 sys sys 1178249215 2888
@@ -8164,7 +8168,7 @@ sys/src/9/port/devcons.c - 664 sys sys 1176658321 22943
 sys/src/9/port/devdraw.c - 664 sys sys 1147023550 44447
 sys/src/9/port/devdup.c - 664 sys sys 1014931172 2332
 sys/src/9/port/devenv.c - 664 sys sys 1169498893 7015
-sys/src/9/port/devfs.c - 664 sys sys 1179356387 12406
+sys/src/9/port/devfs.c - 664 sys sys 1179726929 12395
 sys/src/9/port/devkbmap.c - 664 sys sys 1130763846 3064
 sys/src/9/port/devkprof.c - 664 sys sys 1014931173 3111
 sys/src/9/port/devloopback.c - 664 sys sys 1138458368 14579
@@ -10224,8 +10228,9 @@ sys/src/cmd/dossrv/lock.c - 664 sys sys 954305577 504
 sys/src/cmd/dossrv/mkfile - 664 sys sys 1032057270 310
 sys/src/cmd/dossrv/xfile.c - 664 sys sys 1017679315 4391
 sys/src/cmd/dossrv/xfssrv.c - 664 sys sys 1018323479 3397
-sys/src/cmd/du.c - 664 sys sys 1134659785 3421
+sys/src/cmd/du.c - 664 sys sys 1179726818 5425
 sys/src/cmd/echo.c - 664 sys sys 1081736344 581
+sys/src/cmd/ecp.c - 664 sys sys 1179725473 14664
 sys/src/cmd/ed.c - 664 sys sys 1143695286 21933
 sys/src/cmd/eqn - 20000000775 sys sys 1055698950 0
 sys/src/cmd/eqn/diacrit.c - 664 sys sys 944960989 2302
@@ -12450,7 +12455,7 @@ sys/src/cmd/htmlroff/t9.c - 664 sys sys 1138396082 47
 sys/src/cmd/htmlroff/util.c - 664 sys sys 1138458817 1625
 sys/src/cmd/iconv.c - 664 sys sys 1039753035 1801
 sys/src/cmd/idiff.c - 664 sys sys 1014926695 6896
-sys/src/cmd/import.c - 664 sys sys 1174944074 7118
+sys/src/cmd/import.c - 664 sys sys 1179763017 7949
 sys/src/cmd/init.c - 664 sys sys 1121977160 4462
 sys/src/cmd/iostats - 20000000775 sys sys 1055699098 0
 sys/src/cmd/iostats/iostats.c - 664 sys sys 1140099908 10209
@@ -12458,6 +12463,7 @@ sys/src/cmd/iostats/mkfile - 664 sys sys 1014925727 122
 sys/src/cmd/iostats/statfs.h - 664 sys sys 1140099908 2626
 sys/src/cmd/iostats/statsrv.c - 664 sys sys 1166824341 10773
 sys/src/cmd/ip - 20000000775 sys sys 1128605981 0
+sys/src/cmd/ip/6in4.c - 664 sys sys 1179783180 7341
 sys/src/cmd/ip/arp.h - 664 sys sys 944961005 751
 sys/src/cmd/ip/dhcp.h - 664 sys sys 1178483074 3419
 sys/src/cmd/ip/dhcpclient.c - 664 sys sys 1178482836 11969
@@ -12603,7 +12609,7 @@ sys/src/cmd/ip/telnet.c - 664 sys sys 1162416789 8621
 sys/src/cmd/ip/telnet.h - 664 sys sys 1015090250 5902
 sys/src/cmd/ip/telnetd.c - 664 sys sys 1135487948 11772
 sys/src/cmd/ip/tftpd.c - 664 sys sys 1106943821 7646
-sys/src/cmd/ip/traceroute.c - 664 sys sys 1169162086 8637
+sys/src/cmd/ip/traceroute.c - 664 sys sys 1179779560 8645
 sys/src/cmd/ip/udpecho.c - 664 sys sys 1178483004 789
 sys/src/cmd/join.c - 664 sys sys 1158251357 7550
 sys/src/cmd/jpg - 20000000775 sys sys 1132458749 0

+ 22 - 0
dist/replica/plan9.log

@@ -49078,3 +49078,25 @@
 1179700222 0 c sys/src/9/ip/ipifc.c - 664 sys sys 1179700017 34199
 1179709223 0 c sys/man/3/ip - 664 sys sys 1179708346 24886
 1179709223 1 c sys/src/9/ip/ipmux.c - 664 sys sys 1179708314 15363
+1179725444 0 a sys/man/1/ecp - 664 sys sys 1179725473 2576
+1179725444 1 a sys/man/8/6in4 - 664 sys sys 1179725521 1514
+1179725444 2 a sys/src/cmd/ip/6in4.c - 664 sys sys 1179725515 6941
+1179725444 3 a sys/src/cmd/ecp.c - 664 sys sys 1179725473 14664
+1179727242 0 c 386/bin/du - 775 sys sys 1179726829 76345
+1179727242 1 a 386/bin/ecp - 775 sys sys 1179725473 72658
+1179727242 2 a 386/bin/ip/6in4 - 775 sys sys 1179725607 88513
+1179727242 3 c sys/man/1/du - 664 sys sys 1179726832 2281
+1179727242 4 c sys/src/9/port/devfs.c - 664 sys sys 1179726929 12395
+1179727242 5 c sys/src/cmd/du.c - 664 sys sys 1179726818 5425
+1179763238 0 c sys/src/cmd/import.c - 664 sys sys 1179763017 7949
+1179770423 0 c rc/bin/cpurc - 775 sys sys 1179769494 2009
+1179770423 1 c rc/bin/termrc - 775 sys sys 1179769653 2479
+1179779422 0 c 386/bin/du - 775 sys sys 1179777650 76275
+1179779422 1 c 386/bin/import - 775 sys sys 1179777651 102499
+1179779422 2 c 386/bin/ecp - 775 sys sys 1179777651 72588
+1179779422 3 c 386/bin/ip/6in4 - 775 sys sys 1179777651 88513
+1179781224 0 c sys/man/8/6in4 - 664 sys sys 1179779762 1655
+1179781224 1 c sys/src/cmd/ip/6in4.c - 664 sys sys 1179779683 7114
+1179781224 2 c sys/src/cmd/ip/traceroute.c - 664 sys sys 1179779560 8645
+1179783024 0 c sys/src/cmd/ip/6in4.c - 664 sys sys 1179783180 7341
+1179804623 0 c 386/bin/ip/traceroute - 775 sys sys 1179804021 72750

+ 8 - 2
rc/bin/cpurc

@@ -12,8 +12,6 @@ sysname=`{cat /dev/sysname}
 # parallelism for mk
 NPROC = `{wc -l </dev/sysstat}
 
-prompt=($sysname^'# ' '	')
-
 # site-specific startup
 if(test -e /rc/bin/cpurc.local)
 	. /rc/bin/cpurc.local
@@ -23,6 +21,14 @@ if(test -e /rc/bin/cpurc.local)
 # ndb/dns -s
 # ip/dhcpd
 # ip/tftpd
+
+if (~ $#sysname 0 || ~ $sysname '') {
+	sysname = helix			# default
+	echo -n $sysname >/dev/sysname
+}
+prompt=($sysname^'# ' '	')
+
+# cpu-specific startup
 if(test -e /cfg/$sysname/cpurc)
 	. /cfg/$sysname/cpurc
 

+ 6 - 0
rc/bin/termrc

@@ -18,6 +18,8 @@ if (! ~ $#disk 0)
 	swap $disk(1) >/dev/null >[2=1]
 rm /env/disk
 
+# we do this before we have a name.  we may need to do network
+# setup so that we can get a name.
 if(test -e /rc/bin/termrc.local)
 	. /rc/bin/termrc.local
 
@@ -25,6 +27,10 @@ if(test -e /rc/bin/termrc.local)
 if(! test -e /srv/cs)
 	ndb/cs -f $NDBFILE
 sysname=`{cat /dev/sysname}
+if (~ $#sysname 0 || ~ $sysname '') {
+	sysname = gnot			# default
+	echo -n $sysname >/dev/sysname
+}
 
 # machine specific startup (e.g., for devices not probed)
 if(test -e /cfg/$sysname/termrc)

+ 67 - 10
sys/man/1/du

@@ -4,13 +4,14 @@ du \- disk usage
 .SH SYNOPSIS
 .B du
 [
-.B -afnqstu
-]
-[
+.B -aefhnqstu
+] [
 .B -b
 .I size
-]
-[
+] [
+.B -p
+.I SI-prefix
+] [
 .I file ...
 ]
 .SH DESCRIPTION
@@ -19,7 +20,7 @@ gives the number of Kbytes allocated to data blocks
 of named
 .I files
 and, recursively, of files in named directories.
-It assumes storage is quantized in units of 1024 bytes (Kbytes) by default. 
+It assumes storage is quantized in units of 1024 bytes (Kbytes) by default.
 Other values can be set by the
 .B -b
 option;
@@ -42,8 +43,7 @@ Normally counts are printed only for contained directories.
 .PP
 The
 .B -f
-option ignores errors,
-otherwise it stops on the first error.
+option suppresses the printing of warning messages.
 .PP
 The
 .B -n
@@ -66,15 +66,72 @@ The
 option prints, in the format of
 .B du
 .BR -n ,
-the QID of
+the QID path of
 each file rather than the size.
 .PP
-Finally, the
+The
 .B -s
 option causes
 .I du
 to descend the hierarchy as always, but to print only a summary line
 for each
 .IR file .
+.PP
+The
+.B -e
+option causes
+.I du
+to print values (sizes, times or QID paths)
+in `scientific notation' via
+.IR print (2)'s
+.BR %g .
+.PP
+The
+.B -h
+option causes
+.I du
+to print values (sizes, times or QID paths)
+in scientific notation,
+scaled to less than 1024, and with a suitable SI prefix
+(e.g.,
+.L G
+for binary gigabytes).
+.PP
+The
+.B -p
+option causes
+.I du
+to print values (sizes, times or QID paths)
+in units of
+.IR SI-prefix .
+Case is ignored when looking up
+.IR SI-prefix .
+An empty
+.IR SI-prefix
+corresponds to a scale factor of 1 (e.g., print sizes in bytes).
+.\" .PP
+.\" The
+.\" .B -r
+.\" option causes
+.\" .I du
+.\" to read and discard every byte of every file encountered.
+.SH EXAMPLES
+Print the size of
+.L /tmp
+in fractional binary gigabytes:
+.IP
+.EX
+% du -sepg /tmp
+\&.6960154	/tmp
+.EE
+.LP
+Print the size of
+.L /tmp
+in bytes and in scientific notation:
+.IP
+.EX
+% du -sep '' /tmp
+7.473408e+08	/tmp
+.EE
 .SH SOURCE
 .B /sys/src/cmd/du.c

+ 141 - 0
sys/man/1/ecp

@@ -0,0 +1,141 @@
+.TH ECP 1
+.SH NAME
+ecp \- fast copy, handling errors
+.SH SYNOPSIS
+.in +.5i
+.ti -.5i
+.B ecp
+[
+.B \-bcprvZ
+] [
+.B \-B
+.I block-size
+] [
+.B \-e
+.I max-errors
+] [
+.B \-i
+.I issect
+] [
+.B \-o
+.I ossect
+] [
+.B \-s
+.I sector-size
+]
+.I sectors
+.I input
+.I output
+.SH DESCRIPTION
+.I Ecp
+copies
+.I sectors
+disk sectors of the specified
+.I input
+file to the specified
+.I output
+file.
+.I Ecp
+copies multiple sectors (a `block') at a time for speed.
+When
+.I ecp
+encounters an I/O error,
+it transfers the current block again,
+assuming the file is seekable,
+one sector at a time,
+prints the sector number(s) of the error(s),
+and continues copying.
+.PP
+Options are:
+.TP 4
+.B \-b
+reblock
+.IR input
+on short reads;
+this was used mainly when reading a pipe on standard input
+on 4.2+BSD systems.
+.TP
+.B \-B
+sets the block size (16,384 bytes by default) to
+.IR block-size .
+.TP
+.B \-c
+ask for confirmation on
+.B /dev/cons
+before starting the copy.
+.TP
+.B \-e
+sets a maximum number of consecutive I/O errors to permit
+at the beginning of the copy before quitting to
+.IR max-errors .
+Lots of consecutive errors may indicate a deeper problem,
+such as missing media.
+By default there is no limit.
+.TP
+.B \-i
+seeks to sector
+.I issect
+(assuming zero-origin)
+before beginning input.
+.TP
+.B \-o
+seeks to sector
+.I ossect
+(assuming zero-origin)
+before beginning output.
+.TP
+.B \-p
+print reassuring progress reports;
+helpful mainly when dealing with cranky hardware.
+.TP
+.B \-r
+copy sector groups in reverse order,
+assuming the files are seekable;
+this is most useful when
+.I input
+and
+.I output
+overlap.
+.TP
+.B \-s
+sets the sector size (512 bytes by default) to
+.IR sector-size .
+.TP
+.B \-v
+verify the copy by rereading the
+.I input
+and
+.I output
+files after copying all sectors.
+This is intended to force the disk to deliver the actual
+data written on it rather than some cached copy.
+The locations of any differences are printed.
+.TP
+.B \-Z
+`Swizzle' the input: stir the bits around in some fashion.
+Intended for diagnosing bad disks by copying a disk to itself
+a few times with swizzling on (to defeat caching in operating systems
+or disk controllers).
+.SH "SEE ALSO"
+.I fcp
+in
+.IR cp (1),
+.IR dd (1),
+.IR dup (3)
+.SH BUGS
+.BR \-i ,
+.BR \-o ,
+.BR \-r ,
+.B \-v
+and error retries only work on devices capable of seeking.
+.PP
+The set of options reflects decades of experience
+dealing with troublesome hardware.
+.PP
+If the input file is a tape and
+the last record on the tape before a file mark is less than
+.I blocksize
+bytes long,
+then
+.I ecp
+will read through past the file mark and into the next file.

+ 88 - 0
sys/man/8/6in4

@@ -0,0 +1,88 @@
+.TH 6IN4 8
+.SH NAME
+6in4 - configure and run automatic or manual 6to4 tunnel of IPv6 through IPv4
+.SH SYNOPSIS
+.B ip/6in4
+[
+.B -ag
+] [
+.B -x
+.I netmtpt
+]
+.IB local6[ / mask]
+[
+.I remote4
+[
+.I remote6
+] ]
+.B &
+.SH DESCRIPTION
+.I 6in4
+sets up and maintains a tunnel of IPv6 traffic through an IPv4 connection.
+.I Local6
+and
+.I mask
+define the IPv6 address and subnet of the near end of the tunnel
+.RI ( mask
+defaults to
+.L /128
+for a single-host
+tunnel),
+.I remote4
+is the IPv4 address of the far end of the tunnel
+(must be given explicitly for a configured tunnel, or
+defaults to the anycast address 192.88.99.1 for
+.IR 6to4 ),
+and
+.I remote6
+is the IPv6 address of the far end of the tunnel
+(used as the point-to-point destination for routing, and
+defaults to a link-local address constructed from
+.IR remote4 ).
+.PP
+Supply
+.B -g
+to use the tunnel as the default route for global IPv6 addresses.
+.B -x
+uses the network mounted at
+.I netmtpt
+instead of
+.LR /net .
+.SH EXAMPLES
+If your public IPv6 address is
+.LR 1.2.3.4,
+you can start a 6to4 tunnel simply with
+.IP
+.EX
+6in4 -g 2002:0102:0304::1/48
+.EE
+.PP
+If you use a tunnel broker at address
+.LR 5.6.7.8 , configured to
+give you a
+.L /64
+subnet with address
+.LR 2001:1122:3344:5566:: ,
+you can start the tunnel with
+.IP
+.EX
+6in4 -g 2001:1122:3344:5566::/64 5.6.7.8
+.EE
+.SH FILES
+.B /net/ipmux
+.SH SEE ALSO
+.I ipmux
+in
+.IR ip (3)
+.br
+.B /lib/rfc/rfc3056
+.br
+.B /lib/rfc/rfc3068
+.SH BUGS
+Needs a kernel with an
+.I ipmux
+driver.
+.PP
+The tunnel client filters addresses fairly conservatively in both directions.
+However it's not watertight,
+and may be flakey in other ways so don't put too much trust in it.

+ 8 - 12
sys/src/9/port/devfs.c

@@ -445,16 +445,16 @@ io(Fsdev *mp, Inner *in, int isread, void *a, long l, vlong off)
 	if (isread) {
 		wl = devtab[mc->type]->read(mc, a, l, off);
 		if (wl != l) {
-			print("#k: %s byte %,lld (of %s): short read\n",
-				in->iname, off, mp->name);
-			nexterror();
+//			print("#k: %s byte %,lld (of %s): short read\n",
+//				in->iname, off, mp->name);
+			error("#k: short read");
 		}
 	} else {
 		wl = devtab[mc->type]->write(mc, a, l, off);
 		if (wl != l) {
-			print("#k: %s byte %,lld (of %s): write error\n",
-				in->iname, off, mp->name);
-			nexterror();
+//			print("#k: %s byte %,lld (of %s): write error\n",
+//				in->iname, off, mp->name);
+			error("#k: write error");
 		}
 	}
 	poperror();
@@ -565,10 +565,8 @@ mread(Chan *c, void *a, long n, vlong off)
 		break;
 	case Fmirror:
 		for (i = 0; i < mp->ndevs; i++){
-			if (waserror()){
-				poperror();
+			if (waserror())
 				continue;
-			}
 			in = &mp->inner[i];
 			l = io(mp, in, Isread, a, n, off);
 			poperror();
@@ -623,10 +621,8 @@ mwrite(Chan *c, void *a, long n, vlong off)
 	case Fmirror:
 		allbad = 1;
 		for (i = mp->ndevs - 1; i >= 0; i--){
-			if (waserror()){
-				poperror();
+			if (waserror())
 				continue;
-			}
 			in = &mp->inner[i];
 			l = io(mp, in, Iswrite, a, n, off);
 			poperror();

+ 207 - 96
sys/src/cmd/du.c

@@ -1,35 +1,79 @@
+/*
+ * du - print disk usage
+ */
 #include <u.h>
 #include <libc.h>
 #include <String.h>
 
 extern	vlong	du(char*, Dir*);
-extern	vlong	k(vlong);
 extern	void	err(char*);
-extern	int	warn(char*);
+extern	vlong	blkmultiple(vlong);
 extern	int	seen(Dir*);
+extern	int	warn(char*);
+
+enum {
+	Vkilo = 1024LL,
+};
+
+/* rounding up, how many units does amt occupy? */
+#define HOWMANY(amt, unit)	(((amt)+(unit)-1) / (unit))
+#define ROUNDUP(amt, unit)	(HOWMANY(amt, unit) * (unit))
 
 int	aflag;
+int	autoscale;
 int	fflag;
-int	nflag;
+int	fltflag;
+int	qflag;
+int	readflg;
 int	sflag;
 int	tflag;
 int	uflag;
-int	qflag;
+
 char	*fmt = "%llud\t%q\n";
-vlong	blocksize = 1024LL;
+char	*readbuf;
+vlong	blocksize = Vkilo;	/* actually more likely to be 4K or 8K */
+vlong	unit;			/* scale factor for output */
+
+static char *pfxes[] = {	/* SI prefixes for units > 1 */
+	"",
+	"k", "M", "G",
+	"T", "P", "E",
+	"Z", "Y",
+	nil,
+};
 
 void
 usage(void)
 {
-	fprint(2, "usage: du [-afnqstu] [-b size] [file ...]\n");
+	fprint(2, "usage: du [-aefhnqstu] [-b size] [-p si-pfx] [file ...]\n");
 	exits("usage");
 }
 
+void
+printamt(vlong amt, char *name)
+{
+	if (readflg)
+		return;
+	if (autoscale) {
+		int scale = 0;
+		double val = (double)amt/unit;
+
+		while (fabs(val) >= 1024 && scale < nelem(pfxes)-1) {
+			scale++;
+			val /= 1024;
+		}
+		print("%.6g%s\t%q\n", val, pfxes[scale], name);
+	} else if (fltflag)
+		print("%.6g\t%q\n", (double)amt/unit, name);
+	else
+		print(fmt, HOWMANY(amt, unit), name);
+}
+
 void
 main(int argc, char *argv[])
 {
-	int i;
-	char *s, *ss;
+	int i, scale;
+	char *s, *ss, *name;
 
 	doquote = needsrcquote;
 	quotefmtinstall();
@@ -38,47 +82,135 @@ main(int argc, char *argv[])
 	case 'a':	/* all files */
 		aflag = 1;
 		break;
-	case 's':	/* only top level */
-		sflag = 1;
+	case 'b':	/* block size */
+		s = ARGF();
+		if(s) {
+			blocksize = strtoul(s, &ss, 0);
+			if(s == ss)
+				blocksize = 1;
+			while(*ss == 'k')
+				blocksize *= 1024;
+		}
 		break;
-	case 'f':	/* ignore errors */
+	case 'e':	/* print in %g notation */
+		fltflag = 1;
+		break;
+	case 'f':	/* don't print warnings */
 		fflag = 1;
 		break;
+	case 'h':	/* similar to -h in bsd but more precise */
+		autoscale = 1;
+		break;
 	case 'n':	/* all files, number of bytes */
 		aflag = 1;
-		nflag = 1;
+		blocksize = 1;
+		unit = 1;
 		break;
-	case 't':	/* return modified/accessed time */
-		tflag = 1;
-		break;
-	case 'u':	/* accessed time */
-		uflag = 1;
+	case 'p':
+		s = ARGF();
+		if(s) {
+			for (scale = 0; pfxes[scale] != nil; scale++)
+				if (cistrcmp(s, pfxes[scale]) == 0)
+					break;
+			if (pfxes[scale] == nil)
+				sysfatal("unknown suffix %s", s);
+			unit = 1;
+			while (scale-- > 0)
+				unit *= Vkilo;
+		}
 		break;
 	case 'q':	/* qid */
 		fmt = "%.16llux\t%q\n";
 		qflag = 1;
 		break;
-	case 'b':	/* block size */
-		s = ARGF();
-		if(s) {
-			blocksize = strtoul(s, &ss, 0);
-			if(s == ss)
-				blocksize = 1;
-			if(*ss == 'k')
-				blocksize *= 1024;
-		}
+	case 'r':
+		/* undocumented: just read & ignore every block of every file */
+		readflg = 1;
+		break;
+	case 's':	/* only top level */
+		sflag = 1;
+		break;
+	case 't':	/* return modified/accessed time */
+		tflag = 1;
+		break;
+	case 'u':	/* accessed time */
+		uflag = 1;
 		break;
 	default:
 		usage();
 	} ARGEND
+
+	if (unit == 0)
+		if (qflag || tflag || uflag || autoscale)
+			unit = 1;
+		else
+			unit = Vkilo;
+	if (blocksize < 1)
+		blocksize = 1;
+
+	if (readflg) {
+		readbuf = malloc(blocksize);
+		if (readbuf == nil)
+			sysfatal("out of memory");
+	}
 	if(argc==0)
-		print(fmt, du(".", dirstat(".")), ".");
+		printamt(du(".", dirstat(".")), ".");
 	else
-		for(i=0; i<argc; i++)
-			print(fmt, du(argv[i], dirstat(argv[i])), argv[i]);
+		for(i=0; i<argc; i++) {
+			name = argv[i];
+			printamt(du(name, dirstat(name)), name);
+		}
 	exits(0);
 }
 
+vlong
+dirval(Dir *d, vlong size)
+{
+	if(qflag)
+		return d->qid.path;
+	else if(tflag) {
+		if(uflag)
+			return d->atime;
+		return d->mtime;
+	} else
+		return size;
+}
+
+void
+readfile(char *name)
+{
+	int n, fd = open(name, OREAD);
+
+	if(fd < 0) {
+		warn(name);
+		return;
+	}
+	while ((n = read(fd, readbuf, blocksize)) > 0)
+		continue;
+	if (n < 0)
+		warn(name);
+	close(fd);
+}
+
+vlong
+dufile(char *name, Dir *d)
+{
+	vlong t = blkmultiple(d->length);
+
+	if(aflag || readflg) {
+		String *file = s_copy(name);
+
+		s_append(file, "/");
+		s_append(file, d->name);
+		if (readflg)
+			readfile(s_to_c(file));
+		t = dirval(d, t);
+		printamt(t, s_to_c(file));
+		s_free(file);
+	}
+	return t;
+}
+
 vlong
 du(char *name, Dir *dir)
 {
@@ -90,80 +222,55 @@ du(char *name, Dir *dir)
 	if(dir == nil)
 		return warn(name);
 
+	if((dir->qid.type&QTDIR) == 0)
+		return dirval(dir, blkmultiple(dir->length));
+
 	fd = open(name, OREAD);
 	if(fd < 0)
 		return warn(name);
-
-	if((dir->qid.type&QTDIR) == 0)
-		nk = k(dir->length);
-	else{
-		nk = 0;
-		while((n=dirread(fd, &buf)) > 0) {
-			d = buf;
-			for(i=0; i<n; i++, d++) {
-				if((d->qid.type&QTDIR) == 0) {
-					t = k(d->length);
-					nk += t;
-					if(aflag) {
-						file = s_copy(name);
-						s_append(file, "/");
-						s_append(file, d->name);
-						if(tflag) {
-							t = d->mtime;
-							if(uflag)
-								t = d->atime;
-						}
-						if(qflag)
-							t = d->qid.path;
-						print(fmt, t, s_to_c(file));
-						s_free(file);
-					}
-					continue;
-				}
-				if(strcmp(d->name, ".") == 0 ||
-				   strcmp(d->name, "..") == 0 ||
-				   seen(d))
-					continue;
-				file = s_copy(name);
-				s_append(file, "/");
-				s_append(file, d->name);
-				t = du(s_to_c(file), d);
-				nk += t;
-				if(tflag) {
-					t = d->mtime;
-					if(uflag)
-						t = d->atime;
-				}
-				if(qflag)
-					t = d->qid.path;
-				if(!sflag)
-					print(fmt, t, s_to_c(file));
-				s_free(file);
+	nk = 0;
+	while((n=dirread(fd, &buf)) > 0) {
+		d = buf;
+		for(i = n; i > 0; i--, d++) {
+			if((d->qid.type&QTDIR) == 0) {
+				nk += dufile(name, d);
+				continue;
 			}
-			free(buf);
+
+			if(strcmp(d->name, ".") == 0 ||
+			   strcmp(d->name, "..") == 0 ||
+			   /* !readflg && */ seen(d))
+				continue;	/* don't get stuck */
+
+			file = s_copy(name);
+			s_append(file, "/");
+			s_append(file, d->name);
+
+			t = du(s_to_c(file), d);
+
+			nk += t;
+			t = dirval(d, t);
+			if(!sflag)
+				printamt(t, s_to_c(file));
+			s_free(file);
 		}
-		if(n < 0)
-			warn(name);
+		free(buf);
 	}
+	if(n < 0)
+		warn(name);
 	close(fd);
-	if(tflag) {
-		if(uflag)
-			return dir->atime;
-		return dir->mtime;
-	}
-	if(qflag)
-		return dir->qid.path;
-	return nk;
+	return dirval(dir, nk);
 }
 
-#define	NCACHE	128	/* must be power of two */
-typedef	struct	Cache	Cache;
-struct	Cache
+#define	NCACHE	256	/* must be power of two */
+
+typedef struct
 {
 	Dir*	cache;
 	int	n;
 	int	max;
-} cache[NCACHE];
+} Cache;
+Cache cache[NCACHE];
 
 int
 seen(Dir *dir)
@@ -180,8 +287,12 @@ seen(Dir *dir)
 		   dir->dev == dp->dev)
 			return 1;
 	if(c->n == c->max){
-		c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir));
-		if(cache == 0)
+		if (c->max == 0)
+			c->max = 8;
+		else
+			c->max += c->max/2;
+		c->cache = realloc(c->cache, c->max*sizeof(Dir));
+		if(c->cache == nil)
 			err("malloc failure");
 	}
 	c->cache[c->n++] = *dir;
@@ -203,11 +314,11 @@ warn(char *s)
 	return 0;
 }
 
+/* round up n to nearest block */
 vlong
-k(vlong n)
+blkmultiple(vlong n)
 {
-	if(nflag)
+	if(blocksize == 1)		/* no quantization */
 		return n;
-	n = (n+blocksize-1)/blocksize;
-	return n*blocksize/1024LL;
+	return ROUNDUP(n, blocksize);
 }

+ 617 - 0
sys/src/cmd/ecp.c

@@ -0,0 +1,617 @@
+/*
+ * ecp - copy a file fast (in big blocks), cope with errors, optionally verify.
+ *
+ * Transfers a block at a time.  On error, retries one sector at a time,
+ * and reports all errors on the retry.
+ * Unlike dd, ecp ignores EOF, since it is sometimes reported on error.
+ * Also unlike `dd conv=noerror,sync', ecp doesn't get stuck nor give up.
+ *
+ * Written by Geoff Collyer, originally to run on RSX-11M(!) in 1979.
+ * Later simplified for UNIX and ultimately Plan 9.
+ */
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+/* fundamental constants */
+enum {
+	No = 0,
+	Yes,
+
+	Noseek = 0,		/* need not seek, may seek on seekable files */
+	Mustseek,
+
+	Enone = 0,
+	Eio,
+};
+
+/* tunable parameters */
+enum {
+	Defsectsz = 512,	/* default sector size */
+	/* 10K is a good size for HP WORM drives */
+	Defblksz = 16*1024,	/* default block (big-transfer) size */
+	Mingoodblks = 3,	/* after this many, go back to fast mode */
+};
+
+#define TTY "/dev/cons"			/* plan 9 */
+
+#define badsect(errno) ((errno) != Enone)  /* was last transfer in error? */
+
+/* disk address (in bytes or sectors), also type of 2nd arg. to seek */
+typedef uvlong Daddr;
+typedef vlong Sdaddr;				/* signed disk address */
+typedef long Rdwrfn(int, void *, long);		/* plan 9 read or write */
+
+typedef struct {
+	char	*name;
+	int	fd;
+	Daddr	startsect;
+	int	fast;
+	int	seekable;
+
+	ulong	maxconerrs;		/* maximum consecutive errors */
+	ulong	conerrs;		/* current consecutive errors */
+	Daddr	congoodblks;
+
+	Daddr	harderrs;
+	Daddr	lasterr;		/* sector #s */
+	Daddr	lastgood;
+} File;
+
+/* exports */
+char *argv0;
+
+/* privates */
+static int reblock = No, progress = No, swizzle = No;
+static int reverse = No;
+static ulong sectsz = Defsectsz;
+static ulong blocksize = Defblksz;
+
+static char *buf, *vfybuf;
+static int blksects;
+
+/*
+ * warning - print best error message possible and clear errno
+ */
+void
+warning(char *s1, char *s2)
+{
+	char err[100], msg[256];
+	char *np, *ep = msg + sizeof msg - 1;
+
+	errstr(err, sizeof err);		/* save error string */
+	np = seprint(msg, ep, "%s: ", argv0);
+	np = seprint(np, ep, s1, s2);
+	errstr(err, sizeof err);		/* restore error string */
+	seprint(np, ep, ": %r\n");
+
+	fprint(2, "%s", msg);
+}
+
+int
+eopen(char *file, int mode)
+{
+	int fd = open(file, mode);
+
+	if (fd < 0)
+		sysfatal("can't open %s: %r", file);
+	return fd;
+}
+
+static int					/* boolean */
+confirm(File *src, File *dest)
+{
+	int absent, n, tty = eopen(TTY, 2);
+	char c, junk;
+	Dir *stp;
+
+	if ((stp = dirstat(src->name)) == nil)
+		sysfatal("no input file %s: %r", src->name);
+	free(stp);
+	stp = dirstat(dest->name);
+	absent = (stp == nil);
+	free(stp);
+	fprint(2, "%s: copy %s to %s%s? ", argv0, src->name, dest->name,
+		(absent? " (missing)": ""));
+	n = read(tty, &c, 1);
+	junk = c;
+	if (n < 1)
+		c = 'n';
+	while (n > 0 && junk != '\n')
+		n = read(tty, &junk, 1);
+	close(tty);
+	if (isascii(c) && isupper(c))
+		c = tolower(c);
+	return c == 'y';
+}
+
+static char *
+sectid(File *fp, Daddr sect)
+{
+	static char sectname[256];
+
+	if (fp->startsect == 0)
+		snprint(sectname, sizeof sectname, "%s sector %llud",
+			fp->name, sect);
+	else
+		snprint(sectname, sizeof sectname,
+			"%s sector %llud (relative %llud)",
+			fp->name, sect + fp->startsect, sect);
+	return sectname;
+}
+
+static void
+io_expl(File *fp, char *rw, Daddr sect)		/* explain an i/o error */
+{
+	/* print only first 2 bad sectors in a range, if going forward */
+	if (reverse || fp->conerrs == 0) {
+		char msg[128];
+
+		snprint(msg, sizeof msg, "%s %s", rw, sectid(fp, sect));
+		warning("%s", msg);
+	} else if (fp->conerrs == 1)
+		fprint(2, "%s: ...\n", argv0);
+}
+
+static void
+repos(File *fp, Daddr sect)
+{
+	if (!fp->seekable)
+		sysfatal("%s: trying to seek on unseekable file", fp->name);
+	if (seek(fp->fd, (sect+fp->startsect)*sectsz, 0) == -1)
+		sysfatal("can't seek on %s: %r", fp->name);
+}
+
+static void
+rewind(File *fp)
+{
+	repos(fp, 0);
+}
+
+/*
+ * transfer (many) sectors.  reblock input as needed.
+ * returns Enone if no failures, others on failure with errstr set.
+ */
+static int
+bio(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects, int mustseek)
+{
+	int xfered;
+	ulong toread, bytes = sects * sectsz;
+	static int reblocked = 0;
+
+	if (mustseek) {
+		if (!fp->seekable)
+			sysfatal("%s: need to seek on unseekable file",
+				fp->name);
+		repos(fp, stsect);
+	}
+	if ((long)blocksize != blocksize || (long)bytes != bytes)
+		sysfatal("i/o count too big: %lud", bytes);
+
+	werrstr("");
+	xfered = (*rdwr)(fp->fd, buff, bytes);
+	if (xfered == bytes)
+		return Enone;			/* did as we asked */
+	if (xfered < 0)
+		return Eio;			/* out-and-out i/o error */
+	/*
+	 * Kernel transferred less than asked.  Shouldn't happen;
+	 * probably indicates disk driver error or trying to
+	 * transfer past the end of a disk partition.  Treat as an
+	 * I/O error that reads zeros past the point of error,
+	 * unless reblocking input and this is a read.
+	 */
+	if (rdwr == write)
+		return Eio;
+	if (!reblock) {
+		memset(buff+xfered, '\0', bytes-xfered);
+		return Eio;			/* short read */
+	}
+
+	/* for pipes that return less than asked */
+	if (progress && !reblocked) {
+		fprint(2, "%s: reblocking input\n", argv0);
+		reblocked++;
+	}
+	for (toread = bytes - xfered; toread != 0; toread -= xfered) {
+		xfered = (*rdwr)(fp->fd, buff+bytes-toread, toread);
+		if (xfered <= 0)
+			break;
+	}
+	if (xfered < 0)
+		return Eio;			/* out-and-out i/o error */
+	if (toread != 0)			/* early EOF? */
+		memset(buff+bytes-toread, '\0', toread);
+	return Enone;
+}
+
+/* called only after a single-sector transfer */
+static int
+toomanyerrs(File *fp, Daddr sect)
+{
+	if (sect == fp->lasterr+1)
+		fp->conerrs++;
+	else
+		fp->conerrs = 0;
+	fp->lasterr = sect;
+	return fp->maxconerrs != 0 && fp->conerrs >= fp->maxconerrs &&
+		fp->lastgood == -1;
+}
+
+static void
+ckendrange(File *fp)
+{
+	if (!reverse && fp->conerrs > 0)
+		fprint(2, "%s: %lld: ... last bad sector in range\n",
+			argv0, fp->lasterr);
+}
+
+static int
+transfer(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects,
+	int mustseek)
+{
+	int res = bio(fp, rdwr, buff, stsect, sects, mustseek);
+
+	if (badsect(res)) {
+		fp->fast = 0;		/* read single sectors for a while */
+		fp->congoodblks = 0;
+	} else
+		fp->lastgood = stsect + sects - 1;
+	return res;
+}
+
+/*
+ * Read or write many sectors at once.
+ * If it fails, retry the individual sectors and report errors.
+ */
+static void
+bigxfer(File *fp, Rdwrfn *rdwr, char *buff, Daddr stsect, int sects,
+	int mustseek)
+{
+	int i, badsects = 0, wasfast = fp->fast;
+	char *rw = (rdwr == read? "read": "write");
+
+	if (fp->fast) {
+		if (!badsect(transfer(fp, rdwr, buff, stsect, sects, mustseek)))
+			return;
+		if (progress)
+			fprint(2, "%s: breaking up big transfer on %s error "
+				"`%r' on %s\n", argv0, rw, sectid(fp, stsect));
+	}
+
+	for (i = 0; i < sects; i++)
+		if (badsect(transfer(fp, rdwr, buff+i*sectsz, stsect+i, 1,
+		    Mustseek))) {
+			io_expl(fp, rw, stsect+i);
+			badsects++;
+			fp->harderrs++;
+			if (toomanyerrs(fp, stsect+i))
+				sysfatal("more than %lud consecutive I/O errors",
+					fp->maxconerrs);
+		} else {
+			ckendrange(fp);
+			fp->conerrs = 0;
+		}
+	if (badsects == 0) {
+		ckendrange(fp);
+		fp->conerrs = 0;
+		if (wasfast)
+			fprint(2, "%s: %s error on big transfer at %s but none "
+				"on retries!\n", argv0, rw, sectid(fp, stsect));
+		++fp->congoodblks;
+		if (fp->congoodblks >= Mingoodblks) {
+			fprint(2, "%s: %s: back to big transfers\n", argv0,
+				fp->name);
+			fp->fast = 1;
+		}
+	} else
+		/*
+		 * the last sector could have been in error, so the seek pointer
+		 * may need to be corrected.
+		 */
+		repos(fp, stsect + sects);
+}
+
+static void
+vrfyfailed(File *src, File *dest, Daddr stsect)
+{
+	char *srcsect = strdup(sectid(src, stsect));
+
+	fprint(2, "%s: verify failed at %s (%s)\n", argv0, srcsect,
+		sectid(dest, stsect));
+	free(srcsect);
+}
+
+/*
+ * I've seen SCSI read errors that the kernel printed but then didn't
+ * report to the program doing the read, so if a big verify fails,
+ * break it up and verify each sector separately to isolate the bad sector(s).
+ */
+int						/* error count */
+verify(File *src, File *dest, char *buff, char *buft, Daddr stsect,
+	int sectors)
+{
+	int i, errors = 0;
+
+	for (i = 0; i < sectors; i++)
+		if (memcmp(buff + i*sectsz, buft + i*sectsz, sectsz) != 0)
+			errors++;
+	if (errors == 0)
+		return errors;			/* normal case */
+
+	if (sectors == 1) {
+		vrfyfailed(src, dest, stsect);
+		return errors;
+	}
+
+	/* re-read and verify each sector individually */
+	errors = 0;
+	for (i = 0; i < sectors; i++) {
+		int thissect = stsect + i;
+
+		if (badsect(bio(src,  read, buff, thissect, 1, Mustseek)))
+			io_expl(src,  "read",  thissect);
+		if (badsect(bio(dest, read, buft, thissect, 1, Mustseek)))
+			io_expl(dest, "write", thissect);
+		if (memcmp(buff, buft, sectsz) != 0) {
+			vrfyfailed(src, dest, thissect);
+			++errors;
+		}
+	}
+	if (errors == 0) {
+		char *srcsect = strdup(sectid(src, stsect));
+
+		fprint(2, "%s: verification failed on big read at %s (%s) "
+			"but not on retries!\n", argv0, srcsect,
+			sectid(dest, stsect));
+		free(srcsect);
+	}
+	/*
+	 * the last sector of each could have been in error, so the seek
+	 * pointers may need to be corrected.
+	 */
+	repos(src,  stsect + sectors);
+	repos(dest, stsect + sectors);
+	return errors;
+}
+
+/*
+ * start is starting sector of proposed transfer;
+ * nsects is the total number of sectors being copied;
+ * maxxfr is the block size in sectors.
+ */
+int
+sectsleft(Daddr start, Daddr nsects, int maxxfr)
+{
+	/* nsects-start is sectors to the end */
+	if (start + maxxfr <= nsects - 1)
+		return maxxfr;
+	else
+		return nsects - start;
+}
+
+enum {
+	Rotbits = 3,
+};
+
+void
+swizzlebits(char *buff, int sects)
+{
+	uchar *bp, *endbp;
+
+	endbp = (uchar *)(buff+sects*sectsz);
+	for (bp = (uchar *)buff; bp < endbp; bp++)
+		*bp = ~(*bp>>Rotbits | *bp<<(8-Rotbits));
+}
+
+/*
+ * copy at most blksects sectors, with error retries.
+ * stsect is relative to the start of the copy; 0 is the first sector.
+ * to get actual sector numbers, add e.g. dest->startsect.
+ */
+static int
+copysects(File *src, File *dest, Daddr stsect, Daddr nsects, int mustseek)
+{
+	int xfrsects = sectsleft(stsect, nsects, blksects);
+
+	if (xfrsects > blksects) {
+		fprint(2, "%s: block size of %d is too big.\n", argv0, xfrsects);
+		exits("block size too big");
+	}
+	bigxfer(src,  read,  buf, stsect, xfrsects, mustseek);
+	if (swizzle)
+		swizzlebits(buf, xfrsects);
+	bigxfer(dest, write, buf, stsect, xfrsects, mustseek);
+	/* give a few reassurances at the start, then every 10MB */
+	if (progress &&
+	    (stsect < blksects*10 || stsect%(10*1024*1024/sectsz) == 0))
+		fprint(2, "%s: copied%s to relative sector %llud\n", argv0,
+			(swizzle? " swizzled": ""), stsect + xfrsects - 1);
+	return 0;
+}
+
+/*
+ * verify at most blksects sectors, with error retries.
+ * return error count.
+ */
+static int
+vrfysects(File *src, File *dest, Daddr stsect, Daddr nsects, int mustseek)
+{
+	int xfrsects = sectsleft(stsect, nsects, blksects);
+
+	if (xfrsects > blksects) {
+		fprint(2, "%s: block size of %d is too big.\n", argv0, xfrsects);
+		exits("block size too big");
+	}
+	bigxfer(src,  read, buf,    stsect, xfrsects, mustseek);
+	bigxfer(dest, read, vfybuf, stsect, xfrsects, mustseek);
+	return verify(src, dest, buf, vfybuf, stsect, xfrsects);
+}
+
+static void
+setupfile(File *fp, int mode)
+{
+	fp->fd = open(fp->name, mode);
+	if (fp->fd < 0)
+		sysfatal("can't open %s: %r", fp->name);
+	fp->seekable = (seek(fp->fd, 0, 1) >= 0);
+	if (fp->startsect != 0)
+		rewind(fp);
+}
+
+static Daddr
+copyfile(File *src, File *dest, Daddr nsects, int plsverify)
+{
+	Sdaddr stsect, vererrs = 0;
+	Dir *stp;
+
+	setupfile(src, OREAD);
+	if ((stp = dirstat(dest->name)) == nil) {
+		int fd = create(dest->name, ORDWR, 0666);
+
+		if (fd >= 0)
+			close(fd);
+	}
+	free(stp);
+	setupfile(dest, ORDWR);
+
+	if (progress)
+		fprint(2, "%s: copying first sectors\n", argv0);
+	if (reverse)
+		for (stsect = (nsects/blksects)*blksects; stsect >= 0;
+		     stsect -= blksects)
+			vererrs += copysects(src, dest, stsect, nsects, Mustseek);
+	else {
+		for (stsect = 0; stsect < nsects; stsect += blksects)
+			vererrs += copysects(src, dest, stsect, nsects, Noseek);
+		ckendrange(src);
+		ckendrange(dest);
+	}
+
+	/*
+	 * verification is done as a separate pass rather than immediately after
+	 * writing, in part to defeat caching in clever disk controllers.
+	 * we really want to see the bits that hit the disk.
+	 */
+	if (plsverify) {
+		fprint(2, "%s: copy done; verifying...\n", argv0);
+		rewind(src);
+		rewind(dest);
+		for (stsect = 0; stsect < nsects; stsect += blksects) /* forward */
+			vererrs += vrfysects(src, dest, stsect, nsects, Noseek);
+		if (vererrs <= 0)
+			fprint(2, "%s: no", argv0);
+		else
+			fprint(2, "%s: %llud", argv0, vererrs);
+		fprint(2, " error%s during verification\n",
+			(vererrs != 1? "s": ""));
+	}
+	close(src->fd);
+	close(dest->fd);
+	return vererrs;
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-bcprvZ][-B blocksz][-e errs][-s sectsz]"
+		"[-i issect][-o ossect] sectors from to\n", argv0);
+	exits("usage");
+}
+
+void
+initfile(File *fp)
+{
+	memset(fp, 0, sizeof *fp);
+	fp->fast = 1;
+	fp->lasterr = -1;
+	fp->lastgood = -1;
+}
+
+void
+main(int argc, char **argv)
+{
+	int errflg = 0, plsconfirm = No, plsverify = No;
+	long lval;
+	File src, dest;
+	Sdaddr sect;
+
+	initfile(&src);
+	initfile(&dest);
+	ARGBEGIN {
+	case 'b':
+		reblock = Yes;
+		break;
+	case 'B':
+		lval = atol(EARGF(usage()));
+		if (lval < 0)
+			usage();
+		blocksize = lval;
+		break;
+	case 'c':
+		plsconfirm = Yes;
+		break;
+	case 'e':
+		lval = atol(EARGF(usage()));
+		if (lval < 0)
+			usage();
+		src.maxconerrs = lval;
+		dest.maxconerrs = lval;
+		break;
+	case 'i':
+		sect = atoll(EARGF(usage()));
+		if (sect < 0)
+			usage();
+		src.startsect = sect;
+		break;
+	case 'o':
+		sect = atoll(EARGF(usage()));
+		if (sect < 0)
+			usage();
+		dest.startsect = sect;
+		break;
+	case 'p':
+		progress = Yes;
+		break;
+	case 'r':
+		reverse = Yes;
+		break;
+	case 's':
+		sectsz = atol(EARGF(usage()));
+		if (sectsz <= 0 || sectsz % 512 != 0)
+			usage();
+		break;
+	case 'v':
+		plsverify = Yes;
+		break;
+	case 'Z':
+		swizzle = Yes;
+		break;
+	default:
+		errflg++;
+		break;
+	} ARGEND
+	if (errflg || argc != 3)
+		usage();
+	if (blocksize <= 0 || blocksize % sectsz != 0)
+		sysfatal("block size not a multiple of sector size");
+
+	if (!isascii(argv[0][0]) || !isdigit(argv[0][0])) {
+		fprint(2, "%s: %s is not numeric\n", argv0, argv[0]);
+		exits("non-numeric sector count");
+	}
+	src.name =  argv[1];
+	dest.name = argv[2];
+
+	blksects = blocksize / sectsz;
+	if (blksects < 1)
+		blksects = 1;
+	buf = malloc(blocksize);
+	vfybuf = malloc(blocksize);
+	if (buf == nil || vfybuf == nil)
+		sysfatal("out of memory: %r");
+
+	if (plsconfirm? confirm(&src, &dest): Yes)
+		copyfile(&src, &dest, atoll(argv[0]), plsverify);
+	exits(src.harderrs || dest.harderrs? "hard errors": 0);
+}

+ 44 - 10
sys/src/cmd/import.c

@@ -11,9 +11,9 @@ enum {
 
 static char *encprotos[] = {
 	[Encnone] =	"clear",
-	[Encssl] =		"ssl",
-	[Enctls] = 		"tls",
-				nil,
+	[Encssl] =	"ssl",
+	[Enctls] = 	"tls",
+			nil,
 };
 
 char		*keyspec = "";
@@ -36,6 +36,31 @@ int	filter(int, char *, char *);
 
 static void	mksecret(char *, uchar *);
 
+/*
+ * based on libthread's threadsetname, but drags in less library code.
+ * actually just sets the arguments displayed.
+ */
+void
+procsetname(char *fmt, ...)
+{
+	int fd;
+	char *cmdname;
+	char buf[128];
+	va_list arg;
+
+	va_start(arg, fmt);
+	cmdname = vsmprint(fmt, arg);
+	va_end(arg);
+	if (cmdname == nil)
+		return;
+	snprint(buf, sizeof buf, "#p/%d/args", getpid());
+	if((fd = open(buf, OWRITE)) >= 0){
+		write(fd, cmdname, strlen(cmdname)+1);
+		close(fd);
+	}
+	free(cmdname);
+}
+
 void
 post(char *name, char *envname, int srvfd)
 {
@@ -66,12 +91,10 @@ lookup(char *s, char *l[])
 void
 main(int argc, char **argv)
 {
-	char *mntpt;
-	int fd, mntflags;
-	int oldserver;
-	char *srvpost, srvfile[64];
-	int backwards = 0;
+	char *mntpt, *srvpost, srvfile[64];
+	int backwards = 0, fd, mntflags, oldserver;
 
+	quotefmtinstall();
 	srvpost = nil;
 	oldserver = 0;
 	mntflags = MREPL;
@@ -160,7 +183,8 @@ main(int argc, char **argv)
 		fd = connect(argv[0], argv[1], oldserver);
 
 	if (!oldserver)
-		fprint(fd, "impo %s %s\n", filterp? "aan": "nofilter", encprotos[encproto]);
+		fprint(fd, "impo %s %s\n", filterp? "aan": "nofilter",
+			encprotos[encproto]);
 
 	if (encproto != Encnone && ealgs && ai) {
 		uchar key[16];
@@ -189,6 +213,7 @@ main(int argc, char **argv)
 			fd = filter(fd, filterp, argv[0]);
 
 		/* set up encryption */
+		procsetname("pushssl");
 		fd = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
 		if(fd < 0)
 			sysfatal("can't establish ssl connection: %r");
@@ -201,6 +226,7 @@ main(int argc, char **argv)
 		remove(srvfile);
 		post(srvfile, srvpost, fd);
 	}
+	procsetname("mount on %s", mntpt);
 	if(mount(fd, -1, mntpt, mntflags, "") < 0)
 		sysfatal("can't mount %s: %r", argv[1]);
 	alarm(0);
@@ -226,6 +252,7 @@ old9p(int fd)
 {
 	int p[2];
 
+	procsetname("old9p");
 	if(pipe(p) < 0)
 		sysfatal("pipe: %r");
 
@@ -267,6 +294,7 @@ connect(char *system, char *tree, int oldserver)
 	char *authp;
 
 	na = netmkaddr(system, 0, "exportfs");
+	procsetname("dial %s", na);
 	if((fd = dial(na, 0, dir, 0)) < 0)
 		sysfatal("can't dial %s: %r", system);
 
@@ -276,18 +304,22 @@ connect(char *system, char *tree, int oldserver)
 		else
 			authp = "p9any";
 
+		procsetname("auth_proxy auth_getkey proto=%q role=client %s",
+			authp, keyspec);
 		ai = auth_proxy(fd, auth_getkey, "proto=%q role=client %s",
 			authp, keyspec);
 		if(ai == nil)
 			sysfatal("%r: %s", system);
 	}
 
+	procsetname("writing tree name %s", tree);
 	n = write(fd, tree, strlen(tree));
 	if(n < 0)
 		sysfatal("can't write tree: %r");
 
 	strcpy(buf, "can't read tree");
 
+	procsetname("awaiting OK for %s", tree);
 	n = read(fd, buf, sizeof buf - 1);
 	if(n!=2 || buf[0]!='O' || buf[1]!='K'){
 		if (timedout)
@@ -310,6 +342,7 @@ passive(void)
 	 * Ignore doauth==0 on purpose.  Is it useful here?
 	 */
 
+	procsetname("auth_proxy auth_getkey proto=p9any role=server");
 	ai = auth_proxy(0, auth_getkey, "proto=p9any role=server");
 	if(ai == nil)
 		sysfatal("auth_proxy: %r");
@@ -329,7 +362,8 @@ passive(void)
 void
 usage(void)
 {
-	fprint(2, "usage: import [-abcC] [-A] [-E clear|ssl|tls] [-e 'crypt auth'|clear] [-k keypattern] [-p] host remotefs [mountpoint]\n");
+	fprint(2, "usage: import [-abcC] [-A] [-E clear|ssl|tls] "
+"[-e 'crypt auth'|clear] [-k keypattern] [-p] host remotefs [mountpoint]\n");
 	exits("usage");
 }
 

+ 327 - 0
sys/src/cmd/ip/6in4.c

@@ -0,0 +1,327 @@
+/*
+ * 6in4 - tunnel client for automatic 6to4 or configured v6-in-v4 tunnels
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+enum {
+	IP_IPV6PROTO	= 41,
+};
+
+int anysender;
+int gateway;
+
+uchar local6[IPaddrlen];
+uchar remote6[IPaddrlen];
+uchar remote4[IPaddrlen];
+uchar localmask[IPaddrlen];
+uchar localnet[IPaddrlen];
+uchar myip[IPaddrlen];
+
+/* magic anycast address from rfc3068 */
+uchar anycast6to4[IPv4addrlen] = { 192, 88, 99, 1 };
+
+static char *net = "/net";
+
+static int	badipv4(uchar*);
+static int	badipv6(uchar*);
+static void	ip2tunnel(int, int);
+static void	tunnel2ip(int, int);
+
+static void
+usage(void)
+{
+	fprint(2, "Usage: %s [-ag] [-x mtpt] local6[/mask] [remote4 [remote6]]\n",
+		argv0);
+	exits("Usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int n, tunnel, ifc, cfd;
+	char *p, *cl, *ir;
+	char buf[128], path[64];
+
+	fmtinstall('I', eipfmt);
+	fmtinstall('V', eipfmt);
+	fmtinstall('M', eipfmt);
+
+	ARGBEGIN {
+	case 'a':
+		anysender++;
+		break;
+	case 'g':
+		gateway++;
+		break;
+	case 'x':
+		net = EARGF(usage());
+		break;
+	default:
+		usage();
+	} ARGEND
+	if (argc < 1)
+		usage();
+
+	/* local v6 address (mask defaults to /128) */
+	memcpy(localmask, IPallbits, sizeof localmask);
+	if ((p = strchr(argv[0], '/')) != nil) {
+		parseipmask(localmask, p);
+		*p = 0;
+	}
+	parseip(local6, argv[0]);
+	if (isv4(local6))
+		usage();
+	argv++;
+	argc--;
+	if (argc >= 1 && argv[0][0] == '/') {
+		parseipmask(localmask, argv[0]);
+		argv++;
+		argc--;
+	}
+
+	/* remote v4 address (defaults to anycast 6to4) */
+	if (argc >= 1) {
+		parseip(remote4, argv[0]);
+		if (!isv4(remote4))
+			usage();
+		argv++;
+		argc--;
+	} else {
+		v4tov6(remote4, anycast6to4);
+		anysender++;
+	}
+
+	/* remote v6 address (defaults to link-local w/ v4 as interface part) */
+	if (argc >= 1) {
+		parseip(remote6, argv[0]);
+		if (isv4(remote4))
+			usage();
+		argv++;
+		argc--;
+	} else {
+		remote6[0] = 0xFE;		/* link local */
+		remote6[1] = 0x80;
+		memcpy(remote6 + IPv4off, remote4 + IPv4off, IPv4addrlen);
+	}
+	USED(argv);
+	if (argc != 0)
+		usage();
+
+	maskip(local6, localmask, localnet);
+	if (myipaddr(myip, net) < 0)
+		sysfatal("can't find my ipv4 address on %s", net);
+
+	/*
+	 * open IPv6-in-IPv4 tunnel
+	 */
+	p = seprint(buf, buf + sizeof buf, "ipmux!proto=%2.2x;dst=%V",
+		IP_IPV6PROTO, myip + IPv4off);
+	if (!anysender)
+		seprint(p, buf + sizeof buf, ";src=%V", remote4 + IPv4off);
+	tunnel = dial(buf, 0, 0, 0);
+	if (tunnel < 0)
+		sysfatal("can't make 6in4 tunnel with dial str %s: %r", buf);
+
+	/*
+	 * open local IPv6 interface (as a packet interface)
+	 */
+	cl = smprint("%s/ipifc/clone", net);
+	cfd = open(cl, ORDWR);			/* allocate a conversation */
+	free(cl);
+	n = 0;
+	if (cfd < 0 || (n = read(cfd, buf, sizeof buf - 1)) <= 0)
+		sysfatal("can't make packet interface: %r");
+	buf[n] = 0;
+
+	snprint(path, sizeof path, "%s/ipifc/%s/data", net, buf);
+	ifc = open(path, ORDWR);
+	if (ifc < 0 || fprint(cfd, "bind pkt") < 0)
+		sysfatal("can't bind packet interface: %r");
+	/* 1280 is MTU, apparently from rfc2460 */
+	if (fprint(cfd, "add %I /128 %I 1280", local6, remote6) <= 0)
+		sysfatal("can't set local ipv6 address: %r");
+	close(cfd);
+
+	if (gateway) {
+		/* route global addresses through the tunnel to remote6 */
+		ir = smprint("%s/iproute", net);
+		cfd = open(ir, OWRITE);
+		free(ir);
+		if (cfd < 0 || fprint(cfd, "add 2000:: /3 %I", remote6) <= 0)
+			sysfatal("can't set default global route: %r");
+	}
+
+	switch (rfork(RFPROC|RFNOWAIT|RFMEM)) {
+	case -1:
+		sysfatal("rfork");
+	case 0:
+		ip2tunnel(ifc, tunnel);
+		break;
+	default:
+		tunnel2ip(tunnel, ifc);
+		break;
+	}
+	exits("tunnel gone");
+}
+
+typedef struct Iphdr Iphdr;
+typedef struct Ip6hdr Ip6hdr;
+
+struct Iphdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source (uchar ordering unimportant) */
+	uchar	dst[4];		/* Ip destination (uchar ordering unimportant) */
+};
+
+struct	Ip6hdr {
+	uchar	vcf[4];		/* version:4, traffic class:8, flow label:20 */
+	uchar	ploadlen[2];	/* payload length: packet length - 40 */
+	uchar	proto;		/* next header type */
+	uchar	ttl;		/* hop limit */
+	uchar	src[IPaddrlen];
+	uchar	dst[IPaddrlen];
+};
+
+#define STFHDR (sizeof(Iphdr))
+
+static void
+ip2tunnel(int in, int out)
+{
+	int n, m;
+	char buf[64*1024];
+	Iphdr *op;
+	Ip6hdr *ip;
+
+	op = (Iphdr*)buf;
+	op->vihl = 0x45;		/* v4, hdr is 5 longs? */
+	memcpy(op->src, myip + IPv4off, sizeof op->src);
+	op->proto = IP_IPV6PROTO;
+	op->ttl = 100;
+
+	/* get a V6 packet destined for the tunnel */
+	while ((n = read(in, buf + STFHDR, sizeof buf - STFHDR)) > 0) {
+		/* if not IPV6, drop it */
+		ip = (Ip6hdr*)(buf + STFHDR);
+		if ((ip->vcf[0]&0xF0) != 0x60)
+			continue;
+
+		/* check length: drop if too short, trim if too long */
+		m = nhgets(ip->ploadlen) + sizeof(Ip6hdr);
+		if (m > n)
+			continue;
+		if (m < n)
+			n = m;
+
+		/* drop if v6 source or destination address is naughty */
+		if (badipv6(ip->src) ||
+		    (!equivip6(ip->dst, remote6) && badipv6(ip->dst))) {
+			syslog(0, "6in4", "egress filtered %I -> %I",
+				ip->src, ip->dst);
+			continue;
+		}
+
+		/* send 6to4 packets (2002::) directly to ipv4 target */
+		if (ip->dst[0] == 0x20 && ip->dst[1] == 0x02)
+			memcpy(op->dst, ip->dst+2, sizeof op->dst);
+		else
+			memcpy(op->dst, remote4+IPv4off, sizeof op->dst);
+
+		n += STFHDR;
+		/* pass packet to the other end of the tunnel */
+		if (write(out, op, n) != n) {
+			syslog(0, "6in4", "error writing to tunnel (%r), giving up");
+			break;
+		}
+	}
+}
+
+static void
+tunnel2ip(int in, int out)
+{
+	int n, m;
+	char buf[64*1024];
+	uchar a[IPaddrlen];
+	Ip6hdr *op;
+	Iphdr *ip;
+
+	for (;;) {
+		/* get a packet from the tunnel */
+		n = read(in, buf, sizeof buf);
+		ip = (Iphdr*)(buf + IPaddrlen);
+		n -= IPaddrlen;
+		if (n <= 0) {
+			syslog(0, "6in4", "error reading from tunnel (%r), giving up");
+			break;
+		}
+
+		/* if not IPv4 nor IP protocol IPv6, drop it */
+		if ((ip->vihl&0xF0) != 0x40 || ip->proto != IP_IPV6PROTO)
+			continue;
+
+		/* check length: drop if too short, trim if too long */
+		m = nhgets(ip->length);
+		if (m > n)
+			continue;
+		if (m < n)
+			n = m;
+
+		op = (Ip6hdr*)(buf + IPaddrlen + STFHDR);
+		n -= STFHDR;
+
+		/* don't relay: just accept packets for local host/subnet */
+		/* (this blocks link-local and multicast addresses as well) */
+		maskip(op->dst, localmask, a);
+		if (!equivip6(a, localnet)) {
+			syslog(0, "6in4", "ingress filtered %I -> %I",
+				op->src, op->dst);
+			continue;
+		}
+
+		/* pass V6 packet to the interface */
+		write(out, op, n);
+	}
+}
+
+static int
+badipv4(uchar *a)
+{
+	switch (a[0]) {
+	case 0:				/* unassigned */
+	case 10:			/* private */
+	case 127:			/* loopback */
+		return 1;
+	case 172:
+		return a[1] >= 16;	/* 172.16.0.0/12 private */
+	case 192:
+		return a[1] == 168;	/* 192.168.0.0/16 private */
+	case 169:
+		return a[1] == 254;	/* 169.254.0.0/16 DHCP link-local */
+	}
+	/* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
+	return a[0] >= 240;
+}
+
+static int
+badipv6(uchar *a)
+{
+	int h = a[0]<<8 | a[1];
+
+	if (h == 0 ||		/* compatible, mapped, loopback, unspecified ... */
+	    h >= 0xFE80)	/* multicast, link-local or site-local */
+		return 1;
+	if (h == 0x2002 &&	/* 6to4 address */
+	    badipv4(a+2))
+		return 1;
+	return 0;
+}

+ 1 - 1
sys/src/cmd/ip/traceroute.c

@@ -364,7 +364,7 @@ main(int argc, char **argv)
 		ttl = atoi(EARGF(usage()));
 		break;
 	case 'x':
-		net = ARGF();
+		net = EARGF(usage());
 		break;
 	default:
 		usage();