dev9

plan9 devices in POSIX user space
git clone https://pi.duncano.de/git/plan9 devices in POSIX user space
Log | Files | Refs | LICENSE

commit 7aa84a25cb515ca9b3ed7d0cf4317b6600609fe7
Author: Duncaen <mail@duncano.de>
Date:   Tue,  7 Nov 2017 05:40:44 +0100

initial commit

Diffstat:
.gitignore | 3+++
GNUmakefile | 24++++++++++++++++++++++++
LICENSE | 248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Makefrag | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/AUTOGEN | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/a.out.ed | 1+
a/a.out.h | 46++++++++++++++++++++++++++++++++++++++++++++++
a/allocb.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/allocb.ed | 7+++++++
a/aoe.h | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/arg.h | 22++++++++++++++++++++++
a/auth.c | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/auth.ed | 14++++++++++++++
a/authsrv.h | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/chan.c | 1783+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/chan.ed | 35+++++++++++++++++++++++++++++++++++
a/classmask.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/cleanname.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/convD2M.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/convM2D.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/convM2S.c | 315+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/convS2M.c | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/cursor.h | 7+++++++
a/dat.ed | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/dat.h | 369+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/dev.c | 449+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/dev.ed | 6++++++
a/devcap.c | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devcap.ed | 6++++++
a/devcons.c | 1359+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devcons.ed | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devdraw.c | 2270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devdraw.ed | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devdup.c | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devdup.ed | 3+++
a/devenv.c | 438+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devenv.ed | 5+++++
a/devether.c | 542+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devether.ed | 34++++++++++++++++++++++++++++++++++
a/devmnt.c | 1210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devmnt.ed | 21+++++++++++++++++++++
a/devpipe.c | 391+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devpipe.ed | 5+++++
a/devproc.c | 1447+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devproc.ed | 41+++++++++++++++++++++++++++++++++++++++++
a/devroot.c | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devroot.ed | 4++++
a/devsd.ed | 28++++++++++++++++++++++++++++
a/devsrv.c | 376+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devsrv.ed | 34++++++++++++++++++++++++++++++++++
a/devssl.c | 1505+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devssl.ed | 5+++++
a/devtls.c | 2258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/devtls.ed | 15+++++++++++++++
a/dosfs.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/draw.ed | 6++++++
a/draw.h | 516+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/eipfmt.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/encodefmt.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/error.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
a/etherif.ed | 3+++
a/etherif.h | 39+++++++++++++++++++++++++++++++++++++++
a/fault.c | 472+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fault.ed | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fcall.ed | 1+
a/fcall.h | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fcallfmt.c | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fmt.c | 3454+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fmt.h | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fns.ed | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fns.h | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/fs.ed | 1+
a/fs.h | 38++++++++++++++++++++++++++++++++++++++
a/getfields.c | 37+++++++++++++++++++++++++++++++++++++
a/io.ed | 6++++++
a/io.h | 370+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip.h | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/arp.c | 684+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/chandial.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/devip.c | 1439+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/eipconvtest.c | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/esp.c | 951+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ethermedium.c | 766+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/gre.c | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/icmp.c | 490+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/icmp6.c | 946+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/igmp.c | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/il.c | 1408+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/inferno.c | 46++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ip.c | 776+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ip.ed | 2297+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ip.h | 677+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ipaux.c | 368+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ipifc.c | 1654+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ipmux.c | 842+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/iproute.c | 854+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ipv6.c | 718+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ipv6.h | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/loopbackmedium.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/netdevmedium.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/netlog.c | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/nullmedium.c | 39+++++++++++++++++++++++++++++++++++++++
a/ip/pktmedium.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/ptclbsum.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/rudp.c | 1055+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/tcp.c | 3209+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/tripmedium.c | 398+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ip/udp.c | 619+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/ipaux.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/keyboard.ed | 1+
a/keyboard.h | 25+++++++++++++++++++++++++
a/kfs.ed | 2++
a/kfs.h | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/latin1.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/latin1.ed | 48++++++++++++++++++++++++++++++++++++++++++++++++
a/latin1.h | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/lib.ed | 47+++++++++++++++++++++++++++++++++++++++++++++++
a/lib.h | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/libsec.ed | 2++
a/libsec.h | 392+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/mem.ed | 4++++
a/mem.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/memdraw.ed | 28++++++++++++++++++++++++++++
a/memdraw.h | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/memlayer.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
a/mouse.ed | 2++
a/mouse.h | 9+++++++++
a/netif.c | 761+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/netif.c.ed | 4++++
a/netif.h | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/netif.h.ed | 1+
a/page.c | 651+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/page.ed | 17+++++++++++++++++
a/parse.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/parseip.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/parseip.ed | 3+++
a/part.ed | 11+++++++++++
a/pgrp.c | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/pgrp.ed | 10++++++++++
a/portdat.ed | 42++++++++++++++++++++++++++++++++++++++++++
a/portdat.h | 967+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/portfns.ed | 46++++++++++++++++++++++++++++++++++++++++++++++
a/portfns.h | 415+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/print.c | 25+++++++++++++++++++++++++
a/print.ed | 1+
a/proc.c | 1625+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/proc.ed | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/qio.c | 1537+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/qio.ed | 5+++++
a/qlock.c | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/qlock.ed | 8++++++++
a/sd.ed | 12++++++++++++
a/sd.h | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/sdscsi.c | 424+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/sdscsi.ed | 2++
a/segment.c | 797+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/segment.ed | 27+++++++++++++++++++++++++++
a/strecpy.c | 17+++++++++++++++++
a/swap.c | 407+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/swap.ed | 12++++++++++++
a/sys.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
a/sysfile.c | 1371+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/sysfile.ed | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/sysproc.c | 1214+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/sysproc.ed | 446+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/systab.ed | 2++
a/systab.h | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/thwack.c | 381+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/thwack.ed | 1+
a/thwack.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/tokenize.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/tos.h | 44++++++++++++++++++++++++++++++++++++++++++++
a/trace.h | 24++++++++++++++++++++++++
a/u16.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
a/u32.c | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/u64.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/unthwack.c | 297+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/unthwack.ed | 3+++
a/ureg.ed | 1+
a/ureg.h | 25+++++++++++++++++++++++++
a/utf.c | 2060+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
a/utf.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
conf.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
conf.h | 21+++++++++++++++++++++
devfs-posix.c | 950+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
devmntloop.c | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
devmouse.c | 555+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
devram.c | 405+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
devtab.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
errstr.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
fb/fb.c | 359+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
init.h | 26++++++++++++++++++++++++++
kerndate.h | 1+
kprocdev.c | 461+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libdraw/AUTOGEN | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
libdraw/Makefrag | 12++++++++++++
libdraw/arith.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libdraw/arith.ed | 2++
libdraw/bytesperline.c | 34++++++++++++++++++++++++++++++++++
libdraw/chan.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libdraw/chan.ed | 1+
libdraw/computil.c | 38++++++++++++++++++++++++++++++++++++++
libdraw/defont.c | 402+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libdraw/drawrepl.c | 23+++++++++++++++++++++++
libdraw/fmt.c | 22++++++++++++++++++++++
libdraw/icossin.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libdraw/icossin2.c | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libdraw/rectclip.c | 25+++++++++++++++++++++++++
libdraw/rgb.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/AUTOGEN | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/Makefrag | 15+++++++++++++++
libmemdraw/alloc.c | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/alloc.ed | 11+++++++++++
libmemdraw/arc.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/arctest.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/cload.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/cload.ed | 1+
libmemdraw/cmap.c | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/defont.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/defont.ed | 1+
libmemdraw/draw.c | 2576+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/draw.ed | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/ellipse.c | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/ellipse.ed | 7+++++++
libmemdraw/fillpoly.c | 525+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/fillpoly.ed | 9+++++++++
libmemdraw/line.c | 488+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/line.ed | 6++++++
libmemdraw/load.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/load.ed | 1+
libmemdraw/openmemsubfont.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/poly.c | 24++++++++++++++++++++++++
libmemdraw/string.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemdraw/string.ed | 1+
libmemdraw/subfont.c | 34++++++++++++++++++++++++++++++++++
libmemdraw/unload.c | 25+++++++++++++++++++++++++
libmemdraw/unload.ed | 1+
libmemlayer/AUTOGEN | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/Makefrag | 13+++++++++++++
libmemlayer/draw.c | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/lalloc.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/lalloc.ed | 1+
libmemlayer/layerop.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/ldelete.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/lhide.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/line.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/load.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/lorigin.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/lsetrefresh.c | 35+++++++++++++++++++++++++++++++++++
libmemlayer/ltofront.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/ltorear.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libmemlayer/unload.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/AUTOGEN | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/Makefrag | 16++++++++++++++++
libsec/aes.c | 1575+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/aes.ed | 8++++++++
libsec/des.c | 480+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/des3CBC.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/des3CBC.ed | 1+
libsec/desmodes.c | 31+++++++++++++++++++++++++++++++
libsec/hmac.c | 41+++++++++++++++++++++++++++++++++++++++++
libsec/md4.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/md5.c | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/md5block.c | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/os.h | 2++
libsec/rc4.c | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/sha1.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/sha1block.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/sha2_128.c | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/sha2_64.c | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/sha2block128.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libsec/sha2block64.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
load.c | 37+++++++++++++++++++++++++++++++++++++
main.c | 557+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mmu.c | 399+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
nil.c | 20++++++++++++++++++++
posix-arm/Makefile | 25+++++++++++++++++++++++++
posix-arm/getcallerpc.c | 8++++++++
posix-arm/label.S | 30++++++++++++++++++++++++++++++
posix-arm/md5block.c | 268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
posix-arm/sha1block.c | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
posix-arm/tas.c | 35+++++++++++++++++++++++++++++++++++
posix.c | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sched.c | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
screen.h | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
stub.c | 537+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
term.c | 356+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
time.c | 422+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
trap.c | 1108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tty.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
u.h | 20++++++++++++++++++++
unix.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
x11/glenda.h | 21+++++++++++++++++++++
x11/x11-draw.c | 465+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
x11/x11-inc.h | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
x11/x11-init.c | 834+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
x11/x11-itrans.c | 625+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
x11/x11-kernel.c | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
x11/x11-keysym2rune.c | 871+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
299 files changed, 85970 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.a +devdraw diff --git a/GNUmakefile b/GNUmakefile @@ -0,0 +1,24 @@ +top_srcdir = . +top_builddir = . + +PREFIX = /usr/local +EXEC_PREFIX = ${prefix} +INCDIR = ${prefix}/include +BINDIR = ${exec_prefix}/bin +LIBDIR = ${exec_prefix}/lib + +CC = gcc +LD = ld +AR = ar +AS = gcc -g -c + +CFLAGS += -g -fstack-protector-all +LDFLAGS += -g + +O=o + +OS := $(shell uname |tr A-Z a-z) +ARCH := $(shell uname -m) + +include Makefrag + diff --git a/LICENSE b/LICENSE @@ -0,0 +1,248 @@ +The files in this directory (and the subdirectories) are derived from the +Plan 9 from Bell Labs distribution, which carries this license. + +The local changes are Copyright (c) 2006-2008 Russ Cox and +are distributed as contributions under the terms of this license. + +Other contributors are listed on the AUTHORS file. + + +=================================================================== + +Lucent Public License Version 1.02 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE +PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original + Program, and + b. in the case of each Contributor, + + i. changes to the Program, and + ii. additions to the Program; + + where such changes and/or additions to the Program were added to the + Program by such Contributor itself or anyone acting on such + Contributor's behalf, and the Contributor explicitly consents, in + accordance with Section 3C, to characterization of the changes and/or + additions as Contributions. + +"Contributor" means LUCENT and any other entity that has Contributed a +Contribution to the Program. + +"Distributor" means a Recipient that distributes the Program, +modifications to the Program, or any part thereof. + +"Licensed Patents" mean patent claims licensable by a Contributor +which are necessarily infringed by the use or sale of its Contribution +alone or when combined with the Program. + +"Original Program" means the original version of the software +accompanying this Agreement as released by LUCENT, including source +code, object code and documentation, if any. + +"Program" means the Original Program and Contributions or any part +thereof + +"Recipient" means anyone who receives the Program under this +Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare derivative works of, publicly display, + publicly perform, distribute and sublicense the Contribution of such + Contributor, if any, and such derivative works, in source code and + object code form. + + b. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, if + any, in source code and object code form. The patent license granted + by a Contributor shall also apply to the combination of the + Contribution of that Contributor and the Program if, at the time the + Contribution is added by the Contributor, such addition of the + Contribution causes such combination to be covered by the Licensed + Patents. The patent license granted by a Contributor shall not apply + to (i) any other combinations which include the Contribution, nor to + (ii) Contributions of other Contributors. No hardware per se is + licensed hereunder. + + c. Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. Each + Contributor disclaims any liability to Recipient for claims brought by + any other entity based on infringement of intellectual property rights + or otherwise. As a condition to exercising the rights and licenses + granted hereunder, each Recipient hereby assumes sole responsibility + to secure any other intellectual property rights needed, if any. For + example, if a third party patent license is required to allow + Recipient to distribute the Program, it is Recipient's responsibility + to acquire that license before distributing the Program. + + d. Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A. Distributor may choose to distribute the Program in any form under +this Agreement or under its own license agreement, provided that: + + a. it complies with the terms and conditions of this Agreement; + + b. if the Program is distributed in source code or other tangible + form, a copy of this Agreement or Distributor's own license agreement + is included with each copy of the Program; and + + c. if distributed under Distributor's own license agreement, such + license agreement: + + i. effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii. effectively excludes on behalf of all Contributors all liability + for damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; and + iii. states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party. + +B. Each Distributor must include the following in a conspicuous + location in the Program: + + Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights + Reserved. + +C. In addition, each Contributor must identify itself as the +originator of its Contribution in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution. +Also, each Contributor must agree that the additions and/or changes +are intended to be a Contribution. Once a Contribution is contributed, +it may not thereafter be revoked. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use +of the Program, the Distributor who includes the Program in a +commercial product offering should do so in a manner which does not +create potential liability for Contributors. Therefore, if a +Distributor includes the Program in a commercial product offering, +such Distributor ("Commercial Distributor") hereby agrees to defend +and indemnify every Contributor ("Indemnified Contributor") against +any losses, damages and costs (collectively"Losses") arising from +claims, lawsuits and other legal actions brought by a third party +against the Indemnified Contributor to the extent caused by the acts +or omissions of such Commercial Distributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. +In order to qualify, an Indemnified Contributor must: a) promptly +notify the Commercial Distributor in writing of such claim, and b) +allow the Commercial Distributor to control, and cooperate with the +Commercial Distributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such +claim at its own expense. + +For example, a Distributor might include the Program in a commercial +product offering, Product X. That Distributor is then a Commercial +Distributor. If that Commercial Distributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Distributor's responsibility +alone. Under this section, the Commercial Distributor would have to +defend claims against the Contributors related to those performance +claims and warranties, and if a court requires any Contributor to pay +any damages as a result, the Commercial Distributor must pay those +damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY +WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement, including but not limited to +the risks and costs of program errors, compliance with applicable +laws, damage to or loss of data, programs or equipment, and +unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR +ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. EXPORT CONTROL + +Recipient agrees that Recipient alone is responsible for compliance +with the United States export administration regulations (and the +export control laws and regulation of any other countries). + +8. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with +respect to a patent applicable to software (including a cross-claim or +counterclaim in a lawsuit), then any patent licenses granted by that +Contributor to such Recipient under this Agreement shall terminate as +of the date such litigation is filed. In addition, if Recipient +institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program +itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and +survive. + +LUCENT may publish new versions (including revisions) of this +Agreement from time to time. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new +version of the Agreement is published, Contributor may elect to +distribute the Program (including its Contributions) under the new +version. No one other than LUCENT has the right to modify this +Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, +Recipient receives no rights or licenses to the intellectual property +of any Contributor under this Agreement, whether expressly, by +implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No +party to this Agreement will bring a legal action under this Agreement +more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation. + diff --git a/Makefrag b/Makefrag @@ -0,0 +1,81 @@ +PLAN9BOOTARG=local!/boot/rootfs + +#VPATH=a:libdraw:libmemdraw:libmemlayer:libsec + +CFLAGS_X11= +LDLIBS_X11=-X11 + +CFLAGS+=-I. -Ia + +OFILES=\ + $(patsubst %.c,%.$O,$(wildcard *.c)) + +OFILES_A=\ + $(patsubst %.c,%.$O,$(wildcard a/*.c)) + +OFILES_X11=\ + $(patsubst %.c,%.$O,$(wildcard x11/*.c)) + +OFILES_FB=\ + $(patsubst %.c,%.$O,$(wildcard fb/*.c)) + +OFILES_POSIX=\ + $(patsubst %.c,%.$O,$(wildcard posix-arm/*.c))\ + $(patsubst %.S,%.$O,$(wildcard posix-arm/*.S))\ + +LIBS=\ + libdraw.a\ + libmemdraw.a\ + libmemlayer.a\ + libsec.a\ + +#devdraw: $(LIBS) devdraw.o stub.o + +devdraw: \ + $(OFILES)\ + $(OFILES_A)\ + $(OFILES_FB)\ + $(OFILES_POSIX)\ + | $(LIBS)\ + +devdraw: LDLIBS+=-L. -lsec -lmemlayer -lmemdraw -ldraw +# devdraw: LDLIBS+=-L. libsec.a libmemlayer.a libmemdraw.a libdraw.a + +all: devdraw + +clean: + rm -f $(OFILES) $(OFILES_A) $(OFILES_FB) $(OFILES_POSIX) $(LIBS) + +main.$O: CFLAGS+=-D "BOOTARG=\"$(PLAN9BOOTARG)\"" + +proc.$O: errstr.h +stub.$O: kerndate.h + +errstr.h: a/error.h + sed 's/extern //; s!;.*/\* ! = "!; s! \*\/!";!' $^ >$@ + +kerndate.h: + echo 'ulong kerndate =' `date +%s` ';' >$@ + +include libdraw/Makefrag +include libmemdraw/Makefrag +include libmemlayer/Makefrag +include libsec/Makefrag + +libdraw.a: $(addprefix libdraw/,$(OFILES_LIBDRAW)) +libmemdraw.a: $(addprefix libmemdraw/,$(OFILES_LIBMEMDRAW)) +libmemlayer.a: $(addprefix libmemlayer/,$(OFILES_LIBMEMLAYER)) +libsec.a: $(addprefix libsec/,$(OFILES_LIBSEC)) + +%.a: + $(AR) rs $@ $^ + +%.o: %.S + $(AS) $*.S -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $*.c -o $@ + +devdraw: + $(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS) + diff --git a/a/AUTOGEN b/a/AUTOGEN @@ -0,0 +1,150 @@ +#!/bin/sh + +# Most of the kernel and library files come in virtually unchanged. +# Rather than maintain the few changes by hand, we keep a set +# of ed scripts that we can run on the originals to produce +# the versions we need. +# +# This directory also contains a few files that aren't +# autogenerated, but are extracted from elsewhere and +# would distract from vxplan9 itself if placed in the main +# directory (utf.[ch], fmt.[ch], latin1.h, arg.h). + +autofiles=" +/386/include/ureg.h +/sys/include/a.out.h +/sys/include/authsrv.h +/sys/include/cursor.h +/sys/include/draw.h +/sys/include/fcall.h +/sys/include/ip.h +/sys/include/keyboard.h +/sys/include/libsec.h +/sys/include/memdraw.h +/sys/include/memlayer.h +/sys/include/mouse.h +/sys/include/tos.h +/sys/include/trace.h +/sys/src/9/pc/dat.h +/sys/src/9/pc/devether.c +/sys/src/9/pc/etherif.h +/sys/src/9/pc/fns.h +/sys/src/9/pc/io.h +/sys/src/9/pc/mem.h +/sys/src/9/pc/sdscsi.c +/sys/src/9/port/allocb.c +#/sys/src/9/port/aoe.h +/sys/src/9/port/auth.c +/sys/src/9/port/chan.c +/sys/src/9/port/dev.c +#/sys/src/9/port/devaoe.c +/sys/src/9/port/devcap.c +/sys/src/9/port/devcons.c +/sys/src/9/port/devdraw.c +/sys/src/9/port/devdup.c +/sys/src/9/port/devenv.c +/sys/src/9/port/devmnt.c +/sys/src/9/port/devpipe.c +/sys/src/9/port/devproc.c +/sys/src/9/port/devroot.c +/sys/src/9/port/devsd.c +/sys/src/9/port/devssl.c +/sys/src/9/port/devsrv.c +/sys/src/9/port/devtls.c +/sys/src/9/port/error.h +/sys/src/9/port/fault.c +/sys/src/9/port/latin1.c +/sys/src/9/port/lib.h +/sys/src/9/port/netif.c +/sys/src/9/port/netif.h +/sys/src/9/port/page.c +/sys/src/9/port/parse.c +/sys/src/9/port/pgrp.c +/sys/src/9/port/portdat.h +/sys/src/9/port/portfns.h +/sys/src/9/port/print.c +/sys/src/9/port/proc.c +/sys/src/9/port/qio.c +/sys/src/9/port/qlock.c +/sys/src/9/port/sd.h +#/sys/src/9/port/sdaoe.c +/sys/src/9/port/segment.c +/sys/src/9/port/swap.c +/sys/src/9/port/sysfile.c +/sys/src/9/port/sysproc.c +/sys/src/9/port/systab.h +/sys/src/9/port/thwack.c +/sys/src/9/port/thwack.h +/sys/src/libc/port/u16.c +/sys/src/libc/port/u32.c +/sys/src/9/port/unthwack.c +/sys/src/boot/pc/fs.h +/sys/src/boot/pc/dosfs.h +/sys/src/boot/pc/kfs.h +/sys/src/boot/pc/part.c +/sys/src/libc/9syscall/sys.h +/sys/src/libc/9sys/convD2M.c +/sys/src/libc/9sys/convM2D.c +/sys/src/libc/9sys/convM2S.c +/sys/src/libc/9sys/convS2M.c +/sys/src/libc/9sys/fcallfmt.c +/sys/src/libc/port/cleanname.c +/sys/src/libc/port/encodefmt.c +/sys/src/libc/port/getfields.c +/sys/src/libc/port/strecpy.c +/sys/src/libc/port/tokenize.c +/sys/src/libc/port/u64.c +/sys/src/libip/bo.c +/sys/src/libip/classmask.c +/sys/src/libip/eipfmt.c +/sys/src/libip/ipaux.c +/sys/src/libip/parseip.c +" + +plan9=/usr/local/9vx +if [ $# -gt 1 ] && [ $1 == "-r" ]; then + plan9=`ls -d $2` || exit 1 + shift 2 +fi + +case "$#" in +0) + ;; +*) + autofiles="$*" +esac + +errors=0 + +for f in $autofiles +do + in=`echo $plan9$f | sed 's;//;/;'` + out=`echo $f | sed 's;.*/;;;'` + ed=`echo $f | sed 's;.*/;;; s;$;.ed;'` + test -f $ed || ed=`echo $ed | sed 's;\.[ch]\.ed$;.ed;'` + if test -f $in; then + echo -n $f '->' $out + test -f $ed && echo ' ('$ed')' || echo + test -f $out && chmod +w $out + ( + echo ',s;"../ip/;"ip/;g' + echo ',s;"../port/;";g' + echo ',s;#include.*<;#include ";g' + echo ',s;#include.*>;&FIXINCLUDEME;g' + echo ',s;>FIXINCLUDEME;";g' + echo ',s;"libc.h";"lib.h";g' + echo ',s;SET(\(.*\));;g' + echo 'g/#pragma/d' + test -f $ed && cat $ed + echo w $out + echo q + ) | ed -s $in 2>&1 | egrep -v '^[0-9?]+$' + else + echo "ERROR: $in not found" 1>&2 + errors=`echo $errors + 1 | bc` + fi +done + +test $errors -gt 0 && echo -n $errors error && + (test $errors -gt 1 && echo s || echo) +exit 0 diff --git a/a/a.out.ed b/a/a.out.ed @@ -0,0 +1 @@ +,s/ long/ int32/g diff --git a/a/a.out.h b/a/a.out.h @@ -0,0 +1,46 @@ +typedef struct Exec Exec; +struct Exec +{ + int32 magic; /* magic number */ + int32 text; /* size of text segment */ + int32 data; /* size of initialized data */ + int32 bss; /* size of uninitialized data */ + int32 syms; /* size of symbol table */ + int32 entry; /* entry point32 */ + int32 spsz; /* size of pc/sp offset table */ + int32 pcsz; /* size of pc/line number table */ +}; + +#define HDR_MAGIC 0x00008000 /* header expansion */ + +#define _MAGIC(f, b) ((f)|((((4*(b))+0)*(b))+7)) +#define A_MAGIC _MAGIC(0, 8) /* 68020 */ +#define I_MAGIC _MAGIC(0, 11) /* intel 386 */ +#define J_MAGIC _MAGIC(0, 12) /* intel 960 (retired) */ +#define K_MAGIC _MAGIC(0, 13) /* sparc */ +#define V_MAGIC _MAGIC(0, 16) /* mips 3000 BE */ +#define X_MAGIC _MAGIC(0, 17) /* att dsp 3210 (retired) */ +#define M_MAGIC _MAGIC(0, 18) /* mips 4000 BE */ +#define D_MAGIC _MAGIC(0, 19) /* amd 29000 (retired) */ +#define E_MAGIC _MAGIC(0, 20) /* arm */ +#define Q_MAGIC _MAGIC(0, 21) /* powerpc */ +#define N_MAGIC _MAGIC(0, 22) /* mips 4000 LE */ +#define L_MAGIC _MAGIC(0, 23) /* dec alpha */ +#define P_MAGIC _MAGIC(0, 24) /* mips 3000 LE */ +#define U_MAGIC _MAGIC(0, 25) /* sparc64 */ +#define S_MAGIC _MAGIC(HDR_MAGIC, 26) /* amd64 */ +#define T_MAGIC _MAGIC(HDR_MAGIC, 27) /* powerpc64 */ + +#define MIN_MAGIC 8 +#define MAX_MAGIC 27 /* <= 90 */ + +#define DYN_MAGIC 0x80000000 /* dlm */ + +typedef struct Sym Sym; +struct Sym +{ + vlong value; + uint sig; + char type; + char *name; +}; diff --git a/a/allocb.c b/a/allocb.c @@ -0,0 +1,184 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Hdrspc = 64, /* leave room for high-level headers */ + Bdead = 0x51494F42, /* "QIOB" */ +}; + +struct +{ + Lock lk; + ulong bytes; +} ialloc; + +static Block* +_allocb(int size) +{ + Block *b; + ulong addr; + + if((b = mallocz(sizeof(Block)+size+Hdrspc, 0)) == nil) + return nil; + + b->next = nil; + b->list = nil; + b->free = 0; + b->flag = 0; + b->ref = 0; + + /* align start of data portion by rounding up */ + addr = (ulong)b; + addr = ROUND(addr + sizeof(Block), BLOCKALIGN); + b->base = (uchar*)addr; + + /* align end of data portion by rounding down */ + b->lim = ((uchar*)b) + sizeof(Block)+size+Hdrspc; + addr = (ulong)(b->lim); + addr = addr & ~(BLOCKALIGN-1); + b->lim = (uchar*)addr; + + /* leave sluff at beginning for added headers */ + b->rp = b->lim - ROUND(size, BLOCKALIGN); + if(b->rp < b->base) + panic("_allocb"); + b->wp = b->rp; + + return b; +} + +Block* +allocb(int size) +{ + Block *b; + + /* + * Check in a process and wait until successful. + * Can still error out of here, though. + */ + if(up == nil) + panic("allocb without up: %#p", getcallerpc(&size)); + if((b = _allocb(size)) == nil){ + xsummary(); + mallocsummary(); + panic("allocb: no memory for %d bytes", size); + } + setmalloctag(b, getcallerpc(&size)); + + return b; +} + +Block* +iallocb(int size) +{ + Block *b; + static int m1, m2, mp; + + if(ialloc.bytes > conf.ialloc){ + if((m1++%10000)==0){ + if(mp++ > 1000){ + active.exiting = 1; + panic("iallocb"); + } + iprint("iallocb: limited %lud/%lud\n", + ialloc.bytes, conf.ialloc); + } + return nil; + } + + if((b = _allocb(size)) == nil){ + if((m2++%10000)==0){ + if(mp++ > 1000){ + active.exiting = 1; + panic("iallocb"); + } + iprint("iallocb: no memory %lud/%lud\n", + ialloc.bytes, conf.ialloc); + } + return nil; + } + setmalloctag(b, getcallerpc(&size)); + b->flag = BINTR; + + ilock(&ialloc.lk); + ialloc.bytes += b->lim - b->base; + iunlock(&ialloc.lk); + + return b; +} + +void +freeb(Block *b) +{ + void *dead = (void*)Bdead; + long ref; + + if(b == nil) + return; + + if(0){ + dumpstack(); + panic("freeb: ref %ld; caller pc %#p", ref, getcallerpc(&b)); + } + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + */ + if(b->free) { + b->free(b); + return; + } + if(b->flag & BINTR) { + ilock(&ialloc.lk); + ialloc.bytes -= b->lim - b->base; + iunlock(&ialloc.lk); + } + + /* poison the block in case someone is still holding onto it */ + b->next = dead; + b->rp = dead; + b->wp = dead; + b->lim = dead; + b->base = dead; + + free(b); +} + +void +checkb(Block *b, char *msg) +{ + void *dead = (void*)Bdead; + + if(b == dead) + panic("checkb b %s %#p", msg, b); + if(b->base == dead || b->lim == dead || b->next == dead + || b->rp == dead || b->wp == dead){ + print("checkb: base %#p lim %#p next %#p\n", + b->base, b->lim, b->next); + print("checkb: rp %#p wp %#p\n", b->rp, b->wp); + panic("checkb dead: %s", msg); + } + + if(b->base > b->lim) + panic("checkb 0 %s %#p %#p", msg, b->base, b->lim); + if(b->rp < b->base) + panic("checkb 1 %s %#p %#p", msg, b->base, b->rp); + if(b->wp < b->base) + panic("checkb 2 %s %#p %#p", msg, b->base, b->wp); + if(b->rp > b->lim) + panic("checkb 3 %s %#p %#p", msg, b->rp, b->lim); + if(b->wp > b->lim) + panic("checkb 4 %s %#p %#p", msg, b->wp, b->lim); +} + +void +iallocsummary(void) +{ + print("ialloc %lud/%lud\n", ialloc.bytes, conf.ialloc); +} diff --git a/a/allocb.ed b/a/allocb.ed @@ -0,0 +1,7 @@ +,s!Lock;!Lock lk;! +,s!lock(&ialloc)!lock(\&ialloc.lk)!g +,s!msize(b)!sizeof(Block)+size+Hdrspc! +,s/exit(0)/panic("iallocb")/ +/_xinc/d +/_xdec/s/ ||.*0// +,s/ref < 0/0/ diff --git a/a/aoe.h b/a/aoe.h @@ -0,0 +1,84 @@ +enum { + ACata, + ACconfig, +}; + +enum { + AQCread, + AQCtest, + AQCprefix, + AQCset, + AQCfset, +}; + +enum { + AEcmd = 1, + AEarg, + AEdev, + AEcfg, + AEver, +}; + +enum { + Aoetype = 0x88a2, + Aoesectsz = 512, + Szaoeata = 24+12, + Szaoeqc = 24+8, + Aoever = 1, + + AFerr = 1<<2, + AFrsp = 1<<3, + + AAFwrite= 1, + AAFext = 1<<6, +}; + +typedef struct { + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; + uchar type[2]; + uchar verflag; + uchar error; + uchar major[2]; + uchar minor; + uchar cmd; + uchar tag[4]; +} Aoehdr; + +typedef struct { + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; + uchar type[2]; + uchar verflag; + uchar error; + uchar major[2]; + uchar minor; + uchar cmd; + uchar tag[4]; + uchar aflag; + uchar errfeat; + uchar scnt; + uchar cmdstat; + uchar lba[6]; + uchar res[2]; +} Aoeata; + +typedef struct { + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; + uchar type[2]; + uchar verflag; + uchar error; + uchar major[2]; + uchar minor; + uchar cmd; + uchar tag[4]; + uchar bufcnt[2]; + uchar fwver[2]; + uchar scnt; + uchar verccmd; + uchar cslen[2]; +} Aoeqc; + +extern char Echange[]; +extern char Enotup[]; diff --git a/a/arg.h b/a/arg.h @@ -0,0 +1,22 @@ +#define SET(x) ((x)=0,(void)(x)) + +#define ARGBEGIN for((argv0?0:(argv0=(*argv))),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + diff --git a/a/auth.c b/a/auth.c @@ -0,0 +1,154 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "authsrv.h" + +char *eve; +char hostdomain[DOMLEN]; + +/* + * return true if current user is eve + */ +int +iseve(void) +{ + return strcmp(eve, up->user) == 0; +} + +long +sysfversion(uint32 *arg) +{ + char *vers; + uint arglen, m, msize; + Chan *c; + + msize = arg[1]; + arglen = arg[3]; + vers = uvalidaddr(arg[2], arglen, 1); + /* check there's a NUL in the version string */ + if(arglen==0 || memchr(vers, 0, arglen)==0) + error(Ebadarg); + c = fdtochan(arg[0], ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + m = mntversion(c, vers, msize, arglen); + + cclose(c); + poperror(); + return m; +} + +long +sys_fsession(uint32 *arg) +{ + /* deprecated; backwards compatibility only */ + + if(arg[2] == 0) + error(Ebadarg); + *(char*)uvalidaddr(arg[1], arg[2], 1) = '\0'; + return 0; +} + +long +sysfauth(uint32 *arg) +{ + Chan *c, *ac; + char *aname; + int fd; + + aname = validnamedup(uvalidaddr(arg[1], 1, 0), 1); + if(waserror()){ + free(aname); + nexterror(); + } + c = fdtochan(arg[0], ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + ac = mntauth(c, aname); + /* at this point ac is responsible for keeping c alive */ + cclose(c); + poperror(); /* c */ + free(aname); + poperror(); /* aname */ + + if(waserror()){ + cclose(ac); + nexterror(); + } + + fd = newfd(ac); + if(fd < 0) + error(Enofd); + poperror(); /* ac */ + + /* always mark it close on exec */ + ac->flag |= CCEXEC; + + return fd; +} + +/* + * called by devcons() for user device + * + * anyone can become none + */ +long +userwrite(char *a, int n) +{ + if(n!=4 || strncmp(a, "none", 4)!=0) + error(Eperm); + kstrdup(&up->user, "none"); + up->basepri = PriNormal; + return n; +} + +/* + * called by devcons() for host owner/domain + * + * writing hostowner also sets user + */ +long +hostownerwrite(char *a, int n) +{ + char buf[128]; + + if(!iseve()) + error(Eperm); + if(n <= 0 || n >= sizeof buf) + error(Ebadarg); + memmove(buf, a, n); + buf[n] = 0; + + renameuser(eve, buf); + kstrdup(&eve, buf); + kstrdup(&up->user, buf); + up->basepri = PriNormal; + return n; +} + +long +hostdomainwrite(char *a, int n) +{ + char buf[DOMLEN]; + + if(!iseve()) + error(Eperm); + if(n >= DOMLEN) + error(Ebadarg); + memset(buf, 0, DOMLEN); + strncpy(buf, a, n); + if(buf[0] == 0) + error(Ebadarg); + memmove(hostdomain, buf, DOMLEN); + return n; +} diff --git a/a/auth.ed b/a/auth.ed @@ -0,0 +1,14 @@ +,s/ulong/uint32/g +,g/validaddr/d +/^sysfversion/ . +/arg\[2\]/c + vers = uvalidaddr(arg[2], arglen, 1); +. +/^sys_fsession/ . +/arg\[1\]/c + *(char*)uvalidaddr(arg[1], arg[2], 1) = '\0'; +. +/^sysfauth/ . +/arg\[1\]/c + aname = validnamedup(uvalidaddr(arg[1], 1, 0), 1); +. diff --git a/a/authsrv.h b/a/authsrv.h @@ -0,0 +1,171 @@ + +/* + * Interface for talking to authentication server. + */ +typedef struct Ticket Ticket; +typedef struct Ticketreq Ticketreq; +typedef struct Authenticator Authenticator; +typedef struct Nvrsafe Nvrsafe; +typedef struct Passwordreq Passwordreq; +typedef struct OChapreply OChapreply; +typedef struct OMSchapreply OMSchapreply; + +enum +{ + ANAMELEN= 28, /* name max size in previous proto */ + AERRLEN= 64, /* errstr max size in previous proto */ + DOMLEN= 48, /* authentication domain name length */ + DESKEYLEN= 7, /* encrypt/decrypt des key length */ + CHALLEN= 8, /* plan9 sk1 challenge length */ + NETCHLEN= 16, /* max network challenge length (used in AS protocol) */ + CONFIGLEN= 14, + SECRETLEN= 32, /* secret max size */ + + KEYDBOFF= 8, /* bytes of random data at key file's start */ + OKEYDBLEN= ANAMELEN+DESKEYLEN+4+2, /* old key file entry length */ + KEYDBLEN= OKEYDBLEN+SECRETLEN, /* key file entry length */ + OMD5LEN= 16, +}; + +/* encryption numberings (anti-replay) */ +enum +{ + AuthTreq=1, /* ticket request */ + AuthChal=2, /* challenge box request */ + AuthPass=3, /* change password */ + AuthOK=4, /* fixed length reply follows */ + AuthErr=5, /* error follows */ + AuthMod=6, /* modify user */ + AuthApop=7, /* apop authentication for pop3 */ + AuthOKvar=9, /* variable length reply follows */ + AuthChap=10, /* chap authentication for ppp */ + AuthMSchap=11, /* MS chap authentication for ppp */ + AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */ + AuthHttp=13, /* http domain login */ + AuthVNC=14, /* VNC server login (deprecated) */ + + + AuthTs=64, /* ticket encrypted with server's key */ + AuthTc, /* ticket encrypted with client's key */ + AuthAs, /* server generated authenticator */ + AuthAc, /* client generated authenticator */ + AuthTp, /* ticket encrypted with client's key for password change */ + AuthHr, /* http reply */ +}; + +struct Ticketreq +{ + char type; + char authid[ANAMELEN]; /* server's encryption id */ + char authdom[DOMLEN]; /* server's authentication domain */ + char chal[CHALLEN]; /* challenge from server */ + char hostid[ANAMELEN]; /* host's encryption id */ + char uid[ANAMELEN]; /* uid of requesting user on host */ +}; +#define TICKREQLEN (3*ANAMELEN+CHALLEN+DOMLEN+1) + +struct Ticket +{ + char num; /* replay protection */ + char chal[CHALLEN]; /* server challenge */ + char cuid[ANAMELEN]; /* uid on client */ + char suid[ANAMELEN]; /* uid on server */ + char key[DESKEYLEN]; /* nonce DES key */ +}; +#define TICKETLEN (CHALLEN+2*ANAMELEN+DESKEYLEN+1) + +struct Authenticator +{ + char num; /* replay protection */ + char chal[CHALLEN]; + ulong id; /* authenticator id, ++'d with each auth */ +}; +#define AUTHENTLEN (CHALLEN+4+1) + +struct Passwordreq +{ + char num; + char old[ANAMELEN]; + char new[ANAMELEN]; + char changesecret; + char secret[SECRETLEN]; /* new secret */ +}; +#define PASSREQLEN (2*ANAMELEN+1+1+SECRETLEN) + +struct OChapreply +{ + uchar id; + char uid[ANAMELEN]; + char resp[OMD5LEN]; +}; + +struct OMSchapreply +{ + char uid[ANAMELEN]; + char LMresp[24]; /* Lan Manager response */ + char NTresp[24]; /* NT response */ +}; + +/* + * convert to/from wire format + */ +extern int convT2M(Ticket*, char*, char*); +extern void convM2T(char*, Ticket*, char*); +extern void convM2Tnoenc(char*, Ticket*); +extern int convA2M(Authenticator*, char*, char*); +extern void convM2A(char*, Authenticator*, char*); +extern int convTR2M(Ticketreq*, char*); +extern void convM2TR(char*, Ticketreq*); +extern int convPR2M(Passwordreq*, char*, char*); +extern void convM2PR(char*, Passwordreq*, char*); + +/* + * convert ascii password to DES key + */ +extern int opasstokey(char*, char*); +extern int passtokey(char*, char*); + +/* + * Nvram interface + */ +enum { + NVread = 0, /* just read */ + NVwrite = 1<<0, /* always prompt and rewrite nvram */ + NVwriteonerr = 1<<1, /* prompt and rewrite nvram when corrupt */ + NVwritemem = 1<<2, /* don't prompt, write nvram from argument */ +}; + +/* storage layout */ +struct Nvrsafe +{ + char machkey[DESKEYLEN]; /* was file server's authid's des key */ + uchar machsum; + char authkey[DESKEYLEN]; /* authid's des key from password */ + uchar authsum; + /* + * file server config string of device holding full configuration; + * secstore key on non-file-servers. + */ + char config[CONFIGLEN]; + uchar configsum; + char authid[ANAMELEN]; /* auth userid, e.g., bootes */ + uchar authidsum; + char authdom[DOMLEN]; /* auth domain, e.g., cs.bell-labs.com */ + uchar authdomsum; +}; + +extern uchar nvcsum(void*, int); +extern int readnvram(Nvrsafe*, int); + +/* + * call up auth server + */ +extern int authdial(char *netroot, char *authdom); + +/* + * exchange messages with auth server + */ +extern int _asgetticket(int, char*, char*); +extern int _asrdresp(int, char*, int); +extern int sslnegotiate(int, Ticket*, char**, char**); +extern int srvsslnegotiate(int, Ticket*, char**, char**); diff --git a/a/chan.c b/a/chan.c @@ -0,0 +1,1783 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +int chandebug=0; /* toggled by sysr1 */ +#define DBG if(chandebug)iprint + +enum +{ + PATHSLOP = 20, + PATHMSLOP = 20, +}; + +struct +{ + Lock lk; + int fid; + Chan *free; + Chan *list; +}chanalloc; + +typedef struct Elemlist Elemlist; + +struct Elemlist +{ + char *aname; /* original name */ + char *name; /* copy of name, so '/' can be overwritten */ + int nelems; + char **elems; + int *off; + int mustbedir; + int nerror; + int prefix; +}; + +#define SEP(c) ((c) == 0 || (c) == '/') + +/*static*/ void +dumpmount(void) /* DEBUGGING */ +{ + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + if(up == nil){ + print("no process for dumpmount\n"); + return; + } + pg = up->pgrp; + if(pg == nil){ + print("no pgrp for dumpmount\n"); + return; + } + rlock(&pg->ns); + if(waserror()){ + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++){ + for(f = *h; f; f = f->hash){ + print("head: %#p: %s %#llux.%lud %C %lud -> \n", f, + f->from->path->s, f->from->qid.path, + f->from->qid.vers, devtab[f->from->type]->dc, + f->from->dev); + for(t = f->mount; t; t = t->next) + print("\t%#p: %s (umh %#p) (path %#.8llux dev %C %lud)\n", t, t->to->path->s, t->to->umh, t->to->qid.path, devtab[t->to->type]->dc, t->to->dev); + } + } + poperror(); + runlock(&pg->ns); +} + +char* +chanpath(Chan *c) +{ + if(c == nil) + return "<nil chan>"; + if(c->path == nil) + return "<nil path>"; + if(c->path->s == nil) + return "<nil path.s>"; + return c->path->s; +} + +int +isdotdot(char *p) +{ + return p[0]=='.' && p[1]=='.' && p[2]=='\0'; +} + +long +incref(Ref *r) +{ + long x; + + lock(&r->lk); + x = ++r->ref; + unlock(&r->lk); + return x; +} + +long +decref(Ref *r) +{ + long x; + + lock(&r->lk); + x = --r->ref; + unlock(&r->lk); + if(x < 0) + panic("decref pc=%#p", getcallerpc(&r)); + + return x; +} + +/* + * Rather than strncpy, which zeros the rest of the buffer, kstrcpy + * truncates if necessary, always zero terminates, does not zero fill, + * and puts ... at the end of the string if it's too long. Usually used to + * save a string in up->genbuf; + */ +void +kstrcpy(char *s, char *t, int ns) +{ + int nt; + + nt = strlen(t); + if(nt+1 <= ns){ + memmove(s, t, nt+1); + return; + } + /* too long */ + if(ns < 4){ + /* but very short! */ + strncpy(s, t, ns); + return; + } + /* truncate with ... at character boundary (very rare case) */ + memmove(s, t, ns-4); + ns -= 4; + s[ns] = '\0'; + /* look for first byte of UTF-8 sequence by skipping continuation bytes */ + while(ns>0 && (s[--ns]&0xC0)==0x80) + ; + strcpy(s+ns, "..."); +} + +int +emptystr(char *s) +{ + if(s == nil) + return 1; + if(s[0] == '\0') + return 1; + return 0; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + + n = strlen(s)+1; + /* if it's a user, we can wait for memory; if not, something's very wrong */ + if(up){ + t = smalloc(n); + setmalloctag(t, getcallerpc(&p)); + }else{ + t = malloc(n); + if(t == nil) + panic("kstrdup: no memory"); + } + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} + +void +chandevreset(void) +{ + int i; + + todinit(); /* avoid later reentry causing infinite recursion */ + for(i=0; devtab[i] != nil; i++) + devtab[i]->reset(); +} + +void +chandevinit(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +void +chandevshutdown(void) +{ + int i; + + /* shutdown in reverse order */ + for(i=0; devtab[i] != nil; i++) + ; + for(i--; i >= 0; i--) + devtab[i]->shutdown(); +} + +Chan* +newchan(void) +{ + Chan *c; + + lock(&chanalloc.lk); + c = chanalloc.free; + if(c != 0) + chanalloc.free = c->next; + unlock(&chanalloc.lk); + + if(c == nil){ + c = smalloc(sizeof(Chan)); + lock(&chanalloc.lk); + c->fid = ++chanalloc.fid; + c->link = chanalloc.list; + chanalloc.list = c; + unlock(&chanalloc.lk); + } + + /* if you get an error before associating with a dev, + close calls rootclose, a nop */ + c->type = 0; + c->flag = 0; + c->ref.ref = 1; + c->dev = 0; + c->offset = 0; + c->devoffset = 0; + c->iounit = 0; + c->umh = 0; + c->uri = 0; + c->dri = 0; + c->aux = 0; + c->mchan = 0; + c->mcp = 0; + c->mux = 0; + memset(&c->mqid, 0, sizeof(c->mqid)); + c->path = 0; + c->ismtpt = 0; + + return c; +} + +Ref npath; + +Path* +newpath(char *s) +{ + int i; + Path *p; + + p = smalloc(sizeof(Path)); + i = strlen(s); + p->len = i; + p->alen = i+PATHSLOP; + p->s = smalloc(p->alen); + memmove(p->s, s, i+1); + p->ref.ref = 1; + incref(&npath); + + /* + * Cannot use newpath for arbitrary names because the mtpt + * array will not be populated correctly. The names #/ and / are + * allowed, but other names with / in them draw warnings. + */ + if(strchr(s, '/') && strcmp(s, "#/") != 0 && strcmp(s, "/") != 0) + print("newpath: %s from %#p\n", s, getcallerpc(&s)); + + p->mlen = 1; + p->malen = PATHMSLOP; + p->mtpt = smalloc(p->malen*sizeof p->mtpt[0]); + return p; +} + +static Path* +copypath(Path *p) +{ + int i; + Path *pp; + + pp = smalloc(sizeof(Path)); + pp->ref.ref = 1; + incref(&npath); + DBG("copypath %s %p => %p\n", p->s, p, pp); + + pp->len = p->len; + pp->alen = p->alen; + pp->s = smalloc(p->alen); + memmove(pp->s, p->s, p->len+1); + + pp->mlen = p->mlen; + pp->malen = p->malen; + pp->mtpt = smalloc(p->malen*sizeof pp->mtpt[0]); + for(i=0; i<pp->mlen; i++){ + pp->mtpt[i] = p->mtpt[i]; + if(pp->mtpt[i]) + incref(&pp->mtpt[i]->ref); + } + + return pp; +} + +void +pathclose(Path *p) +{ + int i; + + if(p == nil) + return; +//XXX + DBG("pathclose %p %s ref=%ld =>", p, p->s, p->ref.ref); + for(i=0; i<p->mlen; i++) + DBG(" %p", p->mtpt[i]); + DBG("\n"); + + if(decref(&p->ref)) + return; + decref(&npath); + free(p->s); + for(i=0; i<p->mlen; i++) + if(p->mtpt[i]) + cclose(p->mtpt[i]); + free(p->mtpt); + free(p); +} + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + * (Really only called to remove a trailing .. that has been added. + * Otherwise would need to update n->mtpt as well.) + */ +static void +fixdotdotname(Path *p) +{ + char *r; + + if(p->s[0] == '#'){ + r = strchr(p->s, '/'); + if(r == nil) + return; + cleanname(r); + + /* + * The correct name is #i rather than #i/, + * but the correct name of #/ is #/. + */ + if(strcmp(r, "/")==0 && p->s[1] != '/') + *r = '\0'; + }else + cleanname(p->s); + p->len = strlen(p->s); +} + +static Path* +uniquepath(Path *p) +{ + Path *new; + + if(p->ref.ref > 1){ + /* copy on write */ + new = copypath(p); + pathclose(p); + p = new; + } + return p; +} + +/*static*/ Path* +addelem(Path *p, char *s, Chan *from) +{ + char *t; + int a, i; + Chan *c, **tt; + + if(s[0]=='.' && s[1]=='\0') + return p; + + p = uniquepath(p); + + i = strlen(s); + if(p->len+1+i+1 > p->alen){ + a = p->len+1+i+1 + PATHSLOP; + t = smalloc(a); + memmove(t, p->s, p->len+1); + free(p->s); + p->s = t; + p->alen = a; + } + /* don't insert extra slash if one is present */ + if(p->len>0 && p->s[p->len-1]!='/' && s[0]!='/') + p->s[p->len++] = '/'; + memmove(p->s+p->len, s, i+1); + p->len += i; + if(isdotdot(s)){ + fixdotdotname(p); + DBG("addelem %s .. => rm %p\n", p->s, p->mtpt[p->mlen-1]); + if(p->mlen>1 && (c = p->mtpt[--p->mlen])){ + p->mtpt[p->mlen] = nil; + cclose(c); + } + }else{ + if(p->mlen >= p->malen){ + p->malen = p->mlen+1+PATHMSLOP; + tt = smalloc(p->malen*sizeof tt[0]); + memmove(tt, p->mtpt, p->mlen*sizeof tt[0]); + free(p->mtpt); + p->mtpt = tt; + } + DBG("addelem %s %s => add %p\n", p->s, s, from); + p->mtpt[p->mlen++] = from; + if(from) + incref(&from->ref); + } + return p; +} + +void +chanfree(Chan *c) +{ + c->flag = CFREE; + + if(c->dirrock != nil){ + free(c->dirrock); + c->dirrock = 0; + c->nrock = 0; + c->mrock = 0; + } + if(c->umh != nil){ + putmhead(c->umh); + c->umh = nil; + } + if(c->umc != nil){ + cclose(c->umc); + c->umc = nil; + } + if(c->mux != nil){ + muxclose(c->mux); + c->mux = nil; + } + if(c->mchan != nil){ + cclose(c->mchan); + c->mchan = nil; + } + + pathclose(c->path); + c->path = nil; + + lock(&chanalloc.lk); + c->next = chanalloc.free; + chanalloc.free = c; + unlock(&chanalloc.lk); +} + +void +cclose(Chan *c) +{ + if(c->flag&CFREE) + panic("cclose %#p", getcallerpc(&c)); + + DBG("cclose %p name=%s ref=%ld\n", c, c->path->s, c->ref.ref); + if(decref(&c->ref)) + return; + + if(!waserror()){ + devtab[c->type]->close(c); + poperror(); + } + chanfree(c); +} + +/* + * Queue a chan to be closed by one of the clunk procs. + */ +struct { + Chan *head; + Chan *tail; + int nqueued; + int nclosed; + Lock l; + QLock q; + Rendez r; +} clunkq; +void closeproc(void*); + +void +ccloseq(Chan *c) +{ + if(c->flag&CFREE) + panic("cclose %#p", getcallerpc(&c)); + + DBG("ccloseq %p name=%s ref=%ld\n", c, c->path->s, c->ref.ref); + + if(decref(&c->ref)) + return; + + lock(&clunkq.l); + clunkq.nqueued++; + c->next = nil; + if(clunkq.head) + clunkq.tail->next = c; + else + clunkq.head = c; + clunkq.tail = c; + unlock(&clunkq.l); + + if(!wakeup(&clunkq.r)) + kproc("closeproc", closeproc, nil); +} + +static int +clunkwork(void *v) +{ + return clunkq.head != nil; +} + +void +closeproc(void *v) +{ + Chan *c; + + for(;;){ + qlock(&clunkq.q); + if(clunkq.head == nil){ + if(!waserror()){ + tsleep(&clunkq.r, clunkwork, nil, 5000); + poperror(); + } + if(clunkq.head == nil){ + qunlock(&clunkq.q); + pexit("no work", 1); + } + } + lock(&clunkq.l); + c = clunkq.head; + clunkq.head = c->next; + clunkq.nclosed++; + unlock(&clunkq.l); + qunlock(&clunkq.q); + if(!waserror()){ + devtab[c->type]->close(c); + poperror(); + } + chanfree(c); + } +} + +/* + * Make sure we have the only copy of c. (Copy on write.) + */ +Chan* +cunique(Chan *c) +{ + Chan *nc; + + if(c->ref.ref != 1){ + nc = cclone(c); + cclose(c); + c = nc; + } + + return c; +} + +int +eqqid(Qid a, Qid b) +{ + return a.path==b.path && a.vers==b.vers; +} + +int +eqchan(Chan *a, Chan *b, int skipvers) +{ + if(a->qid.path != b->qid.path) + return 0; + if(!skipvers && a->qid.vers!=b->qid.vers) + return 0; + if(a->type != b->type) + return 0; + if(a->dev != b->dev) + return 0; + return 1; +} + +int +eqchantdqid(Chan *a, int type, int dev, Qid qid, int skipvers) +{ + if(a->qid.path != qid.path) + return 0; + if(!skipvers && a->qid.vers!=qid.vers) + return 0; + if(a->type != type) + return 0; + if(a->dev != dev) + return 0; + return 1; +} + +Mhead* +newmhead(Chan *from) +{ + Mhead *mh; + + mh = smalloc(sizeof(Mhead)); + mh->ref.ref = 1; + mh->from = from; + incref(&from->ref); + return mh; +} + +int +cmount(Chan **newp, Chan *old, int flag, char *spec) +{ + int order, flg; + Chan *new; + Mhead *m, **l, *mh; + Mount *nm, *f, *um, **h; + Pgrp *pg; + + if(QTDIR & (old->qid.type^(*newp)->qid.type)) + error(Emount); + + if(old->umh) + print("cmount: unexpected umh, caller %#p\n", getcallerpc(&newp)); + + order = flag&MORDER; + + if((old->qid.type&QTDIR)==0 && order != MREPL) + error(Emount); + + new = *newp; + mh = new->umh; + + /* + * Not allowed to bind when the old directory is itself a union. + * (Maybe it should be allowed, but I don't see what the semantics + * would be.) + * + * We need to check mh->mount->next to tell unions apart from + * simple mount points, so that things like + * mount -c fd /root + * bind -c /root / + * work. + * + * The check of mount->mflag allows things like + * mount fd /root + * bind -c /root / + * + * This is far more complicated than it should be, but I don't + * see an easier way at the moment. + */ + if((flag&MCREATE) && mh && mh->mount + && (mh->mount->next || !(mh->mount->mflag&MCREATE))) + error(Emount); + + pg = up->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, old->qid); + for(m = *l; m; m = m->hash){ + if(eqchan(m->from, old, 1)) + break; + l = &m->hash; + } + + if(m == nil){ + /* + * nothing mounted here yet. create a mount + * head and add to the hash table. + */ + m = newmhead(old); + *l = m; + + /* + * if this is a union mount, add the old + * node to the mount chain. + */ + if(order != MREPL) + m->mount = newmount(m, old, 0, 0); + } + wlock(&m->lock); + if(waserror()){ + wunlock(&m->lock); + nexterror(); + } + wunlock(&pg->ns); + + nm = newmount(m, new, flag, spec); + if(mh != nil && mh->mount != nil){ + /* + * copy a union when binding it onto a directory + */ + flg = order; + if(order == MREPL) + flg = MAFTER; + h = &nm->next; + um = mh->mount; + for(um = um->next; um; um = um->next){ + f = newmount(m, um->to, flg, um->spec); + *h = f; + h = &f->next; + } + } + + if(m->mount && order == MREPL){ + mountfree(m->mount); + m->mount = 0; + } + + if(flag & MCREATE) + nm->mflag |= MCREATE; + + if(m->mount && order == MAFTER){ + for(f = m->mount; f->next; f = f->next) + ; + f->next = nm; + }else{ + for(f = nm; f->next; f = f->next) + ; + f->next = m->mount; + m->mount = nm; + } + + wunlock(&m->lock); + poperror(); + return nm->mountid; +} + +void +cunmount(Chan *mnt, Chan *mounted) +{ + Pgrp *pg; + Mhead *m, **l; + Mount *f, **p; + + if(mnt->umh) /* should not happen */ + print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); + + /* + * It _can_ happen that mounted->umh is non-nil, + * because mounted is the result of namec(Aopen) + * (see sysfile.c:/^sysunmount). + * If we open a union directory, it will have a umh. + * Although surprising, this is okay, since the + * cclose will take care of freeing the umh. + */ + + pg = up->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, mnt->qid); + for(m = *l; m; m = m->hash){ + if(eqchan(m->from, mnt, 1)) + break; + l = &m->hash; + } + + if(m == 0){ + wunlock(&pg->ns); + error(Eunmount); + } + + wlock(&m->lock); + if(mounted == 0){ + *l = m->hash; + wunlock(&pg->ns); + mountfree(m->mount); + m->mount = nil; + cclose(m->from); + wunlock(&m->lock); + putmhead(m); + return; + } + + p = &m->mount; + for(f = *p; f; f = f->next){ + /* BUG: Needs to be 2 pass */ + if(eqchan(f->to, mounted, 1) || + (f->to->mchan && eqchan(f->to->mchan, mounted, 1))){ + *p = f->next; + f->next = 0; + mountfree(f); + if(m->mount == nil){ + *l = m->hash; + cclose(m->from); + wunlock(&m->lock); + wunlock(&pg->ns); + putmhead(m); + return; + } + wunlock(&m->lock); + wunlock(&pg->ns); + return; + } + p = &f->next; + } + wunlock(&m->lock); + wunlock(&pg->ns); + error(Eunion); +} + +Chan* +cclone(Chan *c) +{ + Chan *nc; + Walkqid *wq; + + wq = devtab[c->type]->walk(c, nil, nil, 0); + if(wq == nil) + error("clone failed"); + nc = wq->clone; + free(wq); + nc->path = c->path; + if(c->path) + incref(&c->path->ref); + return nc; +} + +/* also used by sysfile.c:/^mountfix */ +int +findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) +{ + Pgrp *pg; + Mhead *m; + + pg = up->pgrp; + rlock(&pg->ns); + for(m = MOUNTH(pg, qid); m; m = m->hash){ + rlock(&m->lock); + if(m->from == nil){ + print("m %p m->from 0\n", m); + runlock(&m->lock); + continue; + } + if(eqchantdqid(m->from, type, dev, qid, 1)){ + runlock(&pg->ns); + if(mp != nil){ + incref(&m->ref); + if(*mp != nil) + putmhead(*mp); + *mp = m; + } + if(*cp != nil) + cclose(*cp); + incref(&m->mount->to->ref); + *cp = m->mount->to; + runlock(&m->lock); + return 1; + } + runlock(&m->lock); + } + + runlock(&pg->ns); + return 0; +} + +/* + * Calls findmount but also updates path. + */ +static int +domount(Chan **cp, Mhead **mp, Path **path) +{ + Chan **lc; + Path *p; + + if(findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid) == 0) + return 0; + + if(path){ + p = *path; + p = uniquepath(p); + if(p->mlen <= 0) + print("domount: path %s has mlen==%d\n", p->s, p->mlen); + else{ + lc = &p->mtpt[p->mlen-1]; +DBG("domount %p %s => add %p (was %p)\n", p, p->s, (*mp)->from, p->mtpt[p->mlen-1]); + incref(&(*mp)->from->ref); + if(*lc) + cclose(*lc); + *lc = (*mp)->from; + } + *path = p; + } + return 1; +} + +/* + * If c is the right-hand-side of a mount point, returns the left hand side. + * Changes name to reflect the fact that we've uncrossed the mountpoint, + * so name had better be ours to change! + */ +static Chan* +undomount(Chan *c, Path *path) +{ + Chan *nc; + + if(path->ref.ref != 1 || path->mlen == 0) + print("undomount: path %s ref %ld mlen %d caller %#p\n", + path->s, path->ref.ref, path->mlen, getcallerpc(&c)); + + if(path->mlen>0 && (nc=path->mtpt[path->mlen-1]) != nil){ +DBG("undomount %p %s => remove %p\n", path, path->s, nc); + cclose(c); + path->mtpt[path->mlen-1] = nil; + c = nc; + } + return c; +} + +/* + * Call dev walk but catch errors. + */ +static Walkqid* +ewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + if(waserror()) + return nil; + wq = devtab[c->type]->walk(c, nc, name, nname); + poperror(); + return wq; +} + +/* + * Either walks all the way or not at all. No partial results in *cp. + * *nerror is the number of names to display in an error message. + */ +static char Edoesnotexist[] = "does not exist"; +int +walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int dev, didmount, dotdot, i, n, nhave, ntry, type; + Chan *c, *nc, *mtpt; + Path *path; + Mhead *mh, *nmh; + Mount *f; + Walkqid *wq; + + c = *cp; + incref(&c->ref); + path = c->path; + incref(&path->ref); + mh = nil; + + /* + * While we haven't gotten all the way down the path: + * 1. step through a mount point, if any + * 2. send a walk request for initial dotdot or initial prefix without dotdot + * 3. move to the first mountpoint along the way. + * 4. repeat. + * + * Each time through the loop: + * + * If didmount==0, c is on the undomount side of the mount point. + * If didmount==1, c is on the domount side of the mount point. + * Either way, c's full path is path. + */ + didmount = 0; + for(nhave=0; nhave<nnames; nhave+=n){ + if((c->qid.type&QTDIR)==0){ + if(nerror) + *nerror = nhave; + pathclose(path); + cclose(c); + strcpy(up->errstr, Enotdir); + if(mh != nil) + putmhead(mh); + return -1; + } + ntry = nnames - nhave; + if(ntry > MAXWELEM) + ntry = MAXWELEM; + dotdot = 0; + for(i=0; i<ntry; i++){ + if(isdotdot(names[nhave+i])){ + if(i==0){ + dotdot = 1; + ntry = 1; + }else + ntry = i; + break; + } + } + + if(!dotdot && !nomount && !didmount) + domount(&c, &mh, &path); + + type = c->type; + dev = c->dev; + + if((wq = ewalk(c, nil, names+nhave, ntry)) == nil){ + /* try a union mount, if any */ + if(mh && !nomount){ + /* + * mh->mount->to == c, so start at mh->mount->next + */ + rlock(&mh->lock); + for(f = mh->mount->next; f; f = f->next) + if((wq = ewalk(f->to, nil, names+nhave, ntry)) != nil) + break; + runlock(&mh->lock); + if(f != nil){ + type = f->to->type; + dev = f->to->dev; + } + } + if(wq == nil){ + cclose(c); + pathclose(path); + if(nerror) + *nerror = nhave+1; + if(mh != nil) + putmhead(mh); + return -1; + } + } + + didmount = 0; + if(dotdot){ + assert(wq->nqid == 1); + assert(wq->clone != nil); + + path = addelem(path, "..", nil); + nc = undomount(wq->clone, path); + nmh = nil; + n = 1; + }else{ + nc = nil; + nmh = nil; + if(!nomount){ + for(i=0; i<wq->nqid && i<ntry-1; i++){ + if(findmount(&nc, &nmh, type, dev, wq->qid[i])){ + didmount = 1; + break; + } + } + } + if(nc == nil){ /* no mount points along path */ + if(wq->clone == nil){ + cclose(c); + pathclose(path); + if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ + if(nerror) + *nerror = nhave+wq->nqid+1; + strcpy(up->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->errstr, Enotdir); + } + free(wq); + if(mh != nil) + putmhead(mh); + return -1; + } + n = wq->nqid; + nc = wq->clone; + }else{ /* stopped early, at a mount point */ + didmount = 1; + if(wq->clone != nil){ + cclose(wq->clone); + wq->clone = nil; + } + n = i+1; + } + for(i=0; i<n; i++){ + mtpt = nil; + if(i==n-1 && nmh) + mtpt = nmh->from; + path = addelem(path, names[nhave+i], mtpt); + } + } + cclose(c); + c = nc; + putmhead(mh); + mh = nmh; + free(wq); + } + + putmhead(mh); + + c = cunique(c); + + if(c->umh != nil){ //BUG + print("walk umh\n"); + putmhead(c->umh); + c->umh = nil; + } + + pathclose(c->path); + c->path = path; + + cclose(*cp); + *cp = c; + if(nerror) + *nerror = nhave; + return 0; +} + +/* + * c is a mounted non-creatable directory. find a creatable one. + */ +Chan* +createdir(Chan *c, Mhead *m) +{ + Chan *nc; + Mount *f; + + rlock(&m->lock); + if(waserror()){ + runlock(&m->lock); + nexterror(); + } + for(f = m->mount; f; f = f->next){ + if(f->mflag&MCREATE){ + nc = cclone(f->to); + runlock(&m->lock); + poperror(); + cclose(c); + return nc; + } + } + error(Enocreate); + return 0; +} + +void +saveregisters(void) +{ +} + +static void +growparse(Elemlist *e) +{ + char **new; + int *inew; + enum { Delta = 8 }; + + if(e->nelems % Delta == 0){ + new = smalloc((e->nelems+Delta) * sizeof(char*)); + memmove(new, e->elems, e->nelems*sizeof(char*)); + free(e->elems); + e->elems = new; + inew = smalloc((e->nelems+Delta+1) * sizeof(int)); + memmove(inew, e->off, (e->nelems+1)*sizeof(int)); + free(e->off); + e->off = inew; + } +} + +/* + * The name is known to be valid. + * Copy the name so slashes can be overwritten. + * An empty string will set nelem=0. + * A path ending in / or /. or /.//./ etc. will have + * e.mustbedir = 1, so that we correctly + * reject, e.g., "/adm/users/." when /adm/users is a file + * rather than a directory. + */ +static void +parsename(char *aname, Elemlist *e) +{ + char *name, *slash; + + kstrdup(&e->name, aname); + name = e->name; + e->nelems = 0; + e->elems = nil; + e->off = smalloc(sizeof(int)); + e->off[0] = skipslash(name) - name; + for(;;){ + name = skipslash(name); + if(*name == '\0'){ + e->off[e->nelems] = name+strlen(name) - e->name; + e->mustbedir = 1; + break; + } + growparse(e); + e->elems[e->nelems++] = name; + slash = utfrune(name, '/'); + if(slash == nil){ + e->off[e->nelems] = name+strlen(name) - e->name; + e->mustbedir = 0; + break; + } + e->off[e->nelems] = slash - e->name; + *slash++ = '\0'; + name = slash; + } + + if(0 && chandebug){ + int i; + + print("parsename %s:", e->name); + for(i=0; i<=e->nelems; i++) + print(" %d", e->off[i]); + print("\n"); + } +} + +void* +memrchr(void *va, int c, long n) +{ + uchar *a, *e; + + a = va; + for(e=a+n-1; e>a; e--) + if(*e == c) + return e; + return nil; +} + +void +namelenerror(char *aname, int len, char *err) +{ + char *ename, *name, *next; + int i, errlen; + + /* + * If the name is short enough, just use the whole thing. + */ + errlen = strlen(err); + if(len < ERRMAX/3 || len+errlen < 2*ERRMAX/3) + snprint(up->genbuf, sizeof up->genbuf, "%.*s", + utfnlen(aname, len), aname); + else{ + /* + * Print a suffix of the name, but try to get a little info. + */ + ename = aname+len; + next = ename; + do{ + name = next; + next = memrchr(aname, '/', name-aname); + if(next == nil) + next = aname; + len = ename-next; + }while(len < ERRMAX/3 || len + errlen < 2*ERRMAX/3); + + /* + * If the name is ridiculously long, chop it. + */ + if(name == ename){ + name = ename-ERRMAX/4; + if(name <= aname) + panic("bad math in namelenerror"); + /* walk out of current UTF sequence */ + for(i=0; (*name&0xC0)==0x80 && i<3; i++) + name++; + } + snprint(up->genbuf, sizeof up->genbuf, "...%.*s", + utfnlen(name, ename-name), name); + } + snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, err); + nexterror(); +} + +void +nameerror(char *name, char *err) +{ + namelenerror(name, strlen(name), err); +} + +/* + * Turn a name into a channel. + * &name[0] is known to be a valid address. It may be a kernel address. + * + * Opening with amode Aopen, Acreate, Aremove, or Aaccess guarantees + * that the result will be the only reference to that particular fid. + * This is necessary since we might pass the result to + * devtab[]->remove(). + * + * Opening Atodir or Amount does not guarantee this. + * + * Under certain circumstances, opening Aaccess will cause + * an unnecessary clone in order to get a cunique Chan so it + * can attach the correct name. Sysstat and sys_stat need the + * correct name so they can rewrite the stat info. + */ +Chan* +namec(char *aname, int amode, int omode, ulong perm) +{ + int len, n, t, nomount; + Chan *c, *cnew; + Path *path; + Elemlist e; + Rune r; + Mhead *m; + char *createerr, tmperrbuf[ERRMAX]; + char *name; + + if(aname[0] == '\0') + error("empty file name"); + aname = validnamedup(aname, 1); + if(waserror()){ + free(aname); + nexterror(); + } + if(tracesyscalls) + iprint("\tnamec %s\n", aname); + DBG("namec %s %d %d\n", aname, amode, omode); + name = aname; + + /* + * Find the starting off point (the current slash, the root of + * a device tree, or the current dot) as well as the name to + * evaluate starting there. + */ + nomount = 0; + switch(name[0]){ + case '/': + c = up->slash; + incref(&c->ref); + break; + + case '#': + nomount = 1; + up->genbuf[0] = '\0'; + n = 0; + while(*name != '\0' && (*name != '/' || n < 2)){ + if(n >= sizeof(up->genbuf)-1) + error(Efilename); + up->genbuf[n++] = *name++; + } + up->genbuf[n] = '\0'; + /* + * noattach is sandboxing. + * + * the OK exceptions are: + * | it only gives access to pipes you create + * d this process's file descriptors + * e this process's environment + * the iffy exceptions are: + * c time and pid, but also cons and consctl + * p control of your own processes (and unfortunately + * any others left unprotected) + */ + n = chartorune(&r, up->genbuf+1)+1; + /* actually / is caught by parsing earlier */ + if(utfrune("M", r)) + error(Enoattach); + if(up->pgrp->noattach && utfrune("|decp", r)==nil) + error(Enoattach); + t = devno(r, 1); + if(t == -1) + error(Ebadsharp); + c = devtab[t]->attach(up->genbuf+n); + break; + + default: + c = up->dot; + incref(&c->ref); + break; + } + + e.aname = aname; + e.prefix = name - aname; + e.name = nil; + e.elems = nil; + e.off = nil; + e.nelems = 0; + e.nerror = 0; + if(waserror()){ + cclose(c); + free(e.name); + free(e.elems); + /* + * Prepare nice error, showing first e.nerror elements of name. + */ + if(e.nerror == 0) + nexterror(); + strcpy(tmperrbuf, up->errstr); + if(e.off[e.nerror]==0) + print("nerror=%d but off=%d\n", + e.nerror, e.off[e.nerror]); + if(0 && chandebug) + print("showing %d+%d/%d (of %d) of %s (%d %d)\n", e.prefix, e.off[e.nerror], e.nerror, e.nelems, aname, e.off[0], e.off[1]); + len = e.prefix+e.off[e.nerror]; + free(e.off); + namelenerror(aname, len, tmperrbuf); + } + + /* + * Build a list of elements in the name. + */ + parsename(name, &e); + + /* + * On create, .... + */ + if(amode == Acreate){ + /* perm must have DMDIR if last element is / or /. */ + if(e.mustbedir && !(perm&DMDIR)){ + e.nerror = e.nelems; + error("create without DMDIR"); + } + + /* don't try to walk the last path element just yet. */ + if(e.nelems == 0) + error(Eexist); + e.nelems--; + } + + if(walk(&c, e.elems, e.nelems, nomount, &e.nerror) < 0){ + if(e.nerror < 0 || e.nerror > e.nelems){ + print("namec %s walk error nerror=%d\n", aname, e.nerror); + e.nerror = 0; + } + nexterror(); + } + + if(e.mustbedir && !(c->qid.type&QTDIR)) + error("not a directory"); + + if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)) + error("cannot exec directory"); + + switch(amode){ + case Abind: + /* no need to maintain path - cannot dotdot an Abind */ + m = nil; + if(!nomount) + domount(&c, &m, nil); + if(c->umh != nil) + putmhead(c->umh); + c->umh = m; + break; + + case Aaccess: + case Aremove: + case Aopen: + Open: + /* save&update the name; domount might change c */ + path = c->path; + incref(&path->ref); + m = nil; + if(!nomount) + domount(&c, &m, &path); + + /* our own copy to open or remove */ + c = cunique(c); + + /* now it's our copy anyway, we can put the name back */ + pathclose(c->path); + c->path = path; + + /* record whether c is on a mount point */ + c->ismtpt = m!=nil; + + switch(amode){ + case Aaccess: + case Aremove: + putmhead(m); + break; + + case Aopen: + case Acreate: +if(c->umh != nil){ + print("cunique umh Open\n"); + putmhead(c->umh); + c->umh = nil; +} + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + /* save registers else error() in open has wrong value of c saved */ + saveregisters(); + + if(omode == OEXEC) + c->flag &= ~CCACHE; + + c = devtab[c->type]->open(c, omode&~OCEXEC); + + if(omode & OCEXEC) + c->flag |= CCEXEC; + if(omode & ORCLOSE) + c->flag |= CRCLOSE; + break; + } + break; + + case Atodir: + /* + * Directories (e.g. for cd) are left before the mount point, + * so one may mount on / or . and see the effect. + */ + if(!(c->qid.type & QTDIR)) + error(Enotdir); + break; + + case Amount: + /* + * When mounting on an already mounted upon directory, + * one wants subsequent mounts to be attached to the + * original directory, not the replacement. Don't domount. + */ + break; + + case Acreate: + /* + * We've already walked all but the last element. + * If the last exists, try to open it OTRUNC. + * If omode&OEXCL is set, just give up. + */ + e.nelems++; + e.nerror++; + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ + if(omode&OEXCL) + error(Eexist); + omode |= OTRUNC; + goto Open; + } + + /* + * The semantics of the create(2) system call are that if the + * file exists and can be written, it is to be opened with truncation. + * On the other hand, the create(5) message fails if the file exists. + * If we get two create(2) calls happening simultaneously, + * they might both get here and send create(5) messages, but only + * one of the messages will succeed. To provide the expected create(2) + * semantics, the call with the failed message needs to try the above + * walk again, opening for truncation. This correctly solves the + * create/create race, in the sense that any observable outcome can + * be explained as one happening before the other. + * The create/create race is quite common. For example, it happens + * when two rc subshells simultaneously update the same + * environment variable. + * + * The implementation still admits a create/create/remove race: + * (A) walk to file, fails + * (B) walk to file, fails + * (A) create file, succeeds, returns + * (B) create file, fails + * (A) remove file, succeeds, returns + * (B) walk to file, return failure. + * + * This is hardly as common as the create/create race, and is really + * not too much worse than what might happen if (B) got a hold of a + * file descriptor and then the file was removed -- either way (B) can't do + * anything with the result of the create call. So we don't care about this race. + * + * Applications that care about more fine-grained decision of the races + * can use the OEXCL flag to get at the underlying create(5) semantics; + * by default we provide the common case. + * + * We need to stay behind the mount point in case we + * need to do the first walk again (should the create fail). + * + * We also need to cross the mount point and find the directory + * in the union in which we should be creating. + * + * The channel staying behind is c, the one moving forward is cnew. + */ + m = nil; + cnew = nil; /* is this assignment necessary? */ + if(!waserror()){ /* try create */ + if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) + cnew = createdir(cnew, m); + else{ + cnew = c; + incref(&cnew->ref); + } + + /* + * We need our own copy of the Chan because we're + * about to send a create, which will move it. Once we have + * our own copy, we can fix the name, which might be wrong + * if findmount gave us a new Chan. + */ + cnew = cunique(cnew); + pathclose(cnew->path); + cnew->path = c->path; + incref(&cnew->path->ref); + + devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); + poperror(); + if(omode & OCEXEC) + cnew->flag |= CCEXEC; + if(omode & ORCLOSE) + cnew->flag |= CRCLOSE; + if(m) + putmhead(m); + cclose(c); + c = cnew; + c->path = addelem(c->path, e.elems[e.nelems-1], nil); + break; + } + + /* create failed */ + cclose(cnew); + if(m) + putmhead(m); + if(omode & OEXCL) + nexterror(); + /* save error */ + createerr = up->errstr; + up->errstr = tmperrbuf; + /* note: we depend that walk does not error */ + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ + up->errstr = createerr; + error(createerr); /* report true error */ + } + up->errstr = createerr; + omode |= OTRUNC; + goto Open; + + default: + panic("unknown namec access %d\n", amode); + } + + /* place final element in genbuf for e.g. exec */ + if(e.nelems > 0) + kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); + else + kstrcpy(up->genbuf, ".", sizeof up->genbuf); + free(e.name); + free(e.elems); + free(e.off); + poperror(); /* e c */ + free(aname); + poperror(); /* aname */ + + return c; +} + +/* + * name is valid. skip leading / and ./ as much as possible + */ +char* +skipslash(char *name) +{ + while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) + name++; + return name; +} + +char isfrog[256]={ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, + ['/'] 1, + [0x7f] 1, +}; + +/* + * Check that the name + * a) is in valid memory. + * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. + * c) contains no frogs. + * The first byte is known to be addressible by the requester, so the + * routine works for kernel and user memory both. + * The parameter slashok flags whether a slash character is an error + * or a valid character. + * + * The parameter dup flags whether the string should be copied + * out of user space before being scanned the second time. + * (Otherwise a malicious thread could remove the NUL, causing us + * to access unchecked addresses.) + */ +static char* +validname0(char *aname, int slashok, int dup, ulong pc) +{ + char *ename, *name, *s; + int c, n; + Rune r; + + name = aname; + if(isuaddr(name)){ + if(!dup) + print("warning: validname called from %lux with user pointer", pc); + char *p; + uint t; + p = name; + t = BY2PG-((ulong)p&(BY2PG-1)); + while((ename=vmemchr(p, 0, t)) == nil){ + p += t; + t = BY2PG; + } + }else + ename = memchr(name, 0, (1<<16)); + + if(ename==nil || ename-name>=(1<<16)) + error("name too long"); + + s = nil; + if(dup){ + n = ename-name; + s = smalloc(n+1); + memmove(s, name, n); + s[n] = 0; + aname = s; + name = s; + setmalloctag(s, pc); + } + + while(*name){ + /* all characters above '~' are ok */ + c = *(uchar*)name; + if(c >= Runeself) + name += chartorune(&r, name); + else{ + if(isfrog[c]) + if(!slashok || c!='/'){ + snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); + free(s); + error(up->genbuf); + } + name++; + } + } + return s; +} + +void +validname(char *aname, int slashok) +{ + validname0(aname, slashok, 0, getcallerpc(&aname)); +} + +char* +validnamedup(char *aname, int slashok) +{ + return validname0(aname, slashok, 1, getcallerpc(&aname)); +} + +void +isdir(Chan *c) +{ + if(c->qid.type & QTDIR) + return; + error(Enotdir); +} + +/* + * This is necessary because there are many + * pointers to the top of a given mount list: + * + * - the mhead in the namespace hash table + * - the mhead in chans returned from findmount: + * used in namec and then by unionread. + * - the mhead in chans returned from createdir: + * used in the open/create race protect, which is gone. + * + * The RWlock in the Mhead protects the mount list it contains. + * The mount list is deleted when we cunmount. + * The RWlock ensures that nothing is using the mount list at that time. + * + * It is okay to replace c->mh with whatever you want as + * long as you are sure you have a unique reference to it. + * + * This comment might belong somewhere else. + */ +void +putmhead(Mhead *m) +{ + if(m && decref(&m->ref) == 0){ + m->mount = (Mount*)0xCafeBeef; + free(m); + } +} + diff --git a/a/chan.ed b/a/chan.ed @@ -0,0 +1,35 @@ +,s!Lock;!Lock lk;!g +/dumpmount/-1 s!static !/*static*/ ! +,s!lock(r)!lock(\&r->lk)!g +,s!lock(\&chanalloc)!lock(\&chanalloc.lk)!g +,s!->ref!->ref.ref!g +,s!r->ref.ref!r->ref!g +,s!incref(pp->mtpt\[i\])!incref(\&pp->mtpt[i]->ref)!g +,s!ref(p)!ref(\&p->ref)!g +,s!ref(c)!ref(\&c->ref)!g +,s!ref(from)!ref(\&from->ref)!g +g/^cl/ s/(void\*)/(void *v)/ +,s!ref(c->path)!ref(\&c->path->ref)!g +,s!ref(m)!ref(\&m->ref)!g +,s!ref(m->mount->to)!ref(\&m->mount->to->ref)!g +,s!ref((\*mp)->from)!ref(\&(*mp)->from->ref)!g +,s!ref(path)!ref(\&path->ref)!g +,s!ref(cnew)!ref(\&cnew->ref)!g +,s!ref(cnew->path)!ref(\&cnew->path->ref)!g +/^addelem/-1 s;static;/*static*/; +/DBG("namec/i + if(tracesyscalls) + iprint("\tnamec %s\n", aname); +. +,s/(ulong)name < KZERO/isuaddr(name)/ +/validaddr/d +/ename = vmemchr(name, 0, (1<<16))/c + char *p; + uint t; + p = name; + t = BY2PG-((ulong)p&(BY2PG-1)); + while((ename=vmemchr(p, 0, t)) == nil){ + p += t; + t = BY2PG; + } +. diff --git a/a/classmask.c b/a/classmask.c @@ -0,0 +1,86 @@ +#include "u.h" +#include "lib.h" +#include "ip.h" + +static uchar classmask[4][16] = { + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0x00, +}; + +static uchar v6loopback[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; + +static uchar v6linklocal[IPaddrlen] = { + 0xfe, 0x80, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +static uchar v6linklocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +static int v6llpreflen = 8; /* link-local prefix length in bytes */ + +static uchar v6multicast[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +static uchar v6multicastmask[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +static int v6mcpreflen = 1; /* multicast prefix length */ + +static uchar v6solicitednode[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01, + 0xff, 0, 0, 0 +}; +static uchar v6solicitednodemask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0, 0x0, 0x0 +}; +static int v6snpreflen = 13; + +uchar* +defmask(uchar *ip) +{ + if(isv4(ip)) + return classmask[ip[IPv4off]>>6]; + else { + if(ipcmp(ip, v6loopback) == 0) + return IPallbits; + else if(memcmp(ip, v6linklocal, v6llpreflen) == 0) + return v6linklocalmask; + else if(memcmp(ip, v6solicitednode, v6snpreflen) == 0) + return v6solicitednodemask; + else if(memcmp(ip, v6multicast, v6mcpreflen) == 0) + return v6multicastmask; + return IPallbits; + } +} + +void +maskip(uchar *from, uchar *mask, uchar *to) +{ + int i; + + for(i = 0; i < IPaddrlen; i++) + to[i] = from[i] & mask[i]; +} diff --git a/a/cleanname.c b/a/cleanname.c @@ -0,0 +1,63 @@ +#include "u.h" +#include "lib.h" + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ +#define SEP(x) ((x)=='/' || (x) == 0) +char* +cleanname(char *name) +{ + char *p, *q, *dotdot; + int rooted, erasedprefix; + + rooted = name[0] == '/'; + erasedprefix = 0; + + /* + * invariants: + * p points at beginning of path element we're considering. + * q points just past the last path element we wrote (no slash). + * dotdot points just past the point where .. cannot backtrack + * any further (no slash). + */ + p = q = dotdot = name+rooted; + while(*p) { + if(p[0] == '/') /* null element */ + p++; + else if(p[0] == '.' && SEP(p[1])) { + if(p == name) + erasedprefix = 1; + p += 1; /* don't count the separator in case it is nul */ + } else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) { + p += 2; + if(q > dotdot) { /* can backtrack */ + while(--q > dotdot && *q != '/') + ; + } else if(!rooted) { /* /.. is / but ./../ is .. */ + if(q != name) + *q++ = '/'; + *q++ = '.'; + *q++ = '.'; + dotdot = q; + } + if(q == name) + erasedprefix = 1; /* erased entire path via dotdot */ + } else { /* real path element */ + if(q != name+rooted) + *q++ = '/'; + while((*q = *p) != '/' && *q != 0) + p++, q++; + } + } + if(q == name) /* empty string is really ``.'' */ + *q++ = '.'; + *q = '\0'; + if(erasedprefix && name[0] == '#'){ + /* this was not a #x device path originally - make it not one now */ + memmove(name+2, name, strlen(name)+1); + name[0] = '.'; + name[1] = '/'; + } + return name; +} diff --git a/a/convD2M.c b/a/convD2M.c @@ -0,0 +1,95 @@ +#include "u.h" +#include "lib.h" +#include "fcall.h" + +uint +sizeD2M(Dir *d) +{ + char *sv[4]; + int i, ns; + + sv[0] = d->name; + sv[1] = d->uid; + sv[2] = d->gid; + sv[3] = d->muid; + + ns = 0; + for(i = 0; i < 4; i++) + if(sv[i]) + ns += strlen(sv[i]); + + return STATFIXLEN + ns; +} + +uint +convD2M(Dir *d, uchar *buf, uint nbuf) +{ + uchar *p, *ebuf; + char *sv[4]; + int i, ns, nsv[4], ss; + + if(nbuf < BIT16SZ) + return 0; + + p = buf; + ebuf = buf + nbuf; + + sv[0] = d->name; + sv[1] = d->uid; + sv[2] = d->gid; + sv[3] = d->muid; + + ns = 0; + for(i = 0; i < 4; i++){ + if(sv[i]) + nsv[i] = strlen(sv[i]); + else + nsv[i] = 0; + ns += nsv[i]; + } + + ss = STATFIXLEN + ns; + + /* set size before erroring, so user can know how much is needed */ + /* note that length excludes count field itself */ + PBIT16(p, ss-BIT16SZ); + p += BIT16SZ; + + if(ss > nbuf) + return BIT16SZ; + + PBIT16(p, d->type); + p += BIT16SZ; + PBIT32(p, d->dev); + p += BIT32SZ; + PBIT8(p, d->qid.type); + p += BIT8SZ; + PBIT32(p, d->qid.vers); + p += BIT32SZ; + PBIT64(p, d->qid.path); + p += BIT64SZ; + PBIT32(p, d->mode); + p += BIT32SZ; + PBIT32(p, d->atime); + p += BIT32SZ; + PBIT32(p, d->mtime); + p += BIT32SZ; + PBIT64(p, d->length); + p += BIT64SZ; + + for(i = 0; i < 4; i++){ + ns = nsv[i]; + if(p + ns + BIT16SZ > ebuf) + return 0; + PBIT16(p, ns); + p += BIT16SZ; + if(ns) + memmove(p, sv[i], ns); + p += ns; + } + + if(ss != p - buf) + return 0; + + return p - buf; +} diff --git a/a/convM2D.c b/a/convM2D.c @@ -0,0 +1,94 @@ +#include "u.h" +#include "lib.h" +#include "fcall.h" + +int +statcheck(uchar *buf, uint nbuf) +{ + uchar *ebuf; + int i; + + ebuf = buf + nbuf; + + if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf)) + return -1; + + buf += STATFIXLEN - 4 * BIT16SZ; + + for(i = 0; i < 4; i++){ + if(buf + BIT16SZ > ebuf) + return -1; + buf += BIT16SZ + GBIT16(buf); + } + + if(buf != ebuf) + return -1; + + return 0; +} + +static char nullstring[] = ""; + +uint +convM2D(uchar *buf, uint nbuf, Dir *d, char *strs) +{ + uchar *p, *ebuf; + char *sv[4]; + int i, ns; + + if(nbuf < STATFIXLEN) + return 0; + + p = buf; + ebuf = buf + nbuf; + + p += BIT16SZ; /* ignore size */ + d->type = GBIT16(p); + p += BIT16SZ; + d->dev = GBIT32(p); + p += BIT32SZ; + d->qid.type = GBIT8(p); + p += BIT8SZ; + d->qid.vers = GBIT32(p); + p += BIT32SZ; + d->qid.path = GBIT64(p); + p += BIT64SZ; + d->mode = GBIT32(p); + p += BIT32SZ; + d->atime = GBIT32(p); + p += BIT32SZ; + d->mtime = GBIT32(p); + p += BIT32SZ; + d->length = GBIT64(p); + p += BIT64SZ; + + for(i = 0; i < 4; i++){ + if(p + BIT16SZ > ebuf) + return 0; + ns = GBIT16(p); + p += BIT16SZ; + if(p + ns > ebuf) + return 0; + if(strs){ + sv[i] = strs; + memmove(strs, p, ns); + strs += ns; + *strs++ = '\0'; + } + p += ns; + } + + if(strs){ + d->name = sv[0]; + d->uid = sv[1]; + d->gid = sv[2]; + d->muid = sv[3]; + }else{ + d->name = nullstring; + d->uid = nullstring; + d->gid = nullstring; + d->muid = nullstring; + } + + return p - buf; +} diff --git a/a/convM2S.c b/a/convM2S.c @@ -0,0 +1,315 @@ +#include "u.h" +#include "lib.h" +#include "fcall.h" + +static +uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ - 1; + if(p+n+1 > ep) + return nil; + /* move it down, on top of count, to make room for '\0' */ + memmove(p, p + 1, n); + p[n] = '\0'; + *s = (char*)p; + p += n+1; + return p; +} + +static +uchar* +gqid(uchar *p, uchar *ep, Qid *q) +{ + if(p+QIDSZ > ep) + return nil; + q->type = GBIT8(p); + p += BIT8SZ; + q->vers = GBIT32(p); + p += BIT32SZ; + q->path = GBIT64(p); + p += BIT64SZ; + return p; +} + +/* + * no syntactic checks. + * three causes for error: + * 1. message size field is incorrect + * 2. input buffer too short for its own data (counts too long, etc.) + * 3. too many names or qids + * gqid() and gstring() return nil if they would reach beyond buffer. + * main switch statement checks range and also can fall through + * to test at end of routine. + */ +uint +convM2S(uchar *ap, uint nap, Fcall *f) +{ + uchar *p, *ep; + uint i, size; + + p = ap; + ep = p + nap; + + if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep) + return 0; + size = GBIT32(p); + p += BIT32SZ; + + if(size < BIT32SZ+BIT8SZ+BIT16SZ) + return 0; + + f->type = GBIT8(p); + p += BIT8SZ; + f->tag = GBIT16(p); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Tflush: + if(p+BIT16SZ > ep) + return 0; + f->oldtag = GBIT16(p); + p += BIT16SZ; + break; + + case Tauth: + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Tattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Twalk: + if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->newfid = GBIT32(p); + p += BIT32SZ; + f->nwname = GBIT16(p); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++){ + p = gstring(p, ep, &f->wname[i]); + if(p == nil) + break; + } + break; + + case Topen: + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tcreate: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tread: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Twrite: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Tclunk: + case Tremove: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Tstat: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Twstat: + if(p+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + +/* + */ + case Rversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Rerror: + p = gstring(p, ep, &f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = gqid(p, ep, &f->aqid); + if(p == nil) + break; + break; + + case Rattach: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + break; + + case Rwalk: + if(p+BIT16SZ > ep) + return 0; + f->nwqid = GBIT16(p); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++){ + p = gqid(p, ep, &f->wqid[i]); + if(p == nil) + break; + } + break; + + case Ropen: + case Rcreate: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + if(p+BIT32SZ > ep) + return 0; + f->iounit = GBIT32(p); + p += BIT32SZ; + break; + + case Rread: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Rwrite: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + if(p+BIT16SZ > ep) + return 0; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + + case Rwstat: + break; + } + + if(p==nil || p>ep) + return 0; + if(ap+size == p) + return size; + return 0; +} diff --git a/a/convS2M.c b/a/convS2M.c @@ -0,0 +1,386 @@ +#include "u.h" +#include "lib.h" +#include "fcall.h" + +static +uchar* +pstring(uchar *p, char *s) +{ + uint n; + + if(s == nil){ + PBIT16(p, 0); + p += BIT16SZ; + return p; + } + + n = strlen(s); + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} + +static +uchar* +pqid(uchar *p, Qid *q) +{ + PBIT8(p, q->type); + p += BIT8SZ; + PBIT32(p, q->vers); + p += BIT32SZ; + PBIT64(p, q->path); + p += BIT64SZ; + return p; +} + +static +uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +uint +sizeS2M(Fcall *f) +{ + uint n; + int i; + + n = 0; + n += BIT32SZ; /* size */ + n += BIT8SZ; /* type */ + n += BIT16SZ; /* tag */ + + switch(f->type) + { + default: + return 0; + + case Tversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Tflush: + n += BIT16SZ; + break; + + case Tauth: + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Tattach: + n += BIT32SZ; + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Twalk: + n += BIT32SZ; + n += BIT32SZ; + n += BIT16SZ; + for(i=0; i<f->nwname; i++) + n += stringsz(f->wname[i]); + break; + + case Topen: + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tcreate: + n += BIT32SZ; + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tread: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + + case Twrite: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + n += f->count; + break; + + case Tclunk: + case Tremove: + n += BIT32SZ; + break; + + case Tstat: + n += BIT32SZ; + break; + + case Twstat: + n += BIT32SZ; + n += BIT16SZ; + n += f->nstat; + break; +/* + */ + + case Rversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Rerror: + n += stringsz(f->ename); + break; + + case Rflush: + break; + + case Rauth: + n += QIDSZ; + break; + + case Rattach: + n += QIDSZ; + break; + + case Rwalk: + n += BIT16SZ; + n += f->nwqid*QIDSZ; + break; + + case Ropen: + case Rcreate: + n += QIDSZ; + n += BIT32SZ; + break; + + case Rread: + n += BIT32SZ; + n += f->count; + break; + + case Rwrite: + n += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + n += BIT16SZ; + n += f->nstat; + break; + + case Rwstat: + break; + } + return n; +} + +uint +convS2M(Fcall *f, uchar *ap, uint nap) +{ + uchar *p; + uint i, size; + + size = sizeS2M(f); + if(size == 0) + return 0; + if(size > nap) + return 0; + + p = (uchar*)ap; + + PBIT32(p, size); + p += BIT32SZ; + PBIT8(p, f->type); + p += BIT8SZ; + PBIT16(p, f->tag); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Tflush: + PBIT16(p, f->oldtag); + p += BIT16SZ; + break; + + case Tauth: + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Tattach: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Twalk: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->newfid); + p += BIT32SZ; + PBIT16(p, f->nwname); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++) + p = pstring(p, f->wname[i]); + break; + + case Topen: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tcreate: + PBIT32(p, f->fid); + p += BIT32SZ; + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tread: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Twrite: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Tclunk: + case Tremove: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Tstat: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Twstat: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; +/* + */ + + case Rversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Rerror: + p = pstring(p, f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = pqid(p, &f->aqid); + break; + + case Rattach: + p = pqid(p, &f->qid); + break; + + case Rwalk: + PBIT16(p, f->nwqid); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++) + p = pqid(p, &f->wqid[i]); + break; + + case Ropen: + case Rcreate: + p = pqid(p, &f->qid); + PBIT32(p, f->iounit); + p += BIT32SZ; + break; + + case Rread: + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Rwrite: + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; + + case Rwstat: + break; + } + if(size != p-ap) + return 0; + return size; +} diff --git a/a/cursor.h b/a/cursor.h @@ -0,0 +1,7 @@ + +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; diff --git a/a/dat.ed b/a/dat.ed @@ -0,0 +1,100 @@ +/BIOS32ci/,/BIOS32ci;/d +/^#define up/c +#define up (machp[0]->externup) +. +v/typedef/ s!Lock;!Lock lk;!g +/^struct PMMU/+2;/^}/-1c + struct vxproc *vxproc; // Plan 9 VX + struct vxmem vxmem; + struct vxmmap vxmm; // Plan 9 VX + Uspace *us; + uchar *uzero; +. +g/mmufree/d +g/mmuused/d +g/lastkmap/d +g/kmaptable/d +g/nkmap/d +/ Mach \*m/s/m/m_/ +/^struct Label/+2;/^}/-1c + uint64 bp; // Plan 9 VX + uint64 bx; + uint64 si; + uint64 di; + uint64 sp; + uint64 pc; + uint64 r12; + uint64 r13; + uint64 r14; + uint64 r15; +. +g/BIOS32ci/d +1i +#include <ucontext.h> +#include "libvx32/vx32.h" + +. +/ ulong\* pdb/d +/ Tss\* tss/d +/ Proc\* externup/d +/ ulong ticks/d +/ Lock alarmlock/d +/ void\* alarm/d +/ int inclockintr/d +/ int loopconst/d +g/^ vlong mtrr/d +/ int stack\[1\]/i + int spl; // Plan 9 VX + void *sigstack; +. +/^extern Mach \*m/c +#ifdef TLS + extern __thread Mach *m; // Plan 9 VX + extern __thread Proc *up; // Plan 9 VX +# define thismach m +# define setmach(x) (m = (x)) +#else + extern Mach *getmach(void); + extern void setmach(Mach*); +# define up getmach()->externup +# ifdef WANT_M +# define m getmach() +# endif +#endif +. +/^struct Mach/+1a +#ifndef TLS + Proc* externup; +#endif + int new; +. +/^struct PMMU/i +typedef struct Uspace Uspace; +. +/^#define up/d +$a + +// Plan 9 VX +extern int traceprocs; +extern int tracesyscalls; +extern int doabort; + +/* Pthreads-based sleep and wakeup. */ +typedef struct Psleep Psleep; +typedef struct Pwaiter Pwaiter; +struct Psleep +{ + int init; + pthread_mutex_t mutex; + Pwaiter *waiter; +}; + +struct Uspace +{ + Proc *p; // proc currently mapped + uchar *uzero; + ulong lo; + ulong hi; +}; +. +g/^ u32int e..;$/d diff --git a/a/dat.h b/a/dat.h @@ -0,0 +1,369 @@ +#include <ucontext.h> + +typedef struct BIOS32si BIOS32si; +typedef struct Conf Conf; +typedef struct Confmem Confmem; +typedef struct FPsave FPsave; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct MMU MMU; +typedef struct Mach Mach; +typedef struct Notsave Notsave; +typedef struct PCArch PCArch; +typedef struct Pcidev Pcidev; +typedef struct PCMmap PCMmap; +typedef struct PCMslot PCMslot; +typedef struct Page Page; +typedef struct PMMU PMMU; +typedef struct Proc Proc; +typedef struct Segdesc Segdesc; +typedef vlong Tval; +typedef struct Ureg Ureg; +typedef struct Vctl Vctl; + + +#define MAXSYSARG 5 /* for mount(fd, afd, mpt, flag, arg) */ + +/* + * parameters for sysproc.c + */ +#define AOUT_MAGIC (I_MAGIC) + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + Proc *p; + Mach *m_; + ushort isilock; + long lockcycles; +}; + +struct Label +{ + uintptr sp; + uintptr pc; +}; + +/* + * FPsave.status + */ +enum +{ + /* this is a state */ + FPinit= 0, + FPactive= 1, + FPinactive= 2, + + /* the following is a bit that can be or'd into the state */ + FPillegal= 0x100, +}; + +struct FPsave +{ + ushort control; + ushort r1; + ushort status; + ushort r2; + ushort tag; + ushort r3; + ulong pc; + ushort selector; + ushort r4; + ulong operand; + ushort oselector; + ushort r5; + uchar regs[80]; /* floating point registers */ +}; + +struct Confmem +{ + ulong base; + ulong npage; + ulong kbase; + ulong klimit; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong monitor; /* has monitor? */ + Confmem mem[4]; /* physical memory */ + ulong npage; /* total physical pages of memory */ + ulong upages; /* user page pool */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ +}; + +/* + * MMU stuff in proc + */ +#define NCOLOR 1 +typedef struct Uspace Uspace; +struct PMMU +{ + /* XXXduncan: vx + struct vxproc *vxproc; // Plan 9 VX + struct vxmem vxmem; + struct vxmmap vxmm; // Plan 9 VX + */ + Uspace *us; + uchar *uzero; +}; + +/* + * things saved in the Proc structure during a notify + */ +struct Notsave +{ + ulong svflags; + ulong svcs; + ulong svss; +}; + +#include "portdat.h" + +typedef struct { + ulong link; /* link (old TSS selector) */ + ulong esp0; /* privilege level 0 stack pointer */ + ulong ss0; /* privilege level 0 stack selector */ + ulong esp1; /* privilege level 1 stack pointer */ + ulong ss1; /* privilege level 1 stack selector */ + ulong esp2; /* privilege level 2 stack pointer */ + ulong ss2; /* privilege level 2 stack selector */ + ulong xcr3; /* page directory base register - not used because we don't use trap gates */ + ulong eip; /* instruction pointer */ + ulong eflags; /* flags register */ + ulong eax; /* general registers */ + ulong ecx; + ulong edx; + ulong ebx; + ulong esp; + ulong ebp; + ulong esi; + ulong edi; + ulong es; /* segment selectors */ + ulong cs; + ulong ss; + ulong ds; + ulong fs; + ulong gs; + ulong ldt; /* selector for task's LDT */ + ulong iomap; /* I/O map base address + T-bit */ +} Tss; + +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +struct Mach +{ +#ifndef TLS + Proc* externup; +#endif + int new; + int machno; /* physical id of processor (KNOWN TO ASSEMBLY) */ + ulong splpc; /* pc of last caller to splhi */ + + Segdesc *gdt; /* gdt for this processor */ + + Proc* proc; /* current process on this processor */ + + Page* pdbpool; + int pdbcnt; + + Label sched; /* scheduler wakeup */ + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + int tlbfault; + int tlbpurge; + int pfault; + int cs; + int syscall; + int load; + int intr; + int flushmmu; /* make current proc flush it's mmu state */ + int ilockdepth; + Perf perf; /* performance counters */ + + ulong spuriousintr; + int lastintr; + + + Lock apictimerlock; + int cpumhz; + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + uvlong cpuhz; + int cpuidax; + int cpuiddx; + char cpuidid[16]; + char* cpuidtype; + int havetsc; + int havepge; + uvlong tscticks; + int pdballoc; + int pdbfree; + + + int spl; // Plan 9 VX + void *sigstack; + int stack[1]; +}; + +/* + * KMap the structure doesn't exist, but the functions do. + */ +typedef struct KMap KMap; +#define VA(k) ((void*)(k)) +KMap* kmap(Page*); +void kunmap(KMap*); + +struct +{ + Lock lk; + int machs; /* bitmap of active CPUs */ + int exiting; /* shutdown */ + int ispanic; /* shutdown in response to a panic */ + int thunderbirdsarego; /* lets the added processors continue to schedinit */ +}active; + +/* + * routines for things outside the PC model, like power management + */ +struct PCArch +{ + char* id; + int (*ident)(void); /* this should be in the model */ + void (*reset)(void); /* this should be in the model */ + int (*serialpower)(int); /* 1 == on, 0 == off */ + int (*modempower)(int); /* 1 == on, 0 == off */ + + void (*intrinit)(void); + int (*intrenable)(Vctl*); + int (*intrvecno)(int); + int (*intrdisable)(int); + void (*introff)(void); + void (*intron)(void); + + void (*clockenable)(void); + uvlong (*fastclock)(uvlong*); + void (*timerset)(uvlong); +}; + +/* cpuid instruction result register bits */ +enum { + /* dx */ + Fpuonchip = 1<<0, +// Pse = 1<<3, /* page size extensions */ + Tsc = 1<<4, /* time-stamp counter */ + Cpumsr = 1<<5, /* model-specific registers, rdmsr/wrmsr */ + Pae = 1<<6, /* physical-addr extensions */ + Mce = 1<<7, /* machine-check exception */ + Cmpxchg8b = 1<<8, + Cpuapic = 1<<9, + Mtrr = 1<<12, /* memory-type range regs. */ + Pge = 1<<13, /* page global extension */ +// Pse2 = 1<<17, /* more page size extensions */ + Clflush = 1<<19, + Mmx = 1<<23, + Sse = 1<<25, /* thus sfence instr. */ + Sse2 = 1<<26, /* thus mfence & lfence instr.s */ +}; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + int irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +extern PCArch *arch; /* PC architecture */ + +/* + * Each processor sees its own Mach structure at address MACHADDR. + * However, the Mach structures must also be available via the per-processor + * MMU information array machp, mainly for disambiguation and access to + * the clock which is only maintained by the bootstrap processor (0). + */ +Mach* machp[MAXMACH]; + +#define MACHP(n) (machp[n]) + +#ifdef TLS + extern __thread Mach *m; // Plan 9 VX + extern __thread Proc *up; // Plan 9 VX +# define thismach m +# define setmach(x) (m = (x)) +#else + extern Mach *getmach(void); + extern void setmach(Mach*); +# define up getmach()->externup +# ifdef WANT_M +# define m getmach() +# endif +#endif + +/* + * hardware info about a device + */ +typedef struct { + ulong port; + int size; +} Devport; + +struct DevConf +{ + ulong intnum; /* interrupt number */ + char *type; /* card type, malloced */ + int nports; /* Number of ports */ + Devport *ports; /* The ports themselves */ +}; + + +// Plan 9 VX +extern int traceprocs; +extern int tracesyscalls; +extern int doabort; + +/* Pthreads-based sleep and wakeup. */ +typedef struct Psleep Psleep; +typedef struct Pwaiter Pwaiter; +struct Psleep +{ + int init; + pthread_mutex_t mutex; + Pwaiter *waiter; +}; + +struct Uspace +{ + Proc *p; // proc currently mapped + uchar *uzero; + ulong lo; + ulong hi; +}; diff --git a/a/dev.c b/a/dev.c @@ -0,0 +1,449 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern ulong kerndate; + +void +mkqid(Qid *q, vlong path, ulong vers, int type) +{ + q->type = type; + q->vers = vers; + q->path = path; +} + +int +devno(int c, int user) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) { + if(devtab[i]->dc == c) + return i; + } + if(user == 0) + panic("devno %C %#ux", c, c); + + return -1; +} + +void +devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db) +{ + db->name = n; + if(c->flag&CMSG) + qid.type |= QTMOUNT; + db->qid = qid; + db->type = devtab[c->type]->dc; + db->dev = c->dev; + db->mode = perm; + db->mode |= qid.type << 24; + db->atime = seconds(); + db->mtime = kerndate; + db->length = length; + db->uid = user; + db->gid = eve; + db->muid = user; +} + +/* + * (here, Devgen is the prototype; devgen is the function in dev.c.) + * + * a Devgen is expected to return the directory entry for ".." + * if you pass it s==DEVDOTDOT (-1). otherwise... + * + * there are two contradictory rules. + * + * (i) if c is a directory, a Devgen is expected to list its children + * as you iterate s. + * + * (ii) whether or not c is a directory, a Devgen is expected to list + * its siblings as you iterate s. + * + * devgen always returns the list of children in the root + * directory. thus it follows (i) when c is the root and (ii) otherwise. + * many other Devgens follow (i) when c is a directory and (ii) otherwise. + * + * devwalk assumes (i). it knows that devgen breaks (i) + * for children that are themselves directories, and explicitly catches them. + * + * devstat assumes (ii). if the Devgen in question follows (i) + * for this particular c, devstat will not find the necessary info. + * with our particular Devgen functions, this happens only for + * directories, so devstat makes something up, assuming + * c->name, c->qid, eve, DMDIR|0555. + * + * devdirread assumes (i). the callers have to make sure + * that the Devgen satisfies (i) for the chan being read. + */ +/* + * the zeroth element of the table MUST be the directory itself for .. +*/ +int +devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + if(tab == 0) + return -1; + if(i == DEVDOTDOT){ + /* nothing */ + }else if(name){ + for(i=1; i<ntab; i++) + if(strcmp(tab[i].name, name) == 0) + break; + if(i==ntab) + return -1; + tab += i; + }else{ + /* skip over the first element, that for . itself */ + i++; + if(i >= ntab) + return -1; + tab += i; + } + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +void +devreset(void) +{ +} + +void +devinit(void) +{ +} + +void +devshutdown(void) +{ +} + +Chan* +devattach(int tc, char *spec) +{ + int n; + Chan *c; + char *buf; + + c = newchan(); + mkqid(&c->qid, 0, 0, QTDIR); + c->type = devno(tc, 0); + if(spec == nil) + spec = ""; + n = 1+UTFmax+strlen(spec)+1; + buf = smalloc(n); + snprint(buf, n, "#%C%s", tc, spec); + c->path = newpath(buf); + free(buf); + return c; +} + + +Chan* +devclone(Chan *c) +{ + Chan *nc; + + if(c->flag & COPEN) + panic("clone of open file type %C\n", devtab[c->type]->dc); + + nc = newchan(); + + nc->type = c->type; + nc->dev = c->dev; + nc->mode = c->mode; + nc->qid = c->qid; + nc->offset = c->offset; + nc->umh = nil; + nc->aux = c->aux; + nc->mqid = c->mqid; + nc->mcp = c->mcp; + return nc; +} + +Walkqid* +devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) +{ + int i, j, alloc; + Walkqid *wq; + char *n; + Dir dir; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + goto Done; + } + n = name[j]; + if(strcmp(n, ".") == 0){ + Accept: + wq->qid[wq->nqid++] = nc->qid; + continue; + } + if(strcmp(n, "..") == 0){ + if((*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir) != 1){ + print("devgen walk .. in dev%s %llux broken\n", + devtab[nc->type]->name, nc->qid.path); + error("broken devgen"); + } + nc->qid = dir.qid; + goto Accept; + } + /* + * Ugly problem: If we're using devgen, make sure we're + * walking the directory itself, represented by the first + * entry in the table, and not trying to step into a sub- + * directory of the table, e.g. /net/net. Devgen itself + * should take care of the problem, but it doesn't have + * the necessary information (that we're doing a walk). + */ + if(gen==devgen && nc->qid.path!=tab[0].qid.path) + goto Notfound; + for(i=0;; i++) { + switch((*gen)(nc, n, tab, ntab, i, &dir)){ + case -1: + Notfound: + if(j == 0) + error(Enonexist); + kstrcpy(up->errstr, Enonexist, ERRMAX); + goto Done; + case 0: + continue; + case 1: + if(strcmp(n, dir.name) == 0){ + nc->qid = dir.qid; + goto Accept; + } + continue; + } + } + } + /* + * We processed at least one name, so will return some data. + * If we didn't process all nname entries succesfully, we drop + * the cloned channel and return just the Qids of the walks. + */ +Done: + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* attach cloned channel to same device */ + wq->clone->type = c->type; + } + return wq; +} + +int +devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + char *p, *elem; + + for(i=0;; i++){ + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + if(c->qid.type & QTDIR){ + if(c->path == nil) + elem = "???"; + else if(strcmp(c->path->s, "/") == 0) + elem = "/"; + else + for(elem=p=c->path->s; *p; p++) + if(*p == '/') + elem = p+1; + devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir); + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + + error(Enonexist); + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + if(c->flag&CMSG) + dir.mode |= DMMOUNT; + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + break; + } + } +} + +long +devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen) +{ + long m, dsz; + Dir dir; + + for(m=0; m<n; c->dri++) { + switch((*gen)(c, nil, tab, ntab, c->dri, &dir)){ + case -1: + return m; + + case 0: + break; + + case 1: + dsz = convD2M(&dir, (uchar*)d, n-m); + if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */ + if(m == 0) + error(Eshort); + return m; + } + m += dsz; + d += dsz; + break; + } + } + + return m; +} + +/* + * error(Eperm) if open permission not granted for up->user. + */ +void +devpermcheck(char *fileuid, ulong perm, int omode) +{ + ulong t; + static int access[] = { 0400, 0200, 0600, 0100 }; + + if(strcmp(up->user, fileuid) == 0) + perm <<= 0; + else + if(strcmp(up->user, eve) == 0) + perm <<= 3; + else + perm <<= 6; + + t = access[omode&3]; + if((t&perm) != t) + error(Eperm); +} + +Chan* +devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + + for(i=0;; i++) { + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + goto Return; + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + devpermcheck(dir.uid, dir.mode, omode); + goto Return; + } + break; + } + } +Return: + c->offset = 0; + if((c->qid.type&QTDIR) && omode!=OREAD) + error(Eperm); + c->mode = openmode(omode); + c->flag |= COPEN; + return c; +} + +void +devcreate(Chan *c, char *name, int mode, ulong perm) +{ + error(Eperm); +} + +Block* +devbread(Chan *c, long n, ulong offset) +{ + Block *bp; + + bp = allocb(n); + if(bp == 0) + error(Enomem); + if(waserror()) { + freeb(bp); + nexterror(); + } + bp->wp += devtab[c->type]->read(c, bp->wp, n, offset); + poperror(); + return bp; +} + +long +devbwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + + if(waserror()) { + freeb(bp); + nexterror(); + } + n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset); + poperror(); + freeb(bp); + + return n; +} + +void +devremove(Chan *c) +{ + error(Eperm); +} + +int +devwstat(Chan *c, uchar *stat, int nstat) +{ + error(Eperm); + return 0; +} + +void +devpower(int toggle) +{ + error(Eperm); +} + +int +devconfig(int toggle, char *name, DevConf *conf) +{ + error(Eperm); + return 0; +} diff --git a/a/dev.ed b/a/dev.ed @@ -0,0 +1,6 @@ +g/^decreate/ s/(Chan\*, char\*, int, ulong)/(Chan *c, char *name, int mode, ulong perm)/ +g/^devremove/ s/(Chan\*)/(Chan *c)/ +g/^devwstat/ s/(Chan\*, uchar\*, int)/(Chan *c, uchar *stat, int nstat)/ +g/^devpower/ s/(int)/(int toggle)/ +g/^devconfig/ s/(int, char \*, DevConf \*)/(int toggle, char *name, DevConf *conf)/ +g/^devcreate/ s/(Chan\*, char\*, int, ulong)/(Chan *c, char *name, int mode, ulong perm)/ diff --git a/a/devcap.c b/a/devcap.c @@ -0,0 +1,286 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "libsec.h" + +enum +{ + Hashlen= SHA1dlen, + Maxhash= 256, +}; + +/* + * if a process knows cap->cap, it can change user + * to capabilty->user. + */ +typedef struct Caphash Caphash; +struct Caphash +{ + Caphash *next; + char hash[Hashlen]; + ulong ticks; +}; + +struct +{ + QLock l; + Caphash *first; + int nhash; +} capalloc; + +enum +{ + Qdir, + Qhash, + Quse, +}; + +/* caphash must be last */ +Dirtab capdir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "capuse", {Quse}, 0, 0222, + "caphash", {Qhash}, 0, 0200, +}; +int ncapdir = nelem(capdir); + +static Chan* +capattach(char *spec) +{ + return devattach(L'¤', spec); +} + +static Walkqid* +capwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, capdir, ncapdir, devgen); +} + +static void +capremove(Chan *c) +{ + if(iseve() && c->qid.path == Qhash) + ncapdir = nelem(capdir)-1; + else + error(Eperm); +} + + +static int +capstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, capdir, ncapdir, devgen); +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +capopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + switch((ulong)c->qid.path){ + case Qhash: + if(!iseve()) + error(Eperm); + break; + } + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +/* +static char* +hashstr(uchar *hash) +{ + static char buf[2*Hashlen+1]; + int i; + + for(i = 0; i < Hashlen; i++) + sprint(buf+2*i, "%2.2ux", hash[i]); + buf[2*Hashlen] = 0; + return buf; +} + */ + +static Caphash* +remcap(uchar *hash) +{ + Caphash *t, **l; + + qlock(&capalloc.l); + + /* find the matching capability */ + for(l = &capalloc.first; *l != nil;){ + t = *l; + if(memcmp(hash, t->hash, Hashlen) == 0) + break; + l = &t->next; + } + t = *l; + if(t != nil){ + capalloc.nhash--; + *l = t->next; + } + qunlock(&capalloc.l); + + return t; +} + +/* add a capability, throwing out any old ones */ +static void +addcap(uchar *hash) +{ + Caphash *p, *t, **l; + + p = smalloc(sizeof *p); + memmove(p->hash, hash, Hashlen); + p->next = nil; + p->ticks = msec(); + + qlock(&capalloc.l); + + /* trim extras */ + while(capalloc.nhash >= Maxhash){ + t = capalloc.first; + if(t == nil) + panic("addcap"); + capalloc.first = t->next; + free(t); + capalloc.nhash--; + } + + /* add new one */ + for(l = &capalloc.first; *l != nil; l = &(*l)->next) + ; + *l = p; + capalloc.nhash++; + + qunlock(&capalloc.l); +} + +static void +capclose(Chan *c) +{ +} + +static long +capread(Chan *c, void *va, long n, vlong vl) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, capdir, ncapdir, devgen); + + default: + error(Eperm); + break; + } + return n; +} + +static long +capwrite(Chan *c, void *va, long n, vlong vl) +{ + Caphash *p; + char *cp; + uchar hash[Hashlen]; + char *key, *from, *to; + char err[256]; + + switch((ulong)c->qid.path){ + case Qhash: + if(!iseve()) + error(Eperm); + if(n < Hashlen) + error(Eshort); + memmove(hash, va, Hashlen); + addcap(hash); + break; + + case Quse: + /* copy key to avoid a fault in hmac_xx */ + cp = nil; + if(waserror()){ + free(cp); + nexterror(); + } + cp = smalloc(n+1); + memmove(cp, va, n); + cp[n] = 0; + + from = cp; + key = strrchr(cp, '@'); + if(key == nil) + error(Eshort); + *key++ = 0; + + hmac_sha1((uchar*)from, strlen(from), (uchar*)key, strlen(key), hash, nil); + + p = remcap(hash); + if(p == nil){ + snprint(err, sizeof err, "invalid capability %s@%s", from, key); + error(err); + } + + /* if a from user is supplied, make sure it matches */ + to = strchr(from, '@'); + if(to == nil){ + to = from; + } else { + *to++ = 0; + if(strcmp(from, up->user) != 0) + error("capability must match user"); + } + + /* set user id */ + kstrdup(&up->user, to); + up->basepri = PriNormal; + + free(p); + free(cp); + poperror(); + break; + + default: + error(Eperm); + break; + } + + return n; +} + +Dev capdevtab = { + L'¤', + "cap", + + devreset, + devinit, + devshutdown, + capattach, + capwalk, + capstat, + capopen, + devcreate, + capclose, + capread, + devbread, + capwrite, + devbwrite, + capremove, + devwstat +}; diff --git a/a/devcap.ed b/a/devcap.ed @@ -0,0 +1,6 @@ +/^capwrite/ s/vlong/vlong vl/ +/^capread/ s/vlong/vlong vl/ +/^capclose/ s/Chan\*/Chan *c/ +,s/m->ticks/msec()/ +,s/QLock/QLock l/ +g/lock/ s/capalloc/capalloc.l/ diff --git a/a/devcons.c b/a/devcons.c @@ -0,0 +1,1359 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "authsrv.h" + +void (*consdebug)(void) = nil; +void (*screenputs)(char*, int) = nil; + +Queue* kbdq; /* unprocessed console input */ +Queue* lineq; /* processed console input */ +Queue* kprintoq; /* console output, for /dev/kprint */ +ulong kprintinuse; /* test and set whether /dev/kprint is open */ +int iprintscreenputs = 1; + +int panicking; + +static struct +{ + QLock lk; + + int raw; /* true if we shouldn't process input */ + Ref ctl; /* number of opens to the control file */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + int count; + int ctlpoff; + + /* a place to save up characters at interrupt time before dumping them in the queue */ + Lock lockputc; + char istage[1024]; + char *iw; + char *ir; + char *ie; +} kbd = { + .iw = kbd.istage, + .ir = kbd.istage, + .ie = kbd.istage + sizeof(kbd.istage), +}; + +char *sysname; +vlong fasthz = 1000000000ULL; // Plan 9 VX = nsecs + +static void seedrand(void); +static int readtime(ulong, char*, int); +static int readbintime(char*, int); +static int writetime(char*, int); +static int writebintime(char*, int); + +enum +{ + CMhalt, + CMreboot, + CMpanic, +}; + +Cmdtab rebootmsg[] = +{ + CMhalt, "halt", 1, + CMreboot, "reboot", 0, + CMpanic, "panic", 0, +}; + +void +printinit(void) +{ + lineq = qopen(2*1024, 0, nil, nil); + if(lineq == nil) + panic("printinit"); + qnoblock(lineq, 1); +} + +#if 0 // Plan 9 VX +int +consactive(void) +{ + if(serialoq) + return qlen(serialoq) > 0; + return 0; +} +#endif + +#if 0 // Plan 9 VX +void +prflush(void) +{ + ulong now; + + now = m->ticks; + while(consactive()) + if(m->ticks - now >= HZ) + break; +} +#endif + +/* + * Log console output so it can be retrieved via /dev/kmesg. + * This is good for catching boot-time messages after the fact. + */ +struct { + Lock lk; + char buf[16384]; + uint n; +} kmesg; + +static void +kmesgputs(char *str, int n) +{ + uint nn, d; + + ilock(&kmesg.lk); + /* take the tail of huge writes */ + if(n > sizeof kmesg.buf){ + d = n - sizeof kmesg.buf; + str += d; + n -= d; + } + + /* slide the buffer down to make room */ + nn = kmesg.n; + if(nn + n >= sizeof kmesg.buf){ + d = nn + n - sizeof kmesg.buf; + if(d) + memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d); + nn -= d; + } + + /* copy the data in */ + memmove(kmesg.buf+nn, str, n); + nn += n; + kmesg.n = nn; + iunlock(&kmesg.lk); +} + +/* + * Print a string on the console. Convert \n to \r\n for serial + * line consoles. Locking of the queues is left up to the screen + * or uart code. Multi-line messages to serial consoles may get + * interspersed with other messages. + */ +static void +putstrn0(char *str, int n, int usewrite) +{ + + if(!islo()) + usewrite = 0; + + /* + * how many different output devices do we need? + */ + kmesgputs(str, n); + + /* + * if someone is reading /dev/kprint, + * put the message there. + * if not and there's an attached bit mapped display, + * put the message there. + * + * if there's a serial line being used as a console, + * put the message there. + */ + if(kprintoq != nil && !qisclosed(kprintoq)){ + if(usewrite) + qwrite(kprintoq, str, n); + else + qiwrite(kprintoq, str, n); + }else if(screenputs != nil) + screenputs(str, n); + + uartputs(str, n); +#if 0 // Plan 9 VX + if(serialoq == nil){ + uartputs(str, n); + return; + } + + while(n > 0) { + t = memchr(str, '\n', n); + if(t && !kbd.raw) { + m = t-str; + if(usewrite){ + qwrite(serialoq, str, m); + qwrite(serialoq, "\r\n", 2); + } else { + qiwrite(serialoq, str, m); + qiwrite(serialoq, "\r\n", 2); + } + n -= m+1; + str = t+1; + } else { + if(usewrite) + qwrite(serialoq, str, n); + else + qiwrite(serialoq, str, n); + break; + } + } +#endif +} + +void +putstrn(char *str, int n) +{ + putstrn0(str, n, 0); +} + +int noprint; + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + if(noprint) + return -1; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +/* + * Want to interlock iprints to avoid interlaced output on + * multiprocessor, but don't want to deadlock if one processor + * dies during print and another has something important to say. + * Make a good faith effort. + */ +#if 0 // Plan 9 VX +static Lock iprintlock; +static int +iprintcanlock(Lock *l) +{ + int i; + + for(i=0; i<1000; i++){ + if(canlock(l)) + return 1; + if(l->m == MACHP(m->machno)) + return 0; + microdelay(100); + } + return 0; +} + +int +iprint(char *fmt, ...) +{ + int n, s, locked; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + locked = iprintcanlock(&iprintlock); + if(screenputs != nil && iprintscreenputs) + screenputs(buf, n); + uartputs(buf, n); + if(locked) + unlock(&iprintlock); + splx(s); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + kprintoq = nil; /* don't try to write to /dev/kprint */ + + if(panicking) + for(;;); + panicking = 1; + + s = splhi(); + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + iprint("%s\n", buf); + if(consdebug) + (*consdebug)(); + splx(s); + prflush(); + buf[n] = '\n'; + putstrn(buf, n+1); + dumpstack(); + + restoretty(); exit(1); +} + +/* libmp at least contains a few calls to sysfatal; simulate with panic */ +void +sysfatal(char *fmt, ...) +{ + char err[256]; + va_list arg; + + va_start(arg, fmt); + vseprint(err, err + sizeof err, fmt, arg); + va_end(arg); + panic("sysfatal: %s", err); +} + +void +_assert(char *fmt) +{ + panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt); +} +#endif + +int +pprint(char *fmt, ...) +{ + int n; + Chan *c; + va_list arg; + char buf[2*PRINTSIZE]; + + if(up == nil || up->fgrp == nil) + return 0; + + c = up->fgrp->fd[2]; + if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) + return 0; + n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid); + va_start(arg, fmt); + n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(waserror()) + return 0; + devtab[c->type]->write(c, buf, n, c->offset); + poperror(); + + lock(&c->ref.lk); + c->offset += n; + unlock(&c->ref.lk); + + return n; +} + +static void +echoscreen(char *buf, int n) +{ + char *e, *p; + char ebuf[128]; + int x; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + screenputs(ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + screenputs(ebuf, p - ebuf); +} + +#if 0 // Plan 9 VX +static void +echoserialoq(char *buf, int n) +{ + char *e, *p; + char ebuf[128]; + int x; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + qiwrite(serialoq, ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == '\n'){ + *p++ = '\r'; + *p++ = '\n'; + } else if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + qiwrite(serialoq, ebuf, p - ebuf); +} +#endif + +void +echo(char *buf, int n) +{ + static int ctrlt; + int x; + char *e, *p; + + if(n == 0) + return; + + e = buf+n; + for(p = buf; p < e; p++){ + switch(*p){ + case 0x10: /* ^P */ + if(cpuserver && !kbd.ctlpoff){ + active.exiting = 1; + return; + } + break; + case 0x14: /* ^T */ + ctrlt++; + if(ctrlt > 2) + ctrlt = 2; + continue; + } + + if(ctrlt != 2) + continue; + + /* ^T escapes */ + ctrlt = 0; + switch(*p){ + case 'S': + x = splhi(); + dumpstack(); + procdump(); + splx(x); + return; + case 's': + dumpstack(); + return; + case 'x': + xsummary(); + ixsummary(); + mallocsummary(); + // memorysummary(); + pagersummary(); + return; + case 'd': + if(consdebug == nil) + consdebug = rdb; + else + consdebug = nil; + print("consdebug now %#p\n", consdebug); + return; + case 'D': + if(consdebug == nil) + consdebug = rdb; + consdebug(); + return; + case 'p': + x = spllo(); + procdump(); + splx(x); + return; + case 'q': + scheddump(); + return; + case 'k': + killbig("^t ^t k"); + return; + case 'r': + restoretty(); exit(0); + return; + } + } + + qproduce(kbdq, buf, n); + if(kbd.raw) + return; + kmesgputs(buf, n); + if(screenputs != nil) + echoscreen(buf, n); + uartecho(buf, n); // Plan 9 VX +} + +/* + * Called by a uart interrupt for console input. + * + * turn '\r' into '\n' before putting it into the queue. + */ +int +kbdcr2nl(Queue *q, int ch) +{ + char *next; + + ilock(&kbd.lockputc); /* just a mutex */ + if(ch == '\r' && !kbd.raw) + ch = '\n'; + next = kbd.iw+1; + if(next >= kbd.ie) + next = kbd.istage; + if(next != kbd.ir){ + *kbd.iw = ch; + kbd.iw = next; + } + iunlock(&kbd.lockputc); + return 0; +} + +/* + * Put character, possibly a rune, into read queue at interrupt time. + * Called at interrupt time to process a character. + */ +int +kbdputc(Queue *q, int ch) +{ + int n; + Rune r; + char buf[UTFmax]; + + r = ch; + n = runetochar(buf, &r); + echo(buf, n); + return 0; + +#if 0 // Plan 9 VX + int i, n; + char buf[3]; + Rune r; + char *next; + + if(kbd.ir == nil) + return 0; /* in case we're not inited yet */ + + ilock(&kbd.lockputc); /* just a mutex */ + r = ch; + n = runetochar(buf, &r); + for(i = 0; i < n; i++){ + next = kbd.iw+1; + if(next >= kbd.ie) + next = kbd.istage; + if(next == kbd.ir) + break; + *kbd.iw = buf[i]; + kbd.iw = next; + } + iunlock(&kbd.lockputc); + return 0; +#endif +} + +/* + * we save up input characters till clock time to reduce + * per character interrupt overhead. + */ +#if 0 // Plan 9 VX +static void +kbdputcclock(void) +{ + char *iw; + + /* this amortizes cost of qproduce */ + if(kbd.iw != kbd.ir){ + iw = kbd.iw; + if(iw < kbd.ir){ + echo(kbd.ir, kbd.ie-kbd.ir); + kbd.ir = kbd.istage; + } + if(kbd.ir != iw){ + echo(kbd.ir, iw-kbd.ir); + kbd.ir = iw; + } + } +} +#endif + +enum{ + Qdir, + Qbintime, + Qcons, + Qconsctl, + Qcputime, + Qdrivers, + Qkmesg, + Qkprint, + Qhostdomain, + Qhostowner, + Qnull, + Qosversion, + Qpgrpid, + Qpid, + Qppid, + Qrandom, + Qreboot, + Qswap, + Qsysname, + Qsysstat, + Qtime, + Quser, + Qzero, +}; + +enum +{ + VLNUMSIZE= 22, +}; + +static Dirtab consdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "bintime", {Qbintime}, 24, 0664, + "cons", {Qcons}, 0, 0660, + "consctl", {Qconsctl}, 0, 0220, + "cputime", {Qcputime}, 6*NUMSIZE, 0444, + "drivers", {Qdrivers}, 0, 0444, + "hostdomain", {Qhostdomain}, DOMLEN, 0664, + "hostowner", {Qhostowner}, 0, 0664, + "kmesg", {Qkmesg}, 0, 0440, + "kprint", {Qkprint, 0, QTEXCL}, 0, DMEXCL|0440, + "null", {Qnull}, 0, 0666, + "osversion", {Qosversion}, 0, 0444, + "pgrpid", {Qpgrpid}, NUMSIZE, 0444, + "pid", {Qpid}, NUMSIZE, 0444, + "ppid", {Qppid}, NUMSIZE, 0444, + "random", {Qrandom}, 0, 0444, + "reboot", {Qreboot}, 0, 0664, + "swap", {Qswap}, 0, 0664, + "sysname", {Qsysname}, 0, 0664, + "sysstat", {Qsysstat}, 0, 0666, + "time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664, + "user", {Quser}, 0, 0666, + "zero", {Qzero}, 0, 0444, +}; + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + snprint(tmp, sizeof(tmp), "%*lud", size-1, val); + tmp[size-1] = ' '; + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +int +readstr(ulong off, char *buf, ulong n, char *str) +{ + int size; + + size = strlen(str); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, str+off, n); + return n; +} + +static void +consinit(void) +{ + todinit(); + randominit(); +#if 0 // Plan 9 VX + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine + */ + addclock0link(kbdputcclock, 22); +#endif +} + +static Chan* +consattach(char *spec) +{ + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen); +} + +static int +consstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, consdir, nelem(consdir), devgen); +} + +static Chan* +consopen(Chan *c, int omode) +{ + c->aux = nil; + c = devopen(c, omode, consdir, nelem(consdir), devgen); + switch((ulong)c->qid.path){ + case Qconsctl: + incref(&kbd.ctl); + break; + + case Qkprint: + if(tas(&kprintinuse) != 0){ + c->flag &= ~COPEN; + error(Einuse); + } + if(kprintoq == nil){ + kprintoq = qopen(8*1024, Qcoalesce, 0, 0); + if(kprintoq == nil){ + c->flag &= ~COPEN; + error(Enomem); + } + qnoblock(kprintoq, 1); + }else + qreopen(kprintoq); + c->iounit = qiomaxatomic; + break; + } + return c; +} + +static void +consclose(Chan *c) +{ + switch((ulong)c->qid.path){ + /* last close of control file turns off raw */ + case Qconsctl: + if(c->flag&COPEN){ + if(decref(&kbd.ctl) == 0) + kbd.raw = 0; + } + break; + + /* close of kprint allows other opens */ + case Qkprint: + if(c->flag & COPEN){ + kprintinuse = 0; + qhangup(kprintoq, nil); + } + break; + } +} + +static long +consread(Chan *c, void *buf, long n, vlong off) +{ + ulong l; + Mach *mp; + char *b, *bp, ch; + char tmp[256]; /* must be >= 18*NUMSIZE (Qswap) */ + int i, k, id, send; + vlong offset = off; + + if(n <= 0) + return n; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, consdir, nelem(consdir), devgen); + + case Qcons: + qlock(&kbd.lk); + if(waserror()) { + qunlock(&kbd.lk); + nexterror(); + } + while(!qcanread(lineq)){ + if(qread(kbdq, &ch, 1) == 0) + continue; + send = 0; + if(ch == 0){ + /* flush output on rawoff -> rawon */ + if(kbd.x > 0) + send = !qcanread(kbdq); + }else if(kbd.raw){ + kbd.line[kbd.x++] = ch; + send = !qcanread(kbdq); + }else{ + switch(ch){ + case '\b': + if(kbd.x > 0) + kbd.x--; + break; + case 0x15: /* ^U */ + kbd.x = 0; + break; + case '\n': + case 0x04: /* ^D */ + send = 1; + default: + if(ch != 0x04) + kbd.line[kbd.x++] = ch; + break; + } + } + if(send || kbd.x == sizeof kbd.line){ + qwrite(lineq, kbd.line, kbd.x); + kbd.x = 0; + } + } + n = qread(lineq, buf, n); + qunlock(&kbd.lk); + poperror(); + return n; + + case Qcputime: + k = offset; + if(k >= 6*NUMSIZE) + return 0; + if(k+n > 6*NUMSIZE) + n = 6*NUMSIZE - k; + /* easiest to format in a separate buffer and copy out */ + for(i=0; i<6 && NUMSIZE*i<k+n; i++){ + l = up->time[i]; + if(i == TReal) + l = msec() - l; + l = TK2MS(l); + readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE); + } + memmove(buf, tmp+k, n); + return n; + + case Qkmesg: + /* + * This is unlocked to avoid tying up a process + * that's writing to the buffer. kmesg.n never + * gets smaller, so worst case the reader will + * see a slurred buffer. + */ + if(off >= kmesg.n) + n = 0; + else{ + if(off+n > kmesg.n) + n = kmesg.n - off; + memmove(buf, kmesg.buf+off, n); + } + return n; + + case Qkprint: + return qread(kprintoq, buf, n); + + case Qpgrpid: + return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE); + + case Qpid: + return readnum((ulong)offset, buf, n, up->pid, NUMSIZE); + + case Qppid: + return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE); + + case Qtime: + return readtime((ulong)offset, buf, n); + + case Qbintime: + return readbintime(buf, n); + + case Qhostowner: + return readstr((ulong)offset, buf, n, eve); + + case Qhostdomain: + return readstr((ulong)offset, buf, n, hostdomain); + + case Quser: + return readstr((ulong)offset, buf, n, up->user); + + case Qnull: + return 0; + + case Qsysstat: + b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1); /* +1 for NUL */ + bp = b; + for(id = 0; id < 32; id++) { + if(active.machs & (1<<id)) { + mp = MACHP(id); + readnum(0, bp, NUMSIZE, id, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, mp->load, NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, + (mp->perf.avg_inidle*100)/mp->perf.period, + NUMSIZE); + bp += NUMSIZE; + readnum(0, bp, NUMSIZE, + (mp->perf.avg_inintr*100)/mp->perf.period, + NUMSIZE); + bp += NUMSIZE; + *bp++ = '\n'; + } + } + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qswap: + tmp[0] = 0; + + return readstr((ulong)offset, buf, n, tmp); + + case Qsysname: + if(sysname == nil) + return 0; + return readstr((ulong)offset, buf, n, sysname); + + case Qrandom: + return randomread(buf, n); + + case Qdrivers: + b = malloc(READSTR); + if(b == nil) + error(Enomem); + n = 0; + for(i = 0; devtab[i] != nil; i++) + n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qzero: + memset(buf, 0, n); + return n; + + case Qosversion: + snprint(tmp, sizeof tmp, "2000"); + n = readstr((ulong)offset, buf, n, tmp); + return n; + + default: + print("consread %#llux\n", c->qid.path); + error(Egreg); + } + return -1; /* never reached */ +} + +static long +conswrite(Chan *c, void *va, long n, vlong off) +{ + char buf[256], ch; + long l, bp; + char *a; + Mach *mp; + int id, fd; + Chan *swc; + ulong offset; + Cmdbuf *cb; + Cmdtab *ct; + + a = va; + offset = off; + + switch((ulong)c->qid.path){ + case Qcons: + /* + * Can't page fault in putstrn, so copy the data locally. + */ + l = n; + while(l > 0){ + bp = l; + if(bp > sizeof buf) + bp = sizeof buf; + memmove(buf, a, bp); + putstrn0(buf, bp, 1); + a += bp; + l -= bp; + } + break; + + case Qconsctl: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + for(a = buf; a;){ + if(strncmp(a, "rawon", 5) == 0){ + kbd.raw = 1; + /* clumsy hack - wake up reader */ + ch = 0; + qwrite(kbdq, &ch, 1); + } else if(strncmp(a, "rawoff", 6) == 0){ + kbd.raw = 0; + } else if(strncmp(a, "ctlpon", 6) == 0){ + kbd.ctlpoff = 0; + } else if(strncmp(a, "ctlpoff", 7) == 0){ + kbd.ctlpoff = 1; + } + if((a = strchr(a, ' '))) + a++; + } + break; + + case Qtime: + if(!iseve()) + error(Eperm); + return writetime(a, n); + + case Qbintime: + if(!iseve()) + error(Eperm); + return writebintime(a, n); + + case Qhostowner: + return hostownerwrite(a, n); + + case Qhostdomain: + return hostdomainwrite(a, n); + + case Quser: + return userwrite(a, n); + + case Qnull: + break; + + case Qreboot: + if(!iseve()) + error(Eperm); + cb = parsecmd(a, n); + + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg)); + switch(ct->index) { + case CMhalt: + reboot(nil, 0, 0); + break; + case CMreboot: + rebootcmd(cb->nf-1, cb->f+1); + break; + case CMpanic: + *(ulong*)0=0; + panic("/dev/reboot"); + } + poperror(); + free(cb); + break; + + case Qsysstat: + for(id = 0; id < 32; id++) { + if(active.machs & (1<<id)) { + mp = MACHP(id); + mp->cs = 0; + mp->intr = 0; + mp->syscall = 0; + mp->pfault = 0; + mp->tlbfault = 0; + mp->tlbpurge = 0; + } + } + break; + + case Qswap: + if(n >= sizeof buf) + error(Egreg); + memmove(buf, va, n); /* so we can NUL-terminate */ + buf[n] = 0; + /* start a pager if not already started */ + if(strncmp(buf, "start", 5) == 0){ + kickpager(); + break; + } + if(!iseve()) + error(Eperm); + if(buf[0]<'0' || '9'<buf[0]) + error(Ebadarg); + fd = strtoul(buf, 0, 0); + swc = fdtochan(fd, -1, 1, 1); + setswapchan(swc); + break; + + case Qsysname: + if(offset != 0) + error(Ebadarg); + if(n <= 0 || n >= sizeof buf) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&sysname, buf); + break; + + default: + print("conswrite: %#llux\n", c->qid.path); + error(Egreg); + } + return n; +} + +Dev consdevtab = { + 'c', + "cons", + + devreset, + consinit, + devshutdown, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat, +}; + +static ulong randn; + +static void +seedrand(void) +{ + if(!waserror()){ + randomread((void*)&randn, sizeof(randn)); + poperror(); + } +} + +int +nrand(int n) +{ + if(randn == 0) + seedrand(); + randn = randn*1103515245 + 12345 + msec(); + return (randn>>16) % n; +} + +int +rand(void) +{ + nrand(1); + return randn; +} + +static uvlong uvorder = 0x0001020304050607ULL; + +static uchar* +le2vlong(vlong *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[o[i]] = f[i]; + return f+sizeof(vlong); +} + +static uchar* +vlong2le(uchar *t, vlong from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[i] = f[o[i]]; + return t+sizeof(vlong); +} + +static long order = 0x00010203; + +static uchar* +le2long(long *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)&order; + for(i = 0; i < sizeof(long); i++) + t[o[i]] = f[i]; + return f+sizeof(long); +} + +/*static*/ uchar* +long2le(uchar *t, long from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)&order; + for(i = 0; i < sizeof(long); i++) + t[i] = f[o[i]]; + return t+sizeof(long); +} + +char *Ebadtimectl = "bad time control"; + +/* + * like the old #c/time but with added info. Return + * + * secs nanosecs fastticks fasthz + */ +static int +readtime(ulong off, char *buf, int n) +{ + vlong nsec, ticks; + long sec; + char str[7*NUMSIZE]; + + nsec = todget(&ticks); + if(fasthz == 0LL) + fastticks((uvlong*)&fasthz); + sec = nsec/1000000000ULL; + snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ", + NUMSIZE-1, sec, + VLNUMSIZE-1, nsec, + VLNUMSIZE-1, ticks, + VLNUMSIZE-1, fasthz); + return readstr(off, buf, n, str); +} + +/* + * set the time in seconds + */ +static int +writetime(char *buf, int n) +{ + char b[13]; + long i; + vlong now; + + if(n >= sizeof(b)) + error(Ebadtimectl); + strncpy(b, buf, n); + b[n] = 0; + i = strtol(b, 0, 0); + if(i <= 0) + error(Ebadtimectl); + now = i*1000000000LL; + todset(now, 0, 0); + return n; +} + +/* + * read binary time info. all numbers are little endian. + * ticks and nsec are syncronized. + */ +static int +readbintime(char *buf, int n) +{ + int i; + vlong nsec, ticks; + uchar *b = (uchar*)buf; + + i = 0; + if(fasthz == 0LL) + fastticks((uvlong*)&fasthz); + nsec = todget(&ticks); + if(n >= 3*sizeof(uvlong)){ + vlong2le(b+2*sizeof(uvlong), fasthz); + i += sizeof(uvlong); + } + if(n >= 2*sizeof(uvlong)){ + vlong2le(b+sizeof(uvlong), ticks); + i += sizeof(uvlong); + } + if(n >= 8){ + vlong2le(b, nsec); + i += sizeof(vlong); + } + return i; +} + +/* + * set any of the following + * - time in nsec + * - nsec trim applied over some seconds + * - clock frequency + */ +static int +writebintime(char *buf, int n) +{ + uchar *p; + vlong delta; + long period; + + n--; + p = (uchar*)buf + 1; + switch(*buf){ + case 'n': + if(n < sizeof(vlong)) + error(Ebadtimectl); + le2vlong(&delta, p); + todset(delta, 0, 0); + break; + case 'd': + if(n < sizeof(vlong)+sizeof(long)) + error(Ebadtimectl); + p = le2vlong(&delta, p); + le2long(&period, p); + todset(-1, delta, period); + break; + case 'f': + if(n < sizeof(uvlong)) + error(Ebadtimectl); + le2vlong(&fasthz, p); + todsetfreq(fasthz); + break; + } + return n; +} + +// Plan 9 VX +int +tailkmesg(char *a, int n) +{ + ilock(&kmesg.lk); + if(n > kmesg.n) + n = kmesg.n; + memmove(a, kmesg.buf+kmesg.n-n, n); + iunlock(&kmesg.lk); + return n; +} diff --git a/a/devcons.ed b/a/devcons.ed @@ -0,0 +1,97 @@ +g/exit(/ s/exit/restoretty(); &/ +g/"pool.h"/d +,s!QLock;!QLock lk;!g +/vlong fasthz/ s!;! = 1000000000ULL; // Plan 9 VX = nsecs! +,s!lock(c)!lock(\&c->ref.lk)!g +g/^kbd/ s/Queue\*,/Queue *q,/ +,s!lock(\&kbd)!lock(\&kbd.lk)!g +,s!(a = strchr(a, ' '))!(&)!g +,s!MACHP(0)->ticks!msec()!g +/^long2le/-1 s!static!/*static*/! +/static int ctrlt, pid/ s/, pid// +/^ snprint(tmp/,/imagmem/c + tmp[0] = 0; +. +/^Queue\* serialoq/d +/^consactive/-1i +#if 0 // Plan 9 VX +. +/^}/a +#endif +. +/^kbdputcclock/-1i +#if 0 // Plan 9 VX +. +/^}/a +#endif +. +/^prflush/-1i +#if 0 // Plan 9 VX +. +/^}/a +#endif +. +/^putstrn0/;# +/ int m/d +/ char \*t/d +/ if(serialoq == nil)/i + uartputs(str, n); +#if 0 // Plan 9 VX +. +/^}/i +#endif +. +/^static Lock iprintlock/i +#if 0 // Plan 9 VX +. +/^_assert/;# +/^}/a +#endif +. +/^echoserialoq/-1i +#if 0 // Plan 9 VX +. +/^}/a +#endif +. +/^echo(/-1 s/static // +/^ if(serialoq)/d +d +/^kbdputc/+1a + int n; + Rune r; + char buf[UTFmax]; + + r = ch; + n = runetochar(buf, &r); + echo(buf, n); + return 0; + +#if 0 // Plan 9 VX +. +/^}/i +#endif +. +/at 115200 baud/-1i +#if 0 // Plan 9 VX +. +/addclock0link/a +#endif +. +$a + +// Plan 9 VX +int +tailkmesg(char *a, int n) +{ + ilock(&kmesg.lk); + if(n > kmesg.n) + n = kmesg.n; + memmove(a, kmesg.buf+kmesg.n-n, n); + iunlock(&kmesg.lk); + return n; +} +. +/echoscreen(buf, n)/a + uartecho(buf, n); // Plan 9 VX +. diff --git a/a/devdraw.c b/a/devdraw.c @@ -0,0 +1,2270 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#define Image IMAGE +#include "draw.h" +#include "memdraw.h" +#include "memlayer.h" +#include "cursor.h" +#include "screen.h" + +#define blankscreen(x) +#define ishwimage(x) (0) + +enum +{ + Qtopdir = 0, + Qnew, + Qwinname, + Q3rd, + Q2nd, + Qcolormap, + Qctl, + Qdata, + Qrefresh, +}; + +/* + * Qid path is: + * 4 bits of file type (qids above) + * 24 bits of mux slot number +1; 0 means not attached to client + */ +#define QSHIFT 4 /* location in qid of client # */ + +#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) +#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) +#define CLIENT(q) CLIENTPATH((q).path) + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) +#define IOUNIT (64*1024) + +typedef struct Client Client; +typedef struct Draw Draw; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +ulong blanktime = 30; /* in minutes; a half hour */ + +struct Draw +{ + int clientid; + int nclient; + Client** client; + int nname; + DName* name; + int vers; + int softscreen; + int blanked; /* screen turned off */ + ulong blanktime; /* time of last operation */ + ulong savemap[3*256]; +}; + +struct Client +{ + Ref r; + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; + QLock drawlock; + +static Memimage *screenimage; +static DImage* screendimage; +static char screenname[40]; +static int screennameid; + +static Rectangle flushrect; +static int waste; +static DScreen* dscreen; +extern void flushmemscreen(Rectangle); + void drawmesg(Client*, void*, int); + void drawuninstall(Client*, int); + void drawfreedimage(DImage*); + Client* drawclientofpath(ulong); + DImage* allocdimage(Memimage*); + +static char Enodrawimage[] = "unknown id for draw image"; +static char Enodrawscreen[] = "unknown id for draw screen"; +static char Eshortdraw[] = "short draw message"; +static char Eshortread[] = "draw read too short"; +static char Eimageexists[] = "image id in use"; +static char Escreenexists[] = "screen id in use"; +static char Edrawmem[] = "image memory allocation failed"; +static char Ereadoutside[] = "readimage outside image"; +static char Ewriteoutside[] = "writeimage outside image"; +static char Enotfont[] = "image not a font"; +static char Eindex[] = "character index out of range"; +static char Enoclient[] = "no such draw client"; +static char Enameused[] = "image name in use"; +static char Enoname[] = "no image with that name"; +static char Eoldname[] = "named image no longer valid"; +static char Enamed[] = "image already has name"; +static char Ewrongname[] = "wrong name for image"; + +void +drawqlock(void) +{ + qlock(&drawlock); +} + +int +drawcanqlock(void) +{ + return canqlock(&drawlock); +} + +void +drawqunlock(void) +{ + qunlock(&drawlock); +} + +static int +drawgen(Chan *c, char *_, Dirtab *__, int ___, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + q.vers = 0; + + if(s == DEVDOTDOT){ + switch(QID(c->qid)){ + case Qtopdir: + case Q2nd: + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#i", 0, eve, 0500, dp); + break; + case Q3rd: + cl = drawclientofpath(c->qid.path); + if(cl == nil) + strcpy(up->genbuf, "??"); + else + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0500, dp); + break; + default: + panic("drawwalk %llux", c->qid.path); + } + return 1; + } + + /* + * Top level directory contains the name of the device. + */ + t = QID(c->qid); + if(t == Qtopdir){ + switch(s){ + case 0: + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, "draw", 0, eve, 0555, dp); + break; + case 1: + mkqid(&q, Qwinname, 0, 0); + devdir(c, q, "winname", 0, eve, 0444, dp); + break; + default: + return -1; + } + return 1; + } + + /* + * Second level contains "new" plus all the clients. + */ + if(t == Q2nd || t == Qnew){ + if(s == 0){ + mkqid(&q, Qnew, 0, QTFILE); + devdir(c, q, "new", 0, eve, 0666, dp); + } + else if(s <= sdraw.nclient){ + cl = sdraw.client[s-1]; + if(cl == 0) + return 0; + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + else + return -1; + return 1; + } + + /* + * Third level. + */ + path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */ + q.vers = c->qid.vers; + q.type = QTFILE; + switch(s){ + case 0: + q.path = path|Qcolormap; + devdir(c, q, "colormap", 0, eve, 0600, dp); + break; + case 1: + q.path = path|Qctl; + devdir(c, q, "ctl", 0, eve, 0600, dp); + break; + case 2: + q.path = path|Qdata; + devdir(c, q, "data", 0, eve, 0600, dp); + break; + case 3: + q.path = path|Qrefresh; + devdir(c, q, "refresh", 0, eve, 0400, dp); + break; + default: + return -1; + } + return 1; +} + +static +int +drawrefactive(void *a) +{ + Client *c; + + c = a; + return c->refreshme || c->refresh!=0; +} + +static +void +drawrefreshscreen(DImage *l, Client *client) +{ + while(l != nil && l->dscreen == nil) + l = l->fromname; + if(l != nil && l->dscreen->owner != client) + l->dscreen->owner->refreshme = 1; +} + +static +void +drawrefresh(Memimage *m, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + if(v == 0) + return; + x = v; + c = x->client; + d = x->dimage; + for(ref=c->refresh; ref; ref=ref->next) + if(ref->dimage == d){ + combinerect(&ref->r, r); + return; + } + ref = malloc(sizeof(Refresh)); + if(ref){ + ref->dimage = d; + ref->r = r; + ref->next = c->refresh; + c->refresh = ref; + } +} + +static void +addflush(Rectangle r) +{ + int abb, ar, anbb; + Rectangle nbb; + + if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + return; + + if(flushrect.min.x >= flushrect.max.x){ + flushrect = r; + waste = 0; + return; + } + nbb = flushrect; + combinerect(&nbb, r); + ar = Dx(r)*Dy(r); + abb = Dx(flushrect)*Dy(flushrect); + anbb = Dx(nbb)*Dy(nbb); + /* + * Area of new waste is area of new bb minus area of old bb, + * less the area of the new segment, which we assume is not waste. + * This could be negative, but that's OK. + */ + waste += anbb-abb - ar; + if(waste < 0) + waste = 0; + /* + * absorb if: + * total area is small + * waste is less than half total area + * rectangles touch + */ + if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){ + flushrect = nbb; + return; + } + /* emit current state */ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = r; + waste = 0; +} + +static +void +dstflush(int dstid, Memimage *dst, Rectangle r) +{ + Memlayer *l; + + if(dstid == 0){ + combinerect(&flushrect, r); + return; + } + /* how can this happen? -rsc, dec 12 2002 */ + if(dst == 0){ + print("nil dstflush\n"); + return; + } + l = dst->layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +static +int +drawcmp(char *a, char *b, int n) +{ + if(strlen(a) != n) + return 1; + return memcmp(a, b, n); +} + +DName* +drawlookupname(int n, char *str) +{ + DName *name, *ename; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + return name; + return 0; +} + +int +drawgoodname(DImage *d) +{ + DName *n; + + /* if window, validate the screen's own images */ + if(d->dscreen) + if(drawgoodname(d->dscreen->dimage) == 0 + || drawgoodname(d->dscreen->dfill) == 0) + return 0; + if(d->name == nil) + return 1; + n = drawlookupname(strlen(d->name), d->name); + if(n==nil || n->vers!=d->vers) + return 0; + return 1; +} + +DImage* +drawlookup(Client *client, int id, int checkname) +{ + DImage *d; + + d = client->dimage[id&HASHMASK]; + while(d){ + if(d->id == id){ + if(checkname && !drawgoodname(d)) + error(Eoldname); + return d; + } + d = d->next; + } + return 0; +} + +DScreen* +drawlookupdscreen(int id) +{ + DScreen *s; + + s = dscreen; + while(s){ + if(s->id == id) + return s; + s = s->next; + } + return 0; +} + +DScreen* +drawlookupscreen(Client *client, int id, CScreen **cs) +{ + CScreen *s; + + s = client->cscreen; + while(s){ + if(s->dscreen->id == id){ + *cs = s; + return s->dscreen; + } + s = s->next; + } + error(Enodrawscreen); + for(;;); +} + +DImage* +allocdimage(Memimage *i) +{ + DImage *d; + + d = malloc(sizeof(DImage)); + if(d == 0) + return 0; + d->ref = 1; + d->name = 0; + d->vers = 0; + d->image = i; + d->nfchar = 0; + d->fchar = 0; + d->fromname = 0; + return d; +} + +Memimage* +drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) +{ + DImage *d; + + d = allocdimage(i); + if(d == 0) + return 0; + d->id = id; + d->dscreen = dscreen; + d->next = client->dimage[id&HASHMASK]; + client->dimage[id&HASHMASK] = d; + return i; +} + +Memscreen* +drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) +{ + Memscreen *s; + CScreen *c; + + c = malloc(sizeof(CScreen)); + if(dimage && dimage->image && dimage->image->chan == 0) + panic("bad image %p in drawinstallscreen", dimage->image); + + if(c == 0) + return 0; + if(d == 0){ + d = malloc(sizeof(DScreen)); + if(d == 0){ + free(c); + return 0; + } + s = malloc(sizeof(Memscreen)); + if(s == 0){ + free(c); + free(d); + return 0; + } + s->frontmost = 0; + s->rearmost = 0; + d->dimage = dimage; + if(dimage){ + s->image = dimage->image; + dimage->ref++; + } + d->dfill = dfill; + if(dfill){ + s->fill = dfill->image; + dfill->ref++; + } + d->ref = 0; + d->id = id; + d->screen = s; + d->public = public; + d->next = dscreen; + d->owner = client; + dscreen = d; + } + c->dscreen = d; + d->ref++; + c->next = client->cscreen; + client->cscreen = c; + return d->screen; +} + +void +drawdelname(DName *name) +{ + int i; + + i = name-sdraw.name; + memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); + sdraw.nname--; +} + +void +drawfreedscreen(DScreen *this) +{ + DScreen *ds, *next; + + this->ref--; + if(this->ref < 0) + print("negative ref in drawfreedscreen\n"); + if(this->ref > 0) + return; + ds = dscreen; + if(ds == this){ + dscreen = this->next; + goto Found; + } + while((next = ds->next)){ /* assign = */ + if(next == this){ + ds->next = this->next; + goto Found; + } + ds = next; + } + error(Enodrawimage); + + Found: + if(this->dimage) + drawfreedimage(this->dimage); + if(this->dfill) + drawfreedimage(this->dfill); + free(this->screen); + free(this); +} + +void +drawfreedimage(DImage *dimage) +{ + int i; + Memimage *l; + DScreen *ds; + + dimage->ref--; + if(dimage->ref < 0) + print("negative ref in drawfreedimage\n"); + if(dimage->ref > 0) + return; + + /* any names? */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].dimage == dimage) + drawdelname(sdraw.name+i); + else + i++; + if(dimage->fromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } +// if(dimage->image == screenimage) /* don't free the display */ +// goto Return; + ds = dimage->dscreen; + l = dimage->image; + dimage->dscreen = nil; /* paranoia */ + dimage->image = nil; + if(ds){ + if(l->data == screenimage->data) + addflush(l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(l); + Return: + free(dimage->fchar); + free(dimage); +} + +void +drawuninstallscreen(Client *client, CScreen *this) +{ + CScreen *cs, *next; + + cs = client->cscreen; + if(cs == this){ + client->cscreen = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + while((next = cs->next)){ /* assign = */ + if(next == this){ + cs->next = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + cs = next; + } +} + +void +drawuninstall(Client *client, int id) +{ + DImage *d, *next; + + d = client->dimage[id&HASHMASK]; + if(d == 0) + error(Enodrawimage); + if(d->id == id){ + client->dimage[id&HASHMASK] = d->next; + drawfreedimage(d); + return; + } + while((next = d->next)){ /* assign = */ + if(next->id == id){ + d->next = next->next; + drawfreedimage(next); + return; + } + d = next; + } + error(Enodrawimage); +} + +void +drawaddname(Client *client, DImage *di, int n, char *str) +{ + DName *name, *ename, *new, *t; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + error(Enameused); + t = smalloc((sdraw.nname+1)*sizeof(DName)); + memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); + free(sdraw.name); + sdraw.name = t; + new = &sdraw.name[sdraw.nname++]; + new->name = smalloc(n+1); + memmove(new->name, str, n); + new->name[n] = 0; + new->dimage = di; + new->client = client; + new->vers = ++sdraw.vers; +} + +Client* +drawnewclient(void) +{ + Client *cl, **cp; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl == 0) + break; + } + if(i == sdraw.nclient){ + cp = malloc((sdraw.nclient+1)*sizeof(Client*)); + if(cp == 0) + return 0; + memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*)); + free(sdraw.client); + sdraw.client = cp; + sdraw.nclient++; + cp[i] = 0; + } + cl = malloc(sizeof(Client)); + if(cl == 0) + return 0; + memset(cl, 0, sizeof(Client)); + cl->slot = i; + cl->clientid = ++sdraw.clientid; + cl->op = SoverD; + sdraw.client[i] = cl; + return cl; +} + +static int +drawclientop(Client *cl) +{ + int op; + + op = cl->op; + cl->op = SoverD; + return op; +} + +int +drawhasclients(void) +{ + /* + * if draw has ever been used, we can't resize the frame buffer, + * even if all clients have exited (nclients is cumulative); it's too + * hard to make work. + */ + return sdraw.nclient != 0; +} + +Client* +drawclientofpath(ulong path) +{ + Client *cl; + int slot; + + slot = CLIENTPATH(path); + if(slot == 0) + return nil; + cl = sdraw.client[slot-1]; + if(cl==0 || cl->clientid==0) + return nil; + return cl; +} + + +Client* +drawclient(Chan *c) +{ + Client *client; + + client = drawclientofpath(c->qid.path); + if(client == nil) + error(Enoclient); + return client; +} + +Memimage* +drawimage(Client *client, uchar *a) +{ + DImage *d; + + d = drawlookup(client, BGLONG(a), 1); + if(d == nil) + error(Enodrawimage); + return d->image; +} + +void +drawrectangle(Rectangle *r, uchar *a) +{ + r->min.x = BGLONG(a+0*4); + r->min.y = BGLONG(a+1*4); + r->max.x = BGLONG(a+2*4); + r->max.y = BGLONG(a+3*4); +} + +void +drawpoint(Point *p, uchar *a) +{ + p->x = BGLONG(a+0*4); + p->y = BGLONG(a+1*4); +} + +Point +drawchar(Memimage *dst, Memimage *rdst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + static Memimage *tmp; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + + /* + * If we're drawing greyscale fonts onto a VGA screen, + * it's very costly to read the screen memory to do the + * alpha blending inside memdraw. If this is really a stringbg, + * then rdst is the bg image (in main memory) which we can + * refer to for the underlying dst pixels instead of reading dst + * directly. + */ + if(ishwimage(dst) && !ishwimage(rdst) && font->image->depth > 1){ + if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){ + if(tmp) + freememimage(tmp); + tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan); + if(tmp == nil) + goto fallback; + } + memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S); + memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op); + memdraw(dst, r, tmp, ZP, memopaque, ZP, S); + }else{ + fallback: + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + } + + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static DImage* +makescreenimage(void) +{ + void *X; + int width, depth; + ulong chan; + DImage *di; + Memdata *md; + Memimage *i; + Rectangle r; + + md = malloc(sizeof *md); + if(md == nil) + return nil; + md->allocd = 1; + md->base = nil; + md->bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen, &X); + if(md->bdata == nil){ + free(md); + return nil; + } + md->ref = 1; + i = allocmemimaged(r, chan, md, X); + if(i == nil){ + free(md); + return nil; + } + i->width = width; + i->clipr = r; + + di = allocdimage(i); + if(di == nil){ + freememimage(i); /* frees md */ + return nil; + } + if(!waserror()){ + snprint(screenname, sizeof screenname, "noborder.screen.%d", ++screennameid); + drawaddname(nil, di, strlen(screenname), screenname); + poperror(); + } + return di; +} + +static int +initscreenimage(void) +{ + if(screenimage != nil) + return 1; + + screendimage = makescreenimage(); + if(screendimage == nil) + return 0; + screenimage = screendimage->image; +// iprint("initscreenimage %p %p\n", screendimage, screenimage); + mouseresize(); + return 1; +} + +void +deletescreenimage(void) +{ + drawqlock(); + if(screenimage){ + /* will be freed via screendimage; disable */ + screenimage->clipr = ZR; + screenimage = nil; + } + if(screendimage){ + drawfreedimage(screendimage); + screendimage = nil; + } + drawqunlock(); +} + +void +resetscreenimage(void) +{ + drawqlock(); + initscreenimage(); + drawqunlock(); +} + +static Chan* +drawattach(char *spec) +{ + drawqlock(); + if(!conf.monitor || !initscreenimage()){ + drawqunlock(); + error("no frame buffer"); + } + drawqunlock(); + return devattach('i', spec); +} + +static Walkqid* +drawwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if(screenimage == nil) + error("no frame buffer"); + return devwalk(c, nc, name, nname, 0, 0, drawgen); +} + +static int +drawstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, drawgen); +} + +static Chan* +drawopen(Chan *c, int omode) +{ + Client *cl; + DName *dn; + DImage *di; + + if(c->qid.type & QTDIR){ + c = devopen(c, omode, 0, 0, drawgen); + c->iounit = IOUNIT; + } + + drawqlock(); + if(waserror()){ + drawqunlock(); + nexterror(); + } + + if(QID(c->qid) == Qnew){ + cl = drawnewclient(); + if(cl == 0) + error(Enodev); + c->qid.path = Qctl|((cl->slot+1)<<QSHIFT); + } + + switch(QID(c->qid)){ + case Qwinname: + break; + + case Qnew: + break; + + case Qctl: + cl = drawclient(c); + if(cl->busy) + error(Einuse); + cl->busy = 1; + flushrect = Rect(10000, 10000, -10000, -10000); + dn = drawlookupname(strlen(screenname), screenname); + if(dn == 0) + error("draw: cannot happen 2"); + if(drawinstall(cl, 0, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(cl, 0, 0); + if(di == 0) + error("draw: cannot happen 1"); + di->vers = dn->vers; + di->name = smalloc(strlen(screenname)+1); + strcpy(di->name, screenname); + di->fromname = dn->dimage; + di->fromname->ref++; + incref(&cl->r); + break; + + case Qcolormap: + case Qdata: + case Qrefresh: + cl = drawclient(c); + incref(&cl->r); + break; + } + drawqunlock(); + poperror(); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = IOUNIT; + return c; +} + +static void +drawclose(Chan *c) +{ + int i; + DImage *d, **dp; + Client *cl; + Refresh *r; + + if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ + return; + drawqlock(); + if(waserror()){ + drawqunlock(); + nexterror(); + } + + cl = drawclient(c); + if(QID(c->qid) == Qctl) + cl->busy = 0; + if((c->flag&COPEN) && (decref(&cl->r)==0)){ + while((r = cl->refresh)){ /* assign = */ + cl->refresh = r->next; + free(r); + } + /* free names */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].client == cl) + drawdelname(sdraw.name+i); + else + i++; + while(cl->cscreen) + drawuninstallscreen(cl, cl->cscreen); + /* all screens are freed, so now we can free images */ + dp = cl->dimage; + for(i=0; i<NHASH; i++){ + while((d = *dp) != nil){ + *dp = d->next; + drawfreedimage(d); + } + dp++; + } + sdraw.client[cl->slot] = 0; + drawflush(); /* to erase visible, now dead windows */ + free(cl); + } + drawqunlock(); + poperror(); +} + +long +drawread(Chan *c, void *a, long n, vlong off) +{ + int index, m; + ulong red, green, blue; + Client *cl; + uchar *p; + Refresh *r; + DImage *di; + Memimage *i; + ulong offset = off; + char buf[16]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + if(QID(c->qid) == Qwinname) + return readstr(off, a, n, screenname); + + cl = drawclient(c); + drawqlock(); + if(waserror()){ + drawqunlock(); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n < 12*12) + error(Eshortread); + if(cl->infoid < 0) + error(Enodrawimage); + if(cl->infoid == 0){ + i = screenimage; + if(i == nil) + error(Enodrawimage); + }else{ + di = drawlookup(cl, cl->infoid, 1); + if(di == nil) + error(Enodrawimage); + i = di->image; + } + n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", + cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + cl->infoid = -1; + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + p = malloc(4*12*256+1); + if(p == 0) + error(Enomem); + m = 0; + for(index = 0; index < 256; index++){ + getcolor(index, &red, &green, &blue); + m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); + } + n = readstr(offset, a, n, (char*)p); + free(p); + break; + + case Qdata: + if(cl->readdata == nil) + error("no draw data"); + if(n < cl->nreaddata) + error(Eshortread); + n = cl->nreaddata; + memmove(a, cl->readdata, cl->nreaddata); + free(cl->readdata); + cl->readdata = nil; + break; + + case Qrefresh: + if(n < 5*4) + error(Ebadarg); + for(;;){ + if(cl->refreshme || cl->refresh) + break; + drawqunlock(); + if(waserror()){ + drawqlock(); /* restore lock for waserror() above */ + nexterror(); + } + sleep(&cl->refrend, drawrefactive, cl); + poperror(); + drawqlock(); + } + p = a; + while(cl->refresh && n>=5*4){ + r = cl->refresh; + BPLONG(p+0*4, r->dimage->id); + BPLONG(p+1*4, r->r.min.x); + BPLONG(p+2*4, r->r.min.y); + BPLONG(p+3*4, r->r.max.x); + BPLONG(p+4*4, r->r.max.y); + cl->refresh = r->next; + free(r); + p += 5*4; + n -= 5*4; + } + cl->refreshme = 0; + n = p-(uchar*)a; + break; + } + drawqunlock(); + poperror(); + return n; +} + +void +drawwakeall(void) +{ + Client *cl; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl && (cl->refreshme || cl->refresh)) + wakeup(&cl->refrend); + } +} + +static long +drawwrite(Chan *c, void *a, long n, vlong _) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + drawqlock(); + if(waserror()){ + drawwakeall(); + drawqunlock(); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + m = n; + n = 0; + while(m > 0){ + x = m; + if(x > sizeof(buf)-1) + x = sizeof(buf)-1; + q = memccpy(buf, a, '\n', x); + if(q == 0) + break; + i = q-buf; + n += i; + a = (char*)a + i; + m -= i; + *q = 0; + if(tokenize(buf, fields, nelem(fields)) != 4) + error(Ebadarg); + i = strtoul(fields[0], 0, 0); + red = strtoul(fields[1], 0, 0); + green = strtoul(fields[2], 0, 0); + blue = strtoul(fields[3], &q, 0); + if(fields[3] == q) + error(Ebadarg); + if(red>255 || green>255 || blue>255 || i<0 || i>255) + error(Ebadarg); + red |= red<<8; + red |= red<<16; + green |= green<<8; + green |= green<<16; + blue |= blue<<8; + blue |= blue<<16; + setcolor(i, red, green, blue); + } + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + drawqunlock(); + poperror(); + return n; +} + +uchar* +drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) +{ + int b, x; + + if(p >= maxp) + error(Eshortdraw); + b = *p++; + x = b & 0x7F; + if(b & 0x80){ + if(p+1 >= maxp) + error(Eshortdraw); + x |= *p++ << 7; + x |= *p++ << 15; + if(x & (1<<22)) + x |= ~0<<23; + }else{ + if(b & 0x40) + x |= ~0<<7; + x += oldx; + } + *newx = x; + return p; +} + +static void +printmesg(char *fmt, uchar *a, int plsprnt) +{ + char buf[256]; + char *p, *q; + + if(1|| plsprnt==0){ + return; + } + q = buf; + *q++ = *a++; + for(p=fmt; *p; p++){ + switch(*p){ + case 'l': + q += sprint(q, " %ld", (long)BGLONG(a)); + a += 4; + break; + case 'L': + q += sprint(q, " %.8lux", (ulong)BGLONG(a)); + a += 4; + break; + case 'R': + q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); + a += 16; + break; + case 'P': + q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); + a += 8; + break; + case 'b': + q += sprint(q, " %d", *a++); + break; + case 's': + q += sprint(q, " %d", BGSHORT(a)); + a += 2; + break; + case 'S': + q += sprint(q, " %.4ux", BGSHORT(a)); + a += 2; + break; + } + } + *q++ = '\n'; + *q = 0; + iprint("%.*s", (int)(q-buf), buf); +} + +void +drawmesg(Client *client, void *av, int n) +{ + int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush; + uchar *u, *a, refresh; + char *fmt; + ulong value, chan; + Rectangle r, clipr; + Point p, q, *pp, sp; + Memimage *i, *bg, *dst, *src, *mask; + Memimage *l, **lp; + Memscreen *scrn; + DImage *font, *ll, *di, *ddst, *dsrc; + DName *dn; + DScreen *dscrn; + FChar *fc; + Refx *refx; + CScreen *cs; + Refreshfn reffn; + + a = av; + m = 0; + fmt = nil; + if(waserror()){ + if(fmt) printmesg(fmt, a, 1); + /* iprint("error: %s\n", up->errstr); */ + nexterror(); + } + while((n-=m) > 0){ + a += m; + switch(*a){ + default: + error("bad draw command"); + /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ + case 'b': + printmesg(fmt="LLbLbRRL", a, 0); + m = 1+4+4+1+4+1+4*4+4*4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + scrnid = BGSHORT(a+5); + refresh = a[9]; + chan = BGLONG(a+10); + repl = a[14]; + drawrectangle(&r, a+15); + drawrectangle(&clipr, a+31); + value = BGLONG(a+47); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + if(scrnid){ + dscrn = drawlookupscreen(client, scrnid, &cs); + scrn = dscrn->screen; + if(repl || chan!=scrn->image->chan) + error("image parameters incompatible with screen"); + reffn = nil; + switch(refresh){ + case Refbackup: + break; + case Refnone: + reffn = memlnorefresh; + break; + case Refmesg: + reffn = drawrefresh; + break; + default: + error("unknown refresh method"); + } + l = memlalloc(scrn, r, reffn, 0, value); + if(l == 0) + error(Edrawmem); + addflush(l->layer->screenr); + l->clipr = clipr; + rectclip(&l->clipr, r); + if(drawinstall(client, dstid, l, dscrn) == 0){ + memldelete(l); + error(Edrawmem); + } + dscrn->ref++; + if(reffn){ + refx = nil; + if(reffn == drawrefresh){ + refx = malloc(sizeof(Refx)); + if(refx == 0){ + drawuninstall(client, dstid); + error(Edrawmem); + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + memlsetrefresh(l, reffn, refx); + } + continue; + } + i = allocmemimage(r, chan); + if(i == 0) + error(Edrawmem); + if(repl) + i->flags |= Frepl; + i->clipr = clipr; + if(!repl) + rectclip(&i->clipr, r); + if(drawinstall(client, dstid, i, 0) == 0){ + freememimage(i); + error(Edrawmem); + } + memfillcolor(i, value); + continue; + + /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ + case 'A': + printmesg(fmt="LLLb", a, 1); + m = 1+4+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + if(drawlookupdscreen(dstid)) + error(Escreenexists); + ddst = drawlookup(client, BGLONG(a+5), 1); + dsrc = drawlookup(client, BGLONG(a+9), 1); + if(ddst==0 || dsrc==0) + error(Enodrawimage); + if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) + error(Edrawmem); + continue; + + /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ + case 'c': + printmesg(fmt="LbR", a, 0); + m = 1+4+1+4*4; + if(n < m) + error(Eshortdraw); + ddst = drawlookup(client, BGLONG(a+1), 1); + if(ddst == nil) + error(Enodrawimage); + if(ddst->name) + error("cannot change repl/clipr of shared image"); + dst = ddst->image; + if(a[5]) + dst->flags |= Frepl; + drawrectangle(&dst->clipr, a+6); + continue; + + /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ + case 'd': + printmesg(fmt="LLLRPP", a, 0); + m = 1+4+4+4+4*4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + mask = drawimage(client, a+9); + drawrectangle(&r, a+13); + drawpoint(&p, a+29); + drawpoint(&q, a+37); + op = drawclientop(client); + memdraw(dst, r, src, p, mask, q, op); + dstflush(dstid, dst, r); + continue; + + /* toggle debugging: 'D' val[1] */ + case 'D': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + drawdebug = a[1]; + continue; + + /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ + case 'e': + case 'E': + printmesg(fmt="LLPlllPll", a, 0); + m = 1+4+4+2*4+4+4+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + drawpoint(&p, a+9); + e0 = BGLONG(a+17); + e1 = BGLONG(a+21); + if(e0<0 || e1<0) + error("invalid ellipse semidiameter"); + j = BGLONG(a+25); + if(j < 0) + error("negative ellipse thickness"); + drawpoint(&sp, a+29); + c = j; + if(*a == 'E') + c = -1; + ox = BGLONG(a+37); + oy = BGLONG(a+41); + op = drawclientop(client); + /* high bit indicates arc angles are present */ + if(ox & (1<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + continue; + + /* free: 'f' id[4] */ + case 'f': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + ll = drawlookup(client, BGLONG(a+1), 0); + if(ll && ll->dscreen && ll->dscreen->owner != client) + ll->dscreen->owner->refreshme = 1; + drawuninstall(client, BGLONG(a+1)); + continue; + + /* free screen: 'F' id[4] */ + case 'F': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + drawlookupscreen(client, BGLONG(a+1), &cs); + drawuninstallscreen(client, cs); + continue; + + /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ + case 'i': + printmesg(fmt="Llb", a, 1); + m = 1+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error("cannot use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("cannot use window as font"); + ni = BGLONG(a+5); + if(ni<=0 || ni>4096) + error("bad font size (4096 chars max)"); + free(font->fchar); /* should we complain if non-zero? */ + font->fchar = malloc(ni*sizeof(FChar)); + if(font->fchar == 0) + error("no memory for font"); + memset(font->fchar, 0, ni*sizeof(FChar)); + font->nfchar = ni; + font->ascent = a[9]; + continue; + + /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ + case 'l': + printmesg(fmt="LLSRPbb", a, 0); + m = 1+4+4+2+4*4+2*4+1+1; + if(n < m) + error(Eshortdraw); + font = drawlookup(client, BGLONG(a+1), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + src = drawimage(client, a+5); + ci = BGSHORT(a+9); + if(ci >= font->nfchar) + error(Eindex); + drawrectangle(&r, a+11); + drawpoint(&p, a+27); + memdraw(font->image, r, src, p, memopaque, p, S); + fc = &font->fchar[ci]; + fc->minx = r.min.x; + fc->maxx = r.max.x; + fc->miny = r.min.y; + fc->maxy = r.max.y; + fc->left = a[35]; + fc->width = a[36]; + continue; + + /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ + case 'L': + printmesg(fmt="LPPlllLP", a, 0); + m = 1+4+2*4+2*4+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + drawpoint(&p, a+5); + drawpoint(&q, a+13); + e0 = BGLONG(a+21); + e1 = BGLONG(a+25); + j = BGLONG(a+29); + if(j < 0) + error("negative line width"); + src = drawimage(client, a+33); + drawpoint(&sp, a+37); + op = drawclientop(client); + memline(dst, p, q, e0, e1, j, src, sp, op); + /* avoid memlinebbox if possible */ + if(dstid==0 || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dstid, dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + printmesg("LL", a, 0); + m = 4+4; + if(n < m) + error(Eshortdraw); + break; + * + */ + + /* attach to a named image: 'n' dstid[4] j[1] name[j] */ + case 'n': + printmesg(fmt="Lz", a, 0); + m = 1+4+1; + if(n < m) + error(Eshortdraw); + j = a[5]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + dn = drawlookupname(j, (char*)a+6); + if(dn == nil) + error(Enoname); + if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(client, dstid, 0); + if(di == 0) + error("draw: cannot happen"); + di->vers = dn->vers; + di->name = smalloc(j+1); + di->fromname = dn->dimage; + di->fromname->ref++; + memmove(di->name, a+6, j); + di->name[j] = 0; + client->infoid = dstid; + continue; + + /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ + case 'N': + printmesg(fmt="Lbz", a, 0); + m = 1+4+1+1; + if(n < m) + error(Eshortdraw); + c = a[5]; + j = a[6]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + di = drawlookup(client, BGLONG(a+1), 0); + if(di == 0) + error(Enodrawimage); + if(di->name) + error(Enamed); + if(c) + drawaddname(client, di, j, (char*)a+7); + else{ + dn = drawlookupname(j, (char*)a+7); + if(dn == nil) + error(Enoname); + if(dn->dimage != di) + error(Ewrongname); + drawdelname(dn); + } + continue; + + /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ + case 'o': + printmesg(fmt="LPP", a, 0); + m = 1+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + if(dst->layer){ + drawpoint(&p, a+5); + drawpoint(&q, a+13); + r = dst->layer->screenr; + ni = memlorigin(dst, p, q); + if(ni < 0) + error("image origin failed"); + if(ni > 0){ + addflush(r); + addflush(dst->layer->screenr); + ll = drawlookup(client, BGLONG(a+1), 1); + drawrefreshscreen(ll, client); + } + } + continue; + + /* set compositing operator for next draw operation: 'O' op */ + case 'O': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + client->op = a[1]; + continue; + + /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + printmesg(fmt="LslllLPP", a, 0); + m = 1+4+2+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + ni = BGSHORT(a+5); + if(ni < 0) + error("negative count in polygon"); + e0 = BGLONG(a+7); + e1 = BGLONG(a+11); + j = 0; + if(*a == 'p'){ + j = BGLONG(a+15); + if(j < 0) + error("negative polygon line width"); + } + src = drawimage(client, a+19); + drawpoint(&sp, a+23); + drawpoint(&p, a+31); + ni++; + pp = malloc(ni*sizeof(Point)); + if(pp == nil) + error(Enomem); + doflush = 0; + if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y<ni; y++){ + q = p; + oesize = esize; + u = drawcoord(u, a+n, ox, &p.x); + u = drawcoord(u, a+n, oy, &p.y); + ox = p.x; + oy = p.y; + if(doflush){ + esize = j; + if(*a == 'p'){ + if(y == 0){ + c = memlineendsize(e0); + if(c > esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if(y > 0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dstid, dst, r); + } + pp[y] = p; + } + if(y == 1) + dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + printmesg(fmt="LR", a, 0); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + i = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, i->r)) + error(Ereadoutside); + c = bytesperline(r, i->depth); + c *= Dy(r); + free(client->readdata); + client->readdata = mallocz(c, 0); + if(client->readdata == nil) + error("readimage malloc failed"); + client->nreaddata = memunload(i, r, client->readdata, c); + if(client->nreaddata < 0){ + free(client->readdata); + client->readdata = nil; + error("bad readimage call"); + } + continue; + + /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + printmesg(fmt="LLLPRPs", a, 0); + m = 1+4+4+4+2*4+4*4+2*4+2; + if(*a == 'x') + m += 4+2*4; + if(n < m) + error(Eshortdraw); + + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + font = drawlookup(client, BGLONG(a+9), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + drawpoint(&p, a+13); + drawrectangle(&r, a+21); + drawpoint(&sp, a+37); + ni = BGSHORT(a+45); + u = a+m; + m += ni*2; + if(n < m) + error(Eshortdraw); + clipr = dst->clipr; + dst->clipr = r; + op = drawclientop(client); + bg = dst; + if(*a == 'x'){ + /* paint background */ + bg = drawimage(client, a+47); + drawpoint(&q, a+51); + r.min.x = p.x; + r.min.y = p.y-font->ascent; + r.max.x = p.x; + r.max.y = r.min.y+Dy(font->image->r); + j = ni; + while(--j >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + r.max.x += font->fchar[ci].width; + u += 2; + } + memdraw(dst, r, bg, q, memopaque, ZP, op); + u -= 2*ni; + } + q = p; + while(--ni >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + q = drawchar(dst, bg, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + continue; + + /* use public screen: 'S' id[4] chan[4] */ + case 'S': + printmesg(fmt="Ll", a, 0); + m = 1+4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + dscrn = drawlookupdscreen(dstid); + if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) + error(Enodrawscreen); + if(dscrn->screen->image->chan != BGLONG(a+5)) + error("inconsistent chan"); + if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) + error(Edrawmem); + continue; + + /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ + case 't': + printmesg(fmt="bsL", a, 0); + m = 1+1+2; + if(n < m) + error(Eshortdraw); + nw = BGSHORT(a+2); + if(nw < 0) + error(Ebadarg); + if(nw == 0) + continue; + m += nw*4; + if(n < m) + error(Eshortdraw); + lp = malloc(nw*sizeof(Memimage*)); + if(lp == 0) + error(Enomem); + if(waserror()){ + free(lp); + nexterror(); + } + for(j=0; j<nw; j++) + lp[j] = drawimage(client, a+1+1+2+j*4); + if(lp[0]->layer == 0) + error("images are not windows"); + for(j=1; j<nw; j++) + if(lp[j]->layer->screen != lp[0]->layer->screen) + error("images not on same screen"); + if(a[1]) + memltofrontn(lp, nw); + else + memltorearn(lp, nw); + if(lp[0]->layer->screen->image->data == screenimage->data) + for(j=0; j<nw; j++) + addflush(lp[j]->layer->screenr); + ll = drawlookup(client, BGLONG(a+1+1+2), 1); + drawrefreshscreen(ll, client); + poperror(); + free(lp); + continue; + + /* visible: 'v' */ + case 'v': + printmesg(fmt="", a, 0); + m = 1; + drawflush(); + continue; + + /* write: 'y' id[4] R[4*4] data[x*1] */ + /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ + case 'y': + case 'Y': + printmesg(fmt="LR", a, 0); + // iprint("load %c\n", *a); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, dst->r)) + error(Ewriteoutside); + y = memload(dst, r, a+m, n-m, *a=='Y'); + if(y < 0) + error("bad writeimage call"); + dstflush(dstid, dst, r); + m += y; + continue; + } + } + poperror(); +} + +Dev drawdevtab = { + 'i', + "draw", + + devreset, + devinit, + devshutdown, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * On 8 bit displays, load the default color map + */ +void +drawcmap(void) +{ + int r, g, b, cr, cg, cb, v; + int num, den; + int i, j; + + drawactive(1); /* to restore map from backup */ + for(r=0,i=0; r!=4; r++) + for(v=0; v!=4; v++,i+=16){ + for(g=0,j=v-r; g!=4; g++) + for(b=0;b!=4;b++,j++){ + den = r; + if(g > den) + den = g; + if(b > den) + den = b; + if(den == 0) /* divide check -- pick grey shades */ + cr = cg = cb = v*17; + else{ + num = 17*(4*den+v); + cr = r*num/den; + cg = g*num/den; + cb = b*num/den; + } + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +void +drawblankscreen(int blank) +{ + int i, nc; + ulong *p; + + if(blank == sdraw.blanked) + return; + if(!drawcanqlock()) + return; + if(!initscreenimage()){ + drawqunlock(); + return; + } + p = sdraw.savemap; + nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth; + + /* + * blankscreen uses the hardware to blank the screen + * when possible. to help in cases when it is not possible, + * we set the color map to be all black. + */ + if(blank == 0){ /* turn screen on */ + for(i=0; i<nc; i++, p+=3) + setcolor(i, p[0], p[1], p[2]); + blankscreen(0); + }else{ /* turn screen off */ + blankscreen(1); + for(i=0; i<nc; i++, p+=3){ + getcolor(i, &p[0], &p[1], &p[2]); + setcolor(i, 0, 0, 0); + } + } + sdraw.blanked = blank; + drawqunlock(); +} + +/* + * record activity on screen, changing blanking as appropriate + */ +void +drawactive(int active) +{ + if(active){ + drawblankscreen(0); + sdraw.blanktime = msec()/1000; + }else{ + if(blanktime && sdraw.blanktime && TK2SEC(msec()/1000 - sdraw.blanktime)/60 >= blanktime) + drawblankscreen(1); + } +} + +int +drawidletime(void) +{ + return TK2SEC(msec()/1000 - sdraw.blanktime)/60; +} + +/* why is this here? why can't caller use drawqlock himself? */ +void +drawflushr(Rectangle r) +{ + drawqlock(); + flushmemscreen(r); + drawqunlock(); +} + +void +drawreplacescreenimage(Memimage *m) +{ + int i; + DImage *di; + + if(screendimage == nil) + return; + + /* + * Replace the screen image because the screen + * was resized. Clients still have references to the + * old screen image, so we can't free it just yet. + */ + drawqlock(); + di = allocdimage(m); + if(di == nil){ + print("no memory to replace screen image\n"); + freememimage(m); + drawqunlock(); + return; + } + + /* Replace old screen image in global name lookup. */ + for(i=0; i<sdraw.nname; i++){ + if(sdraw.name[i].dimage == screendimage) + if(sdraw.name[i].client == nil){ + sdraw.name[i].dimage = di; + break; + } + } + + drawfreedimage(screendimage); + screendimage = di; + screenimage = m; + + /* + * Every client, when it starts, gets a copy of the + * screen image as image 0. Clients only use it + * for drawing if there is no /dev/winname, but + * this /dev/draw provides a winname (early ones + * didn't; winname originated in rio), so the + * image only ends up used to find the screen + * resolution and pixel format during initialization. + * Silently remove the now-outdated image 0s. + */ + for(i=0; i<sdraw.nclient; i++){ + if(sdraw.client[i] && !waserror()){ + drawuninstall(sdraw.client[i], 0); + poperror(); + } + } + + drawqunlock(); + mouseresize(); +} diff --git a/a/devdraw.ed b/a/devdraw.ed @@ -0,0 +1,107 @@ +/^drawgen/ s/char\*, Dirtab\*, int,/char *_, Dirtab *__, int ___,/ +,s/(Memimage\*,/(Memimage *m,/ +,s/(next = ds->next)/(&)/ +,s/(next = d->next)/(&)/ +,s/(next = cs->next)/(&)/ +/^makescreenimage/+2i + void *X; +. +/attachscreen/ s/);/, \&X);/ +/allocmemimaged/ s/md)/md, X)/ +,s/(r = cl->refresh)/(&)/ +/^drawwrite/ s/vlong)/vlong _)/ +g/SET(s,q,p)/d +g/USED/d +g/^ int s;/d +/screen.h"/a + +#define blankscreen(x) +#define ishwimage(x) (0) +. +/^drawlookupscreen/;# +/ return 0/ s/return 0/for(;;)/ +g/Edepth/d +/l = dimage->image;/d +-1i + l = dimage->image; + dimage->dscreen = nil; /* paranoia */ + dimage->image = nil; +. +/^drawattach/;# +/initscreenimage/s/(.*)/(!conf.monitor || !initscreenimage())/ +/freememimage(dimage->image);/ s/dimage->image/l/ +,s/dunlock/drawqunlock/g +,s/candlock/drawcanqlock/g +,s/dlock(/drawqlock(/g +,s;MACHP(0)->ticks;msec()/1000;g +/^drawqlock/-1 s/static // +/^drawqunlock/-1 s/static // +/^drawcanqlock/-1 s/static // +$a + +/* why is this here? why can't caller use drawqlock himself? */ +void +drawflushr(Rectangle r) +{ + drawqlock(); + flushmemscreen(r); + drawqunlock(); +} + +void +drawreplacescreenimage(Memimage *m) +{ + int i; + DImage *di; + + if(screendimage == nil) + return; + + /* + * Replace the screen image because the screen + * was resized. Clients still have references to the + * old screen image, so we can't free it just yet. + */ + drawqlock(); + di = allocdimage(m); + if(di == nil){ + print("no memory to replace screen image\n"); + freememimage(m); + drawqunlock(); + return; + } + + /* Replace old screen image in global name lookup. */ + for(i=0; i<sdraw.nname; i++){ + if(sdraw.name[i].dimage == screendimage) + if(sdraw.name[i].client == nil){ + sdraw.name[i].dimage = di; + break; + } + } + + drawfreedimage(screendimage); + screendimage = di; + screenimage = m; + + /* + * Every client, when it starts, gets a copy of the + * screen image as image 0. Clients only use it + * for drawing if there is no /dev/winname, but + * this /dev/draw provides a winname (early ones + * didn't; winname originated in rio), so the + * image only ends up used to find the screen + * resolution and pixel format during initialization. + * Silently remove the now-outdated image 0s. + */ + for(i=0; i<sdraw.nclient; i++){ + if(sdraw.client[i] && !waserror()){ + drawuninstall(sdraw.client[i], 0); + poperror(); + } + } + + drawqunlock(); + mouseresize(); +} +. diff --git a/a/devdup.c b/a/devdup.c @@ -0,0 +1,146 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* Qid is (2*fd + (file is ctl))+1 */ + +static int +dupgen(Chan *c, char *name, Dirtab *dt, int ndt, int s, Dir *dp) +{ + Fgrp *fgrp = up->fgrp; + Chan *f; + static int perm[] = { 0400, 0200, 0600, 0 }; + int p; + Qid q; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp); + return 1; + } + if(s == 0) + return 0; + s--; + if(s/2 > fgrp->maxfd) + return -1; + if((f=fgrp->fd[s/2]) == nil) + return 0; + if(s & 1){ + p = 0400; + sprint(up->genbuf, "%dctl", s/2); + }else{ + p = perm[f->mode&3]; + sprint(up->genbuf, "%d", s/2); + } + mkqid(&q, s+1, 0, QTFILE); + devdir(c, q, up->genbuf, 0, eve, p, dp); + return 1; +} + +static Chan* +dupattach(char *spec) +{ + return devattach('d', spec); +} + +static Walkqid* +dupwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, (Dirtab *)0, 0, dupgen); +} + +static int +dupstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, (Dirtab *)0, 0L, dupgen); +} + +static Chan* +dupopen(Chan *c, int omode) +{ + Chan *f; + int fd, twicefd; + + if(c->qid.type & QTDIR){ + if(omode != 0) + error(Eisdir); + c->mode = 0; + c->flag |= COPEN; + c->offset = 0; + return c; + } + if(c->qid.type & QTAUTH) + error(Eperm); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if((twicefd & 1)){ + /* ctl file */ + f = c; + f->mode = openmode(omode); + f->flag |= COPEN; + f->offset = 0; + }else{ + /* fd file */ + f = fdtochan(fd, openmode(omode), 0, 1); + cclose(c); + } + if(omode & OCEXEC) + f->flag |= CCEXEC; + return f; +} + +static void +dupclose(Chan *c) +{ +} + +static long +dupread(Chan *c, void *va, long n, vlong offset) +{ + char *a = va; + char buf[256]; + int fd, twicefd; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, (Dirtab *)0, 0L, dupgen); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if(twicefd & 1){ + c = fdtochan(fd, -1, 0, 1); + procfdprint(c, fd, 0, buf, sizeof buf); + cclose(c); + return readstr((ulong)offset, va, n, buf); + } + panic("dupread"); + return 0; +} + +static long +dupwrite(Chan *c, void *v, long n, vlong o) +{ + error(Eperm); + return 0; /* not reached */ +} + +Dev dupdevtab = { + 'd', + "dup", + + devreset, + devinit, + devshutdown, + dupattach, + dupwalk, + dupstat, + dupopen, + devcreate, + dupclose, + dupread, + devbread, + dupwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/a/devdup.ed b/a/devdup.ed @@ -0,0 +1,3 @@ +g/^dupgen/ s/char \*, Dirtab\*, int,/char *name, Dirtab *dt, int ndt,/ +g/^dupclose/ s/Chan\*/Chan *c/ +g/^dupwrite/ s/Chan\*, void\*, long, vlong/Chan *c, void *v, long n, vlong o/ diff --git a/a/devenv.c b/a/devenv.c @@ -0,0 +1,438 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Maxenvsize = 16300, +}; + +static Egrp *envgrp(Chan *c); +static int envwriteable(Chan *c); + +static Egrp confegrp; /* global environment group containing the kernel configuration */ + +static Evalue* +envlookup(Egrp *eg, char *name, ulong qidpath) +{ + Evalue *e; + int i; + + for(i=0; i<eg->nent; i++){ + e = eg->ent[i]; + if(e->qid.path == qidpath || (name && e->name[0]==name[0] && strcmp(e->name, name) == 0)) + return e; + } + return nil; +} + +static int +envgen(Chan *c, char *name, Dirtab *dt, int i, int s, Dir *dp) +{ + Egrp *eg; + Evalue *e; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp); + return 1; + } + + eg = envgrp(c); + rlock(&eg->lk); + e = 0; + if(name) + e = envlookup(eg, name, -1); + else if(s < eg->nent) + e = eg->ent[s]; + + if(e == 0) { + runlock(&eg->lk); + return -1; + } + + /* make sure name string continues to exist after we release lock */ + kstrcpy(up->genbuf, e->name, sizeof up->genbuf); + devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp); + runlock(&eg->lk); + return 1; +} + +static Chan* +envattach(char *spec) +{ + Chan *c; + Egrp *egrp = nil; + + if(spec && *spec) { + if(strcmp(spec, "c") == 0) + egrp = &confegrp; + if(egrp == nil) + error(Ebadarg); + } + + c = devattach('e', spec); + c->aux = egrp; + return c; +} + +static Walkqid* +envwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, envgen); +} + +static int +envstat(Chan *c, uchar *db, int n) +{ + if(c->qid.type & QTDIR) + c->qid.vers = envgrp(c)->vers; + return devstat(c, db, n, 0, 0, envgen); +} + +static Chan* +envopen(Chan *c, int omode) +{ + Egrp *eg; + Evalue *e; + int trunc; + + eg = envgrp(c); + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eperm); + } + else { + trunc = omode & OTRUNC; + if(omode != OREAD && !envwriteable(c)) + error(Eperm); + if(trunc) + wlock(&eg->lk); + else + rlock(&eg->lk); + e = envlookup(eg, nil, c->qid.path); + if(e == 0) { + if(trunc) + wunlock(&eg->lk); + else + runlock(&eg->lk); + error(Enonexist); + } + if(trunc && e->value) { + e->qid.vers++; + free(e->value); + e->value = 0; + e->len = 0; + } + if(trunc) + wunlock(&eg->lk); + else + runlock(&eg->lk); + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +envcreate(Chan *c, char *name, int omode, ulong perm) +{ + Egrp *eg; + Evalue *e; + Evalue **ent; + + if(c->qid.type != QTDIR) + error(Eperm); + + omode = openmode(omode); + eg = envgrp(c); + + wlock(&eg->lk); + if(waserror()) { + wunlock(&eg->lk); + nexterror(); + } + + if(envlookup(eg, name, -1)) + error(Eexist); + + e = smalloc(sizeof(Evalue)); + e->name = smalloc(strlen(name)+1); + strcpy(e->name, name); + + if(eg->nent == eg->ment){ + eg->ment += 32; + ent = smalloc(sizeof(eg->ent[0])*eg->ment); + if(eg->nent) + memmove(ent, eg->ent, sizeof(eg->ent[0])*eg->nent); + free(eg->ent); + eg->ent = ent; + } + e->qid.path = ++eg->path; + e->qid.vers = 0; + eg->vers++; + eg->ent[eg->nent++] = e; + c->qid = e->qid; + + wunlock(&eg->lk); + poperror(); + + c->offset = 0; + c->mode = omode; + c->flag |= COPEN; +} + +static void +envremove(Chan *c) +{ + int i; + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) + error(Eperm); + + eg = envgrp(c); + wlock(&eg->lk); + e = 0; + for(i=0; i<eg->nent; i++){ + if(eg->ent[i]->qid.path == c->qid.path){ + e = eg->ent[i]; + eg->nent--; + eg->ent[i] = eg->ent[eg->nent]; + eg->vers++; + break; + } + } + wunlock(&eg->lk); + if(e == 0) + error(Enonexist); + free(e->name); + if(e->value) + free(e->value); + free(e); +} + +static void +envclose(Chan *c) +{ + /* + * cclose can't fail, so errors from remove will be ignored. + * since permissions aren't checked, + * envremove can't not remove it if its there. + */ + if(c->flag & CRCLOSE) + envremove(c); +} + +static long +envread(Chan *c, void *a, long n, vlong off) +{ + Egrp *eg; + Evalue *e; + ulong offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, envgen); + + eg = envgrp(c); + rlock(&eg->lk); + e = envlookup(eg, nil, c->qid.path); + if(e == 0) { + runlock(&eg->lk); + error(Enonexist); + } + + if(offset > e->len) /* protects against overflow converting vlong to ulong */ + n = 0; + else if(offset + n > e->len) + n = e->len - offset; + if(n <= 0) + n = 0; + else + memmove(a, e->value+offset, n); + runlock(&eg->lk); + return n; +} + +static long +envwrite(Chan *c, void *a, long n, vlong off) +{ + char *s; + ulong len; + Egrp *eg; + Evalue *e; + ulong offset = off; + + if(n <= 0) + return 0; + if(offset > Maxenvsize || n > (Maxenvsize - offset)) + error(Etoobig); + + eg = envgrp(c); + wlock(&eg->lk); + e = envlookup(eg, nil, c->qid.path); + if(e == 0) { + wunlock(&eg->lk); + error(Enonexist); + } + + len = offset+n; + if(len > e->len) { + s = smalloc(len); + if(e->value){ + memmove(s, e->value, e->len); + free(e->value); + } + e->value = s; + e->len = len; + } + memmove(e->value+offset, a, n); + e->qid.vers++; + eg->vers++; + wunlock(&eg->lk); + return n; +} + +Dev envdevtab = { + 'e', + "env", + + devreset, + devinit, + devshutdown, + envattach, + envwalk, + envstat, + envopen, + envcreate, + envclose, + envread, + devbread, + envwrite, + devbwrite, + envremove, + devwstat, +}; + +void +envcpy(Egrp *to, Egrp *from) +{ + int i; + Evalue *ne, *e; + + rlock(&from->lk); + to->ment = (from->nent+31)&~31; + to->ent = smalloc(to->ment*sizeof(to->ent[0])); + for(i=0; i<from->nent; i++){ + e = from->ent[i]; + ne = smalloc(sizeof(Evalue)); + ne->name = smalloc(strlen(e->name)+1); + strcpy(ne->name, e->name); + if(e->value){ + ne->value = smalloc(e->len); + memmove(ne->value, e->value, e->len); + ne->len = e->len; + } + ne->qid.path = ++to->path; + to->ent[i] = ne; + } + to->nent = from->nent; + runlock(&from->lk); +} + +void +closeegrp(Egrp *eg) +{ + int i; + Evalue *e; + + if(decref(&eg->ref) == 0){ + for(i=0; i<eg->nent; i++){ + e = eg->ent[i]; + free(e->name); + if(e->value) + free(e->value); + free(e); + } + free(eg->ent); + free(eg); + } +} + +static Egrp* +envgrp(Chan *c) +{ + if(c->aux == nil) + return up->egrp; + return c->aux; +} + +static int +envwriteable(Chan *c) +{ + return iseve() || c->aux == nil; +} + +/* + * to let the kernel set environment variables + */ +void +ksetenv(char *ename, char *eval, int conf) +{ + Chan *c; + char buf[2*KNAMELEN]; + + snprint(buf, sizeof(buf), "#e%s/%s", conf?"c":"", ename); + c = namec(buf, Acreate, OWRITE, 0600); + devtab[c->type]->write(c, eval, strlen(eval), 0); + cclose(c); +} + +/* + * Return a copy of configuration environment as a sequence of strings. + * The strings alternate between name and value. A zero length name string + * indicates the end of the list + */ +char * +getconfenv(void) +{ + Egrp *eg = &confegrp; + Evalue *e; + char *p, *q; + int i, n; + + rlock(&eg->lk); + if(waserror()) { + runlock(&eg->lk); + nexterror(); + } + + /* determine size */ + n = 0; + for(i=0; i<eg->nent; i++){ + e = eg->ent[i]; + n += strlen(e->name) + e->len + 2; + } + p = malloc(n + 1); + if(p == nil) + error(Enomem); + q = p; + for(i=0; i<eg->nent; i++){ + e = eg->ent[i]; + strcpy(q, e->name); + q += strlen(q) + 1; + memmove(q, e->value, e->len); + q[e->len] = 0; + /* move up to the first null */ + q += strlen(q) + 1; + } + *q = 0; + + poperror(); + runlock(&eg->lk); + return p; +} diff --git a/a/devenv.ed b/a/devenv.ed @@ -0,0 +1,5 @@ +g/^envgen/ s/Dirtab\*, int/Dirtab \*dt, int i/ +,s!lock(eg)!lock(\&eg->lk)!g +g/^envcreate/ s/ulong)/ulong perm)/ +,s!lock(from)!lock(\&from->lk)!g +,s!ref(eg)!ref(\&eg->ref)!g diff --git a/a/devether.c b/a/devether.c @@ -0,0 +1,542 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "error.h" +#include "netif.h" + +#include "etherif.h" + +extern int memsize; +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if(etherxx[ctlrno] == 0) + error(Enodev); + + chan = devattach('l', spec); + if(waserror()){ + chanfree(chan); + nexterror(); + } + chan->dev = ctlrno; + if(etherxx[ctlrno]->attach) + etherxx[ctlrno]->attach(etherxx[ctlrno]); + poperror(); + return chan; +} + +static Walkqid* +etherwalk(Chan* chan, Chan* nchan, char** name, int nname) +{ + return netifwalk(&etherxx[chan->dev]->ni, chan, nchan, name, nname); +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + return netifstat(&etherxx[chan->dev]->ni, chan, dp, n); +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + return netifopen(&etherxx[chan->dev]->ni, chan, omode); +} + +static void +ethercreate(Chan* ch, char* c, int i, ulong ul) +{ +} + +static void +etherclose(Chan* chan) +{ + netifclose(&etherxx[chan->dev]->ni, chan); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + + ether = etherxx[chan->dev]; + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid) + return ether->ifstat(ether, buf, n, offset); + else if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + + return netifread(&ether->ni, chan, buf, n, offset); +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + return netifbread(&etherxx[chan->dev]->ni, chan, n, offset); +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + return netifwstat(&etherxx[chan->dev]->ni, chan, dp, n); +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->tscticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->ni.inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = &ether->ni.f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multicast addresses */ + if(multi && memcmp(pkt->d, ether->ni.bcast, sizeof(pkt->d)) != 0 && ether->ni.prom == 0){ + if(!activemulti(&ether->ni, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->ni.f; fp < ep; fp++){ + if(f = *fp) + if(f->type == type || f->type < 0) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) { + print("soverflow for f->in\n"); + ether->ni.soverflows++; + } + } + else { + print("soverflow iallocb\n"); + ether->ni.soverflows++; + } + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) { + print("soverflow for fx->in\n"); + ether->ni.soverflows++; + } + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->ni.outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->ni.bcast, sizeof(pkt->d)) == 0 || ether->ni.prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + if(qfull(ether->oq)) + print("etheroq: WARNING: ether->oq full!\n"); + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + } else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong v) +{ + Ether *ether; + Block *bp; + int nn, onoff; + Cmdbuf *cb; + + ether = etherxx[chan->dev]; + if(NETTYPE(chan->qid.path) != Ndataqid) { + nn = netifwrite(&ether->ni, chan, buf, n); + if(nn >= 0) + return nn; + cb = parsecmd(buf, n); + if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + return n; + } + free(cb); + if(ether->ctl!=nil) + return ether->ctl(ether,buf,n); + + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + poperror(); + bp->wp += n; + + return etheroq(ether, bp); +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong u) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + + return etheroq(ether, bp); +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static Ether* +etherprobe(int cardno, int ctlrno) +{ + int i, lg; + ulong mb, bsz; + Ether *ether; + char buf[128], name[32]; + + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + ether->ni.mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + + if(cardno < 0){ + for(cardno = 0; cards[cardno].type; cardno++){ + for(i = 0; i < ether->isac.nopt; i++){ + if(strncmp(ether->isac.opt[i], "ea=", 3)) + continue; + if(parseether(ether->ea, &ether->isac.opt[i][3])) + memset(ether->ea, 0, Eaddrlen); + } + break; + } + } + + if(cardno >= MaxEther || cards[cardno].type == nil){ + free(ether); + return nil; + } + if(cards[cardno].reset(ether) < 0){ + free(ether); + return nil; + } + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(ether->isac.irq == 2) + ether->isac.irq = 9; + snprint(name, sizeof(name), "ether%d", ctlrno); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + ctlrno, cards[cardno].type, ether->ni.mbps, ether->isac.port, ether->isac.irq); + if(ether->isac.mem) + i += sprint(buf+i, " addr 0x%luX", ether->isac.mem); + if(ether->isac.size) + i += sprint(buf+i, " size 0x%luX", ether->isac.size); + i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + /* compute log10(ether->ni.mbps) into lg */ + for(lg = 0, mb = ether->ni.mbps; mb >= 10; lg++) + mb /= 10; + if (lg > 0) + lg--; + if (lg > 14) /* 2^(14+17) = 2⁳ⁱ */ + lg = 14; + /* allocate larger output queues for higher-speed interfaces */ + bsz = 1UL << (lg + 17); /* 2ⁱ⁷ = 128K, bsz = 2ⁿ × 128K */ + while (bsz > memsize && bsz > 128*1024) + bsz /= 2; + + netifinit(&ether->ni, name, Ntypes, bsz); + if(ether->oq == nil) { + ether->oq = qopen(bsz, Qmsg, 0, 0); + ether->ni.limit = bsz; + } + if(ether->oq == nil) + panic("etherreset %s: can't allocate output queue of %ld bytes", + name, bsz); + ether->ni.alen = Eaddrlen; + memmove(ether->ni.addr, ether->ea, Eaddrlen); + memset(ether->ni.bcast, 0xFF, Eaddrlen); + + return ether; +} + +static void +etherreset(void) +{ + Ether *ether; + int cardno, ctlrno; + + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if((ether = etherprobe(-1, ctlrno)) == nil) + continue; + etherxx[ctlrno] = ether; + } + + cardno = ctlrno = 0; + while(cards[cardno].type != nil && ctlrno < MaxEther){ + if(etherxx[ctlrno] != nil){ + ctlrno++; + continue; + } + if((ether = etherprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + etherxx[ctlrno] = ether; + ctlrno++; + } +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i = 0; i < MaxEther; i++){ + ether = etherxx[i]; + if(ether == nil) + continue; + if(ether->shutdown == nil) { + print("#l%d: no shutdown fuction\n", i); + continue; + } + (*ether->shutdown)(ether); + } +} + + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, +}; diff --git a/a/devether.ed b/a/devether.ed @@ -0,0 +1,34 @@ +/"pool\.h"/d +/^static Ether/i +extern int memsize; +. +,s!mainmem->maxsize / 8!memsize!g +,s/MACHP(0)->ticks/MACHP(0)->tscticks/g +/isaconfig/;/}/d +/cistrcmp/;+1d +/getconf/;+2d +/intrenable/-6;+6d +,s/(etherxx\[chan->dev\]/(\&etherxx\[chan->dev\]->ni/ +/^ethercreate/ s/Chan\*, char\*, int, ulong/Chan* ch, char* c, int i, ulong ul/ +/^etherwrite/ s/vlong/vlong v/ +/^etherbwrite/ s/ulong/ulong u/ +/activemulti(/ s/ether/\&&->ni/ +/netifread(/ s/ether/\&&->ni/ +/netifwrite(/ s/ether/\&&->ni/ +/netifinit(/ s/ether/\&&->ni/ +,s/ether->inpackets/ether->ni.inpackets/g +,s/ether->f/ether->ni.f/g +,s/ether->soverflows/ether->ni.soverflows/g +,s/ether->outpackets/ether->ni.outpackets/g +,s/ether->bcast/ether->ni.bcast/g +,s/ether->prom/ether->ni.prom/g +,s/ether->mbps/ether->ni.mbps/g +,s/ether->alen/ether->ni.alen/g +,s/ether->addr/ether->ni.addr/g +,s/ether->limit/ether->ni.limit/g +,s/ether->nopt/ether->isac.nopt/g +,s/ether->opt/ether->isac.opt/g +,s/ether->mem/ether->isac.mem/g +,s/ether->size/ether->isac.size/g +,s/ether->port/ether->isac.port/g +,s/ether->irq/ether->isac.irq/g diff --git a/a/devmnt.c b/a/devmnt.c @@ -0,0 +1,1210 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * References are managed as follows: + * The channel to the server - a network connection or pipe - has one + * reference for every Chan open on the server. The server channel has + * c->mux set to the Mnt used for muxing control to that server. Mnts + * have no reference count; they go away when c goes away. + * Each channel derived from the mount point has mchan set to c, + * and increfs/decrefs mchan to manage references on the server + * connection. + */ + +#define MAXRPC (IOHDRSZ+8192) + +struct Mntrpc +{ + Chan* c; /* Channel for whom we are working */ + Mntrpc* list; /* Free/pending list */ + Fcall request; /* Outgoing file system protocol message */ + Fcall reply; /* Incoming reply */ + Mnt* m; /* Mount device during rpc */ + Rendez r; /* Place to hang out */ + uchar* rpc; /* I/O Data buffer */ + uint rpclen; /* len of buffer */ + Block *b; /* reply blocks */ + char done; /* Rpc completed */ + uvlong stime; /* start time for mnt statistics */ + ulong reqlen; /* request length for mnt statistics */ + ulong replen; /* reply length for mnt statistics */ + Mntrpc* flushed; /* message this one flushes */ +}; + +enum +{ + TAGSHIFT = 5, /* ulong has to be 32 bits */ + TAGMASK = (1<<TAGSHIFT)-1, + NMASK = (64*1024)>>TAGSHIFT, +}; + +struct Mntalloc +{ + Lock lk; + Mnt* list; /* Mount devices in use */ + Mnt* mntfree; /* Free list */ + Mntrpc* rpcfree; + int nrpcfree; + int nrpcused; + ulong id; + ulong tagmask[NMASK]; +}mntalloc; + +Mnt* mntchk(Chan*); +void mntdirfix(uchar*, Chan*); +Mntrpc* mntflushalloc(Mntrpc*, ulong); +void mntflushfree(Mnt*, Mntrpc*); +void mntfree(Mntrpc*); +void mntgate(Mnt*); +void mntpntfree(Mnt*); +void mntqrm(Mnt*, Mntrpc*); +Mntrpc* mntralloc(Chan*, ulong); +long mntrdwr(int, Chan*, void*, long, vlong); +int mntrpcread(Mnt*, Mntrpc*); +void mountio(Mnt*, Mntrpc*); +void mountmux(Mnt*, Mntrpc*); +void mountrpc(Mnt*, Mntrpc*); +int rpcattn(void*); +Chan* mntchan(void); + +char Esbadstat[] = "invalid directory entry received from server"; +char Enoversion[] = "version not established for mount channel"; + + +void (*mntstats)(int, Chan*, uvlong, ulong); + +static void +mntreset(void) +{ + mntalloc.id = 1; + mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ + mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); +/* We can't install %M since eipfmt does and is used in the kernel [sape] */ + + cinit(); +} + +/* + * Version is not multiplexed: message sent only once per connection. + */ +long +mntversion(Chan *c, char *version, int msize, int returnlen) +{ + Fcall f; + uchar *msg; + Mnt *m; + char *v; + long k, l; + uvlong oo; + char buf[128]; + + qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ + if(waserror()){ + qunlock(&c->umqlock); + nexterror(); + } + + /* defaults */ + if(msize == 0) + msize = MAXRPC; + if(msize > c->iounit && c->iounit != 0) + msize = c->iounit; + v = version; + if(v == nil || v[0] == '\0') + v = VERSION9P; + + /* validity */ + if(msize < 0) + error("bad iounit in version call"); + if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) + error("bad 9P version specification"); + + m = c->mux; + + if(m != nil){ + qunlock(&c->umqlock); + poperror(); + + strecpy(buf, buf+sizeof buf, m->version); + k = strlen(buf); + if(strncmp(buf, v, k) != 0){ + snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); + error(buf); + } + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, buf, k); + } + return k; + } + + f.type = Tversion; + f.tag = NOTAG; + f.msize = msize; + f.version = v; + msg = malloc(8192+IOHDRSZ); + if(msg == nil) + exhausted("version memory"); + if(waserror()){ + free(msg); + nexterror(); + } + k = convS2M(&f, msg, 8192+IOHDRSZ); + if(k == 0) + error("bad fversion conversion on send"); + + lock(&c->ref.lk); + oo = c->offset; + c->offset += k; + unlock(&c->ref.lk); + + l = devtab[c->type]->write(c, msg, k, oo); + + if(l < k){ + lock(&c->ref.lk); + c->offset -= k - l; + unlock(&c->ref.lk); + error("short write in fversion"); + } + + /* message sent; receive and decode reply */ + k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); + if(k <= 0) + error("EOF receiving fversion reply"); + + lock(&c->ref.lk); + c->offset += k; + unlock(&c->ref.lk); + + l = convM2S(msg, k, &f); + if(l != k) + error("bad fversion conversion on reply"); + if(f.type != Rversion){ + if(f.type == Rerror) + error(f.ename); + error("unexpected reply type in fversion"); + } + if(f.msize > msize) + error("server tries to increase msize in fversion"); + if(f.msize<256 || f.msize>1024*1024) + error("nonsense value of msize in fversion"); + k = strlen(f.version); + if(strncmp(f.version, v, k) != 0) + error("bad 9P version returned from server"); + + /* now build Mnt associated with this connection */ + lock(&mntalloc.lk); + m = mntalloc.mntfree; + if(m != 0) + mntalloc.mntfree = m->list; + else { + m = malloc(sizeof(Mnt)); + if(m == 0) { + unlock(&mntalloc.lk); + exhausted("mount devices"); + } + } + m->list = mntalloc.list; + mntalloc.list = m; + m->version = nil; + kstrdup(&m->version, f.version); + m->id = mntalloc.id++; + m->q = qopen(10*MAXRPC, 0, nil, nil); + m->msize = f.msize; + unlock(&mntalloc.lk); + + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, f.version, k); + } + + poperror(); /* msg */ + free(msg); + + lock(&m->lk); + m->queue = 0; + m->rip = 0; + + c->flag |= CMSG; + c->mux = m; + m->c = c; + unlock(&m->lk); + + poperror(); /* c */ + qunlock(&c->umqlock); + + return k; +} + +Chan* +mntauth(Chan *c, char *spec) +{ + Mnt *m; + Mntrpc *r; + + m = c->mux; + + if(m == nil){ + mntversion(c, VERSION9P, MAXRPC, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tauth; + r->request.afid = c->fid; + r->request.uname = up->user; + r->request.aname = spec; + mountrpc(m, r); + + c->qid = r->reply.aqid; + c->mchan = m->c; + incref(&m->c->ref); + c->mqid = c->qid; + c->mode = ORDWR; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + return c; + +} + +static Chan* +mntattach(char *muxattach) +{ + Mnt *m; + Chan *c; + Mntrpc *r; + struct bogus{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + bogus = *((struct bogus *)muxattach); + c = bogus.chan; + + { // Plan 9 VX addition + extern Dev mntloopdevtab; + Chan *mc; + if(devtab[c->type] == &mntloopdevtab){ + if(bogus.authchan || (bogus.spec && bogus.spec[0])) + error(Ebadarg); + mc = c->aux; + incref(&mc->ref); + return mc; + } + } + + + m = c->mux; + + if(m == nil){ + mntversion(c, nil, 0, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tattach; + r->request.fid = c->fid; + if(bogus.authchan == nil) + r->request.afid = NOFID; + else + r->request.afid = bogus.authchan->fid; + r->request.uname = up->user; + r->request.aname = bogus.spec; + mountrpc(m, r); + + c->qid = r->reply.qid; + c->mchan = m->c; + incref(&m->c->ref); + c->mqid = c->qid; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + if(bogus.flags&MCACHE) + c->flag |= CCACHE; + return c; +} + +Chan* +mntchan(void) +{ + Chan *c; + + c = devattach('M', 0); + lock(&mntalloc.lk); + c->dev = mntalloc.id++; + unlock(&mntalloc.lk); + + if(c->mchan) + panic("mntchan non-zero %p", c->mchan); + return c; +} + +static Walkqid* +mntwalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i, alloc; + Mnt *m; + Mntrpc *r; + Walkqid *wq; + + if(nc != nil) + print("mntwalk: nc != nil\n"); + if(nname > MAXWELEM) + error("devmnt: too many name elements"); + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + + alloc = 0; + m = mntchk(c); + r = mntralloc(c, m->msize); + if(nc == nil){ + nc = devclone(c); + /* + * Until the other side accepts this fid, we can't mntclose it. + * Therefore set type to 0 for now; rootclose is known to be safe. + */ + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twalk; + r->request.fid = c->fid; + r->request.newfid = nc->fid; + r->request.nwname = nname; + memmove(r->request.wname, name, nname*sizeof(char*)); + + mountrpc(m, r); + + if(r->reply.nwqid > nname) + error("too many QIDs returned by walk"); + if(r->reply.nwqid < nname){ + if(alloc) + cclose(nc); + wq->clone = nil; + if(r->reply.nwqid == 0){ + free(wq); + wq = nil; + goto Return; + } + } + + /* move new fid onto mnt device and update its qid */ + if(wq->clone != nil){ + if(wq->clone != c){ + wq->clone->type = c->type; + wq->clone->mchan = c->mchan; + incref(&c->mchan->ref); + } + if(r->reply.nwqid > 0) + wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; + } + wq->nqid = r->reply.nwqid; + for(i=0; i<wq->nqid; i++) + wq->qid[i] = r->reply.wqid[i]; + + Return: + poperror(); + mntfree(r); + poperror(); + return wq; +} + +static int +mntstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + if(n < BIT16SZ) + error(Eshortstat); + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Tstat; + r->request.fid = c->fid; + mountrpc(m, r); + + if(r->reply.nstat > n){ + n = BIT16SZ; + PBIT16((uchar*)dp, r->reply.nstat-2); + }else{ + n = r->reply.nstat; + memmove(dp, r->reply.stat, n); + validstat(dp, n); + mntdirfix(dp, c); + } + poperror(); + mntfree(r); + return n; +} + +static Chan* +mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.mode = omode; + if(type == Tcreate){ + r->request.perm = perm; + r->request.name = name; + } + mountrpc(m, r); + + c->qid = r->reply.qid; + c->offset = 0; + c->mode = openmode(omode); + c->iounit = r->reply.iounit; + if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ) + c->iounit = m->msize-IOHDRSZ; + c->flag |= COPEN; + poperror(); + mntfree(r); + + if(c->flag & CCACHE) + copen(c); + + return c; +} + +static Chan* +mntopen(Chan *c, int omode) +{ + return mntopencreate(Topen, c, nil, omode, 0); +} + +static void +mntcreate(Chan *c, char *name, int omode, ulong perm) +{ + mntopencreate(Tcreate, c, name, omode, perm); +} + +static void +mntclunk(Chan *c, int t) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()){ + mntfree(r); + nexterror(); + } + + r->request.type = t; + r->request.fid = c->fid; + mountrpc(m, r); + mntfree(r); + poperror(); +} + +void +muxclose(Mnt *m) +{ + Mntrpc *q, *r; + + for(q = m->queue; q; q = r) { + r = q->list; + mntfree(q); + } + m->id = 0; + free(m->version); + m->version = nil; + mntpntfree(m); +} + +void +mntpntfree(Mnt *m) +{ + Mnt *f, **l; + Queue *q; + + lock(&mntalloc.lk); + l = &mntalloc.list; + for(f = *l; f; f = f->list) { + if(f == m) { + *l = m->list; + break; + } + l = &f->list; + } + m->list = mntalloc.mntfree; + mntalloc.mntfree = m; + q = m->q; + unlock(&mntalloc.lk); + + qfree(q); +} + +static void +mntclose(Chan *c) +{ + mntclunk(c, Tclunk); +} + +static void +mntremove(Chan *c) +{ + mntclunk(c, Tremove); +} + +static int +mntwstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twstat; + r->request.fid = c->fid; + r->request.nstat = n; + r->request.stat = dp; + mountrpc(m, r); + poperror(); + mntfree(r); + return n; +} + +static long +mntread(Chan *c, void *buf, long n, vlong off) +{ + uchar *p, *e; + int nc, cache, isdir, dirlen; + + isdir = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) { + cache = 0; + isdir = 1; + } + + p = buf; + if(cache) { + nc = cread(c, buf, n, off); + if(nc > 0) { + n -= nc; + if(n == 0) + return nc; + p += nc; + off += nc; + } + n = mntrdwr(Tread, c, p, n, off); + cupdate(c, p, n, off); + return n + nc; + } + + n = mntrdwr(Tread, c, buf, n, off); + if(isdir) { + for(e = &p[n]; p+BIT16SZ < e; p += dirlen){ + dirlen = BIT16SZ+GBIT16(p); + if(p+dirlen > e) + break; + validstat(p, dirlen); + mntdirfix(p, c); + } + if(p != e) + error(Esbadstat); + } + return n; +} + +static long +mntwrite(Chan *c, void *buf, long n, vlong off) +{ + return mntrdwr(Twrite, c, buf, n, off); +} + +long +mntrdwr(int type, Chan *c, void *buf, long n, vlong off) +{ + Mnt *m; + Mntrpc *r; + char *uba; + int cache; + ulong cnt, nr, nreq; + + m = mntchk(c); + uba = buf; + cnt = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) + cache = 0; + for(;;) { + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.offset = off; + r->request.data = uba; + nr = n; + if(nr > m->msize-IOHDRSZ) + nr = m->msize-IOHDRSZ; + r->request.count = nr; + mountrpc(m, r); + nreq = r->request.count; + nr = r->reply.count; + if(nr > nreq) + nr = nreq; + + if(type == Tread) + r->b = bl2mem((uchar*)uba, r->b, nr); + else if(cache) + cwrite(c, (uchar*)uba, nr, off); + + poperror(); + mntfree(r); + off += nr; + uba += nr; + cnt += nr; + n -= nr; + if(nr != nreq || n == 0 || up->nnote) + break; + } + return cnt; +} + +void +mountrpc(Mnt *m, Mntrpc *r) +{ + char *sn, *cn; + int t; + + r->reply.tag = 0; + r->reply.type = Tmax; /* can't ever be a valid message type */ + + mountio(m, r); + + t = r->reply.type; + switch(t) { + case Rerror: + error(r->reply.ename); + case Rflush: + error(Eintr); + default: + if(t == r->request.type+1) + break; + sn = "?"; + if(m->c->path != nil) + sn = m->c->path->s; + cn = "?"; + if(r->c != nil && r->c->path != nil) + cn = r->c->path->s; + print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n", + up->text, up->pid, sn, cn, + r, r->request.tag, r->request.fid, r->request.type, + r->reply.type, r->reply.tag); + error(Emountrpc); + } +} + +void +mountio(Mnt *m, Mntrpc *r) +{ + int n; + + while(waserror()) { + if(m->rip == up) + mntgate(m); + if(strcmp(up->errstr, Eintr) != 0){ + mntflushfree(m, r); + nexterror(); + } + r = mntflushalloc(r, m->msize); + } + + lock(&m->lk); + r->m = m; + r->list = m->queue; + m->queue = r; + unlock(&m->lk); + + /* Transmit a file system rpc */ + if(m->msize == 0) + panic("msize"); + n = convS2M(&r->request, r->rpc, m->msize); + if(n < 0) + panic("bad message type in mountio"); + if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n) + error(Emountrpc); + r->stime = fastticks(nil); + r->reqlen = n; + + /* Gate readers onto the mount point one at a time */ + for(;;) { + lock(&m->lk); + if(m->rip == 0) + break; + unlock(&m->lk); + sleep(&r->r, rpcattn, r); + if(r->done){ + poperror(); + mntflushfree(m, r); + return; + } + } + m->rip = up; + unlock(&m->lk); + while(r->done == 0) { + if(mntrpcread(m, r) < 0) + error(Emountrpc); + mountmux(m, r); + } + mntgate(m); + poperror(); + mntflushfree(m, r); +} + +static int +doread(Mnt *m, int len) +{ + Block *b; + + while(qlen(m->q) < len){ + b = devtab[m->c->type]->bread(m->c, m->msize, 0); + if(b == nil) + return -1; + if(blocklen(b) == 0){ + freeblist(b); + return -1; + } + qaddlist(m->q, b); + } + return 0; +} + +int +mntrpcread(Mnt *m, Mntrpc *r) +{ + int i, t, len, hlen; + Block *b, **l, *nb; + + r->reply.type = 0; + r->reply.tag = 0; + + /* read at least length, type, and tag and pullup to a single block */ + if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0) + return -1; + nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ); + + /* read in the rest of the message, avoid ridiculous (for now) message sizes */ + len = GBIT32(nb->rp); + if(len > m->msize){ + qdiscard(m->q, qlen(m->q)); + return -1; + } + if(doread(m, len) < 0) + return -1; + + /* pullup the header (i.e. everything except data) */ + t = nb->rp[BIT32SZ]; + switch(t){ + case Rread: + hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ; + break; + default: + hlen = len; + break; + } + nb = pullupqueue(m->q, hlen); + + if(convM2S(nb->rp, len, &r->reply) <= 0){ + /* bad message, dump it */ + print("mntrpcread: convM2S failed\n"); + qdiscard(m->q, len); + return -1; + } + + /* hang the data off of the fcall struct */ + l = &r->b; + *l = nil; + do { + b = qremove(m->q); + if(hlen > 0){ + b->rp += hlen; + len -= hlen; + hlen = 0; + } + i = BLEN(b); + if(i <= len){ + len -= i; + *l = b; + l = &(b->next); + } else { + /* split block and put unused bit back */ + nb = allocb(i-len); + memmove(nb->wp, b->rp+len, i-len); + b->wp = b->rp+len; + nb->wp += i-len; + qputback(m->q, nb); + *l = b; + return 0; + } + }while(len > 0); + + return 0; +} + +void +mntgate(Mnt *m) +{ + Mntrpc *q; + + lock(&m->lk); + m->rip = 0; + for(q = m->queue; q; q = q->list) { + if(q->done == 0) + if(wakeup(&q->r)) + break; + } + unlock(&m->lk); +} + +void +mountmux(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *q; + + lock(&m->lk); + l = &m->queue; + for(q = *l; q; q = q->list) { + /* look for a reply to a message */ + if(q->request.tag == r->reply.tag) { + *l = q->list; + if(q != r) { + /* + * Completed someone else. + * Trade pointers to receive buffer. + */ + q->reply = r->reply; + q->b = r->b; + r->b = nil; + } + q->done = 1; + unlock(&m->lk); + if(mntstats != nil) + (*mntstats)(q->request.type, + m->c, q->stime, + q->reqlen + r->replen); + if(q != r) + wakeup(&q->r); + return; + } + l = &q->list; + } + unlock(&m->lk); + print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type); +} + +/* + * Create a new flush request and chain the previous + * requests from it + */ +Mntrpc* +mntflushalloc(Mntrpc *r, ulong iounit) +{ + Mntrpc *fr; + + fr = mntralloc(0, iounit); + + fr->request.type = Tflush; + if(r->request.type == Tflush) + fr->request.oldtag = r->request.oldtag; + else + fr->request.oldtag = r->request.tag; + fr->flushed = r; + + return fr; +} + +/* + * Free a chain of flushes. Remove each unanswered + * flush and the original message from the unanswered + * request queue. Mark the original message as done + * and if it hasn't been answered set the reply to to + * Rflush. + */ +void +mntflushfree(Mnt *m, Mntrpc *r) +{ + Mntrpc *fr; + + while(r){ + fr = r->flushed; + if(!r->done){ + r->reply.type = Rflush; + mntqrm(m, r); + } + if(fr) + mntfree(r); + r = fr; + } +} + +int +alloctag(void) +{ + int i, j; + ulong v; + + for(i = 0; i < NMASK; i++){ + v = mntalloc.tagmask[i]; + if(v == ~0UL) + continue; + for(j = 0; j < 1<<TAGSHIFT; j++) + if((v & (1<<j)) == 0){ + mntalloc.tagmask[i] |= 1<<j; + return (i<<TAGSHIFT) + j; + } + } + panic("no friggin tags left"); + return NOTAG; +} + +void +freetag(int t) +{ + mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK)); +} + +Mntrpc* +mntralloc(Chan *c, ulong msize) +{ + Mntrpc *new; + + lock(&mntalloc.lk); + new = mntalloc.rpcfree; + if(new == nil){ + new = malloc(sizeof(Mntrpc)); + if(new == nil) { + unlock(&mntalloc.lk); + exhausted("mount rpc header"); + } + /* + * The header is split from the data buffer as + * mountmux may swap the buffer with another header. + */ + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + unlock(&mntalloc.lk); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + new->request.tag = alloctag(); + } + else { + mntalloc.rpcfree = new->list; + mntalloc.nrpcfree--; + if(new->rpclen < msize){ + free(new->rpc); + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + mntalloc.nrpcused--; + unlock(&mntalloc.lk); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + } + } + mntalloc.nrpcused++; + unlock(&mntalloc.lk); + new->c = c; + new->done = 0; + new->flushed = nil; + new->b = nil; + return new; +} + +void +mntfree(Mntrpc *r) +{ + if(r->b != nil) + freeblist(r->b); + lock(&mntalloc.lk); + if(mntalloc.nrpcfree >= 10){ + free(r->rpc); + freetag(r->request.tag); + free(r); + } + else{ + r->list = mntalloc.rpcfree; + mntalloc.rpcfree = r; + mntalloc.nrpcfree++; + } + mntalloc.nrpcused--; + unlock(&mntalloc.lk); +} + +void +mntqrm(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *f; + + lock(&m->lk); + r->done = 1; + + l = &m->queue; + for(f = *l; f; f = f->list) { + if(f == r) { + *l = r->list; + break; + } + l = &f->list; + } + unlock(&m->lk); +} + +Mnt* +mntchk(Chan *c) +{ + Mnt *m; + + /* This routine is mostly vestiges of prior lives; now it's just sanity checking */ + + if(c->mchan == nil) + panic("mntchk 1: nil mchan c %s\n", chanpath(c)); + + m = c->mchan->mux; + + if(m == nil) + print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan)); + + /* + * Was it closed and reused (was error(Eshutdown); now, it cannot happen) + */ + if(m->id == 0 || m->id >= c->dev) + panic("mntchk 3: can't happen"); + + return m; +} + +/* + * Rewrite channel type and dev for in-flight data to + * reflect local values. These entries are known to be + * the first two in the Dir encoding after the count. + */ +void +mntdirfix(uchar *dirbuf, Chan *c) +{ + uint r; + + r = devtab[c->type]->dc; + dirbuf += BIT16SZ; /* skip count */ + PBIT16(dirbuf, r); + dirbuf += BIT16SZ; + PBIT32(dirbuf, c->dev); +} + +int +rpcattn(void *v) +{ + Mntrpc *r; + + r = v; + return r->done || r->m->rip == 0; +} + +Dev mntdevtab = { + 'M', + "mnt", + + mntreset, + devinit, + devshutdown, + mntattach, + mntwalk, + mntstat, + mntopen, + mntcreate, + mntclose, + mntread, + devbread, + mntwrite, + devbwrite, + mntremove, + mntwstat, +}; diff --git a/a/devmnt.ed b/a/devmnt.ed @@ -0,0 +1,21 @@ +,s!Lock;!Lock lk;! +,s!lock(c)!lock(\&c->ref.lk)!g +,s!lock(\&mntalloc)!lock(\&mntalloc.lk)!g +,s!lock(m)!lock(\&m->lk)!g +,s!ref(m->c)!ref(\&m->c->ref)!g +,s!ref(c->mchan)!ref(\&c->mchan->ref)!g +/c = bogus.chan/a + + { // Plan 9 VX addition + extern Dev mntloopdevtab; + Chan *mc; + if(devtab[c->type] == &mntloopdevtab){ + if(bogus.authchan || (bogus.spec && bogus.spec[0])) + error(Ebadarg); + mc = c->aux; + incref(&mc->ref); + return mc; + } + } + +. diff --git a/a/devpipe.c b/a/devpipe.c @@ -0,0 +1,391 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "netif.h" + +typedef struct Pipe Pipe; +struct Pipe +{ + QLock lk; + Pipe *next; + int ref; + ulong path; + Queue *q[2]; + int qref[2]; +}; + +struct +{ + Lock lk; + ulong path; +} pipealloc; + +enum +{ + Qdir, + Qdata0, + Qdata1, +}; + +Dirtab pipedir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "data", {Qdata0}, 0, 0600, + "data1", {Qdata1}, 0, 0600, +}; +#define NPIPEDIR 3 + +static void +pipeinit(void) +{ + if(conf.pipeqsize == 0){ + if(conf.nmach > 1) + conf.pipeqsize = 256*1024; + else + conf.pipeqsize = 32*1024; + } +} + +/* + * create a pipe, no streams are created until an open + */ +static Chan* +pipeattach(char *spec) +{ + Pipe *p; + Chan *c; + + c = devattach('|', spec); + p = malloc(sizeof(Pipe)); + if(p == 0) + exhausted("memory"); + p->ref = 1; + + p->q[0] = qopen(conf.pipeqsize, 0, 0, 0); + if(p->q[0] == 0){ + free(p); + exhausted("memory"); + } + p->q[1] = qopen(conf.pipeqsize, 0, 0, 0); + if(p->q[1] == 0){ + free(p->q[0]); + free(p); + exhausted("memory"); + } + + lock(&pipealloc.lk); + p->path = ++pipealloc.path; + unlock(&pipealloc.lk); + + mkqid(&c->qid, NETQID(2*p->path, Qdir), 0, QTDIR); + c->aux = p; + c->dev = 0; + return c; +} + +static int +pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Qid q; + int len; + Pipe *p; + + if(i == DEVDOTDOT){ + devdir(c, c->qid, "#|", 0, eve, DMDIR|0555, dp); + return 1; + } + i++; /* skip . */ + if(tab==0 || i>=ntab) + return -1; + + tab += i; + p = c->aux; + switch((ulong)tab->qid.path){ + case Qdata0: + len = qlen(p->q[0]); + break; + case Qdata1: + len = qlen(p->q[1]); + break; + default: + len = tab->length; + break; + } + mkqid(&q, NETQID(NETID(c->qid.path), tab->qid.path), 0, QTFILE); + devdir(c, q, tab->name, len, eve, tab->perm, dp); + return 1; +} + + +static Walkqid* +pipewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Pipe *p; + + wq = devwalk(c, nc, name, nname, pipedir, NPIPEDIR, pipegen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + p = c->aux; + qlock(&p->lk); + p->ref++; + if(c->flag & COPEN){ + print("channel open in pipewalk\n"); + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + } + qunlock(&p->lk); + } + return wq; +} + +static int +pipestat(Chan *c, uchar *db, int n) +{ + Pipe *p; + Dir dir; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); + break; + case Qdata0: + devdir(c, c->qid, "data", qlen(p->q[0]), eve, 0600, &dir); + break; + case Qdata1: + devdir(c, c->qid, "data1", qlen(p->q[1]), eve, 0600, &dir); + break; + default: + panic("pipestat"); + } + n = convD2M(&dir, db, n); + if(n < BIT16SZ) + error(Eshortstat); + return n; +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +pipeopen(Chan *c, int omode) +{ + Pipe *p; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + p = c->aux; + qlock(&p->lk); + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + qunlock(&p->lk); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +pipeclose(Chan *c) +{ + Pipe *p; + + p = c->aux; + qlock(&p->lk); + + if(c->flag & COPEN){ + /* + * closing either side hangs up the stream + */ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]--; + if(p->qref[0] == 0){ + qhangup(p->q[1], 0); + qclose(p->q[0]); + } + break; + case Qdata1: + p->qref[1]--; + if(p->qref[1] == 0){ + qhangup(p->q[0], 0); + qclose(p->q[1]); + } + break; + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(p->qref[0] == 0 && p->qref[1] == 0){ + qreopen(p->q[0]); + qreopen(p->q[1]); + } + + /* + * free the structure on last close + */ + p->ref--; + if(p->ref == 0){ + qunlock(&p->lk); + free(p->q[0]); + free(p->q[1]); + free(p); + } else + qunlock(&p->lk); +} + +static long +piperead(Chan *c, void *va, long n, vlong offset) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + return devdirread(c, va, n, pipedir, NPIPEDIR, pipegen); + case Qdata0: + return qread(p->q[0], va, n); + case Qdata1: + return qread(p->q[1], va, n); + default: + panic("piperead"); + } + return -1; /* not reached */ +} + +static Block* +pipebread(Chan *c, long n, ulong offset) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + return qbread(p->q[0], n); + case Qdata1: + return qbread(p->q[1], n); + } + + return devbread(c, n, offset); +} + +/* + * a write to a closed pipe causes a note to be sent to + * the process. + */ +static long +pipewrite(Chan *c, void *va, long n, vlong offset) +{ + Pipe *p; + + if(!islo()) + print("pipewrite hi %#p\n", getcallerpc(&c)); + if(waserror()) { + /* avoid notes when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) + postnote(up, 1, "sys: write on closed pipe", NUser); + nexterror(); + } + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qwrite(p->q[1], va, n); + break; + + case Qdata1: + n = qwrite(p->q[0], va, n); + break; + + default: + panic("pipewrite"); + } + + poperror(); + return n; +} + +static long +pipebwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + Pipe *p; + + if(waserror()) { + /* avoid notes when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) + postnote(up, 1, "sys: write on closed pipe", NUser); + nexterror(); + } + + p = c->aux; + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qbwrite(p->q[1], bp); + break; + + case Qdata1: + n = qbwrite(p->q[0], bp); + break; + + default: + n = 0; + panic("pipebwrite"); + } + + poperror(); + return n; +} + +Dev pipedevtab = { + '|', + "pipe", + + devreset, + pipeinit, + devshutdown, + pipeattach, + pipewalk, + pipestat, + pipeopen, + devcreate, + pipeclose, + piperead, + pipebread, + pipewrite, + pipebwrite, + devremove, + devwstat, +}; diff --git a/a/devpipe.ed b/a/devpipe.ed @@ -0,0 +1,5 @@ +,s!Lock;!Lock lk;!g +,s!lock(\&pipealloc)!lock(\&pipealloc.lk)!g +,s!lock(p)!lock(\&p->lk)!g +g/^pipe/ s/long)/long offset)/ +g/^pipegen/ s/char\*,/char *name,/ diff --git a/a/devproc.c b/a/devproc.c @@ -0,0 +1,1447 @@ +#include "u.h" +#include "trace.h" +#include "tos.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "ureg.h" + +extern uchar _end[]; // Plan 9 VX + +enum +{ + Qdir, + Qtrace, + Qargs, + Qctl, + Qfd, + Qfpregs, + Qkregs, + Qmem, + Qnote, + Qnoteid, + Qnotepg, + Qns, + Qproc, + Qregs, + Qsegment, + Qstatus, + Qtext, + Qwait, + Qprofile, + Qsyscall, +}; + +enum +{ + CMclose, + CMclosefiles, + CMfixedpri, + CMhang, + CMkill, + CMnohang, + CMnoswap, + CMpri, + CMprivate, + CMprofile, + CMstart, + CMstartstop, + CMstartsyscall, + CMstop, + CMwaitstop, + CMwired, + CMtrace, +}; + +enum{ + Nevents = 0x4000, + Emask = Nevents - 1, +}; + +#define STATSIZE (2*KNAMELEN+12+9*12) +/* + * Status, fd, and ns are left fully readable (0444) because of their use in debugging, + * particularly on shared servers. + * Arguably, ns and fd shouldn't be readable; if you'd prefer, change them to 0000 + */ +Dirtab procdir[] = +{ + "args", {Qargs}, 0, 0660, + "ctl", {Qctl}, 0, 0000, + "fd", {Qfd}, 0, 0444, + "fpregs", {Qfpregs}, sizeof(FPsave), 0000, + "kregs", {Qkregs}, sizeof(Ureg), 0400, + "mem", {Qmem}, 0, 0000, + "note", {Qnote}, 0, 0000, + "noteid", {Qnoteid}, 0, 0664, + "notepg", {Qnotepg}, 0, 0000, + "ns", {Qns}, 0, 0444, + "proc", {Qproc}, 0, 0400, + "regs", {Qregs}, sizeof(Ureg), 0000, + "segment", {Qsegment}, 0, 0444, + "status", {Qstatus}, STATSIZE, 0444, + "text", {Qtext}, 0, 0000, + "wait", {Qwait}, 0, 0400, + "profile", {Qprofile}, 0, 0400, + "syscall", {Qsyscall}, 0, 0400, +}; + +static +Cmdtab proccmd[] = { + CMclose, "close", 2, + CMclosefiles, "closefiles", 1, + CMfixedpri, "fixedpri", 2, + CMhang, "hang", 1, + CMnohang, "nohang", 1, + CMnoswap, "noswap", 1, + CMkill, "kill", 1, + CMpri, "pri", 2, + CMprivate, "private", 1, + CMprofile, "profile", 1, + CMstart, "start", 1, + CMstartstop, "startstop", 1, + CMstartsyscall, "startsyscall", 1, + CMstop, "stop", 1, + CMwaitstop, "waitstop", 1, + CMwired, "wired", 2, + CMtrace, "trace", 0, +}; + +/* Segment type from portdat.h */ +static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", }; + +/* + * Qids are, in path: + * 4 bits of file type (qids above) + * 23 bits of process slot number + 1 + * in vers, + * 32 bits of pid, for consistency checking + * If notepg, c->pgrpid.path is pgrp slot, .vers is noteid. + */ +#define QSHIFT 5 /* location in qid of proc slot # */ + +#define QID(q) ((((ulong)(q).path)&0x0000001F)>>0) +#define SLOT(q) (((((ulong)(q).path)&0x07FFFFFE0)>>QSHIFT)-1) +#define PID(q) ((q).vers) +#define NOTEID(q) ((q).vers) + +void procctlreq(Proc*, char*, int); +int procctlmemio(Proc*, ulong, int, void*, int); +Chan* proctext(Chan*, Proc*); +Segment* txt2data(Proc*, Segment*); +int procstopped(void*); +void mntscan(Mntwalk*, Proc*); + +static Traceevent *tevents; +static Lock tlock; +static int topens; +static int tproduced, tconsumed; +void (*proctrace)(Proc*, int, vlong); + +extern int unfair; + +static void +profclock(Ureg *ur, Timer *t) +{ +} + +static int +procgen(Chan *c, char *name, Dirtab *tab, int _, int s, Dir *dp) +{ + Qid qid; + Proc *p; + char *ename; + Segment *q; + ulong pid, path, perm, len; + + if(s == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#p", 0, eve, 0555, dp); + return 1; + } + + if(c->qid.path == Qdir){ + if(s == 0){ + strcpy(up->genbuf, "trace"); + mkqid(&qid, Qtrace, -1, QTFILE); + devdir(c, qid, up->genbuf, 0, eve, 0444, dp); + return 1; + } + + if(name != nil){ + /* ignore s and use name to find pid */ + pid = strtol(name, &ename, 10); + if(pid==0 || ename[0]!='\0') + return -1; + s = procindex(pid); + if(s < 0) + return -1; + } + else if(--s >= conf.nproc) + return -1; + + p = proctab(s); + pid = p->pid; + if(pid == 0) + return 0; + sprint(up->genbuf, "%lud", pid); + /* + * String comparison is done in devwalk so name must match its formatted pid + */ + if(name != nil && strcmp(name, up->genbuf) != 0) + return -1; + mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR); + devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp); + return 1; + } + if(c->qid.path == Qtrace){ + strcpy(up->genbuf, "trace"); + mkqid(&qid, Qtrace, -1, QTFILE); + devdir(c, qid, up->genbuf, 0, eve, 0444, dp); + return 1; + } + if(s >= nelem(procdir)) + return -1; + if(tab) + panic("procgen"); + + tab = &procdir[s]; + path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */ + + p = proctab(SLOT(c->qid)); + perm = tab->perm; + if(perm == 0) + perm = p->procmode; + else /* just copy read bits */ + perm |= p->procmode & 0444; + + len = tab->length; + switch(QID(c->qid)) { + case Qwait: + len = p->nwait; /* incorrect size, but >0 means there's something to read */ + break; + case Qprofile: + q = p->seg[TSEG]; + if(q && q->profile) { + len = (q->top-q->base)>>LRESPROF; + len *= sizeof(*q->profile); + } + break; + } + + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, p->user, perm, dp); + return 1; +} + +static void +_proctrace(Proc* p, int etype, vlong ts) +{ + Traceevent *te; + + if (p->trace == 0 || topens == 0 || + tproduced - tconsumed >= Nevents) + return; + + te = &tevents[tproduced&Emask]; + te->pid = p->pid; + te->etype = etype; + if (ts == 0) + te->time = todget(nil); + else + te->time = ts; + tproduced++; +} + +static void +procinit(void) +{ + if(conf.nproc >= (1<<(16-QSHIFT))-1) + print("warning: too many procs for devproc\n"); + addclock0link((void (*)(void))profclock, 113); /* Relative prime to HZ */ +} + +static Chan* +procattach(char *spec) +{ + return devattach('p', spec); +} + +static Walkqid* +procwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, procgen); +} + +static int +procstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, procgen); +} + +/* + * none can't read or write state on other + * processes. This is to contain access of + * servers running as none should they be + * subverted by, for example, a stack attack. + */ +static void +nonone(Proc *p) +{ + if(p == up) + return; + if(strcmp(up->user, "none") != 0) + return; + if(iseve()) + return; + error(Eperm); +} + +static Chan* +procopen(Chan *c, int omode) +{ + Proc *p; + Pgrp *pg; + Chan *tc; + int pid; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, procgen); + + if(QID(c->qid) == Qtrace){ + if (omode != OREAD) + error(Eperm); + lock(&tlock); + if (waserror()){ + unlock(&tlock); + nexterror(); + } + if (topens > 0) + error("already open"); + topens++; + if (tevents == nil){ + tevents = (Traceevent*)malloc(sizeof(Traceevent) * Nevents); + if(tevents == nil) + error(Enomem); + tproduced = tconsumed = 0; + } + proctrace = _proctrace; + unlock(&tlock); + poperror(); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + p = proctab(SLOT(c->qid)); + qlock(&p->debug); + if(waserror()){ + qunlock(&p->debug); + nexterror(); + } + pid = PID(c->qid); + if(p->pid != pid) + error(Eprocdied); + + omode = openmode(omode); + + switch(QID(c->qid)){ + case Qtext: + if(omode != OREAD) + error(Eperm); + tc = proctext(c, p); + tc->offset = 0; + qunlock(&p->debug); + poperror(); + return tc; + + case Qproc: + case Qkregs: + case Qsegment: + case Qprofile: + case Qfd: + if(omode != OREAD) + error(Eperm); + break; + + case Qnote: + if(p->privatemem) + error(Eperm); + break; + + case Qmem: + case Qctl: + if(p->privatemem) + error(Eperm); + nonone(p); + break; + + case Qargs: + case Qnoteid: + case Qstatus: + case Qwait: + case Qregs: + case Qfpregs: + case Qsyscall: + nonone(p); + break; + + case Qns: + if(omode != OREAD) + error(Eperm); + c->aux = malloc(sizeof(Mntwalk)); + break; + + case Qnotepg: + nonone(p); + pg = p->pgrp; + if(pg == nil) + error(Eprocdied); + if(omode!=OWRITE || pg->pgrpid == 1) + error(Eperm); + c->pgrpid.path = pg->pgrpid+1; + c->pgrpid.vers = p->noteid; + break; + + default: + pprint("procopen %#lux\n", QID(c->qid)); + error(Egreg); + } + + /* Affix pid to qid */ + if(p->state != Dead) + c->qid.vers = p->pid; + + /* make sure the process slot didn't get reallocated while we were playing */ + coherence(); + if(p->pid != pid) + error(Eprocdied); + + tc = devopen(c, omode, 0, 0, procgen); + qunlock(&p->debug); + poperror(); + + return tc; +} + +static int +procwstat(Chan *c, uchar *db, int n) +{ + Proc *p; + Dir *d; + + if(c->qid.type&QTDIR) + error(Eperm); + + if(QID(c->qid) == Qtrace) + return devwstat(c, db, n); + + p = proctab(SLOT(c->qid)); + nonone(p); + d = nil; + if(waserror()){ + free(d); + qunlock(&p->debug); + nexterror(); + } + qlock(&p->debug); + + if(p->pid != PID(c->qid)) + error(Eprocdied); + + if(strcmp(up->user, p->user) != 0 && strcmp(up->user, eve) != 0) + error(Eperm); + + d = smalloc(sizeof(Dir)+n); + n = convM2D(db, n, &d[0], (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d->uid) && strcmp(d->uid, p->user) != 0){ + if(strcmp(up->user, eve) != 0) + error(Eperm); + else + kstrdup(&p->user, d->uid); + } + if(d->mode != ~0UL) + p->procmode = d->mode&0777; + + poperror(); + free(d); + qunlock(&p->debug); + return n; +} + + +static long +procoffset(long offset, char *va, int *np) +{ + if(offset > 0) { + offset -= *np; + if(offset < 0) { + memmove(va, va+*np+offset, -offset); + *np = -offset; + } + else + *np = 0; + } + return offset; +} + +static int +procqidwidth(Chan *c) +{ + char buf[32]; + + return sprint(buf, "%lud", c->qid.vers); +} + +int +procfdprint(Chan *c, int fd, int w, char *s, int ns) +{ + int n; + + if(w == 0) + w = procqidwidth(c); + n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n", + fd, + &"r w rw"[(c->mode&3)<<1], + devtab[c->type]->dc, c->dev, + c->qid.path, w, c->qid.vers, c->qid.type, + c->iounit, c->offset, c->path->s); + return n; +} + +static int +procfds(Proc *p, char *va, int count, long offset) +{ + Fgrp *f; + Chan *c; + char buf[256]; + int n, i, w, ww; + char *a; + + /* print to buf to avoid holding fgrp lock while writing to user space */ + if(count > sizeof buf) + count = sizeof buf; + a = buf; + + qlock(&p->debug); + f = p->fgrp; + if(f == nil){ + qunlock(&p->debug); + return 0; + } + lock(&f->ref.lk); + if(waserror()){ + unlock(&f->ref.lk); + qunlock(&p->debug); + nexterror(); + } + + n = readstr(0, a, count, p->dot->path->s); + n += snprint(a+n, count-n, "\n"); + offset = procoffset(offset, a, &n); + /* compute width of qid.path */ + w = 0; + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + ww = procqidwidth(c); + if(ww > w) + w = ww; + } + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + n += procfdprint(c, i, w, a+n, count-n); + offset = procoffset(offset, a, &n); + } + unlock(&f->ref.lk); + qunlock(&p->debug); + poperror(); + + /* copy result to user space, now that locks are released */ + memmove(va, buf, n); + + return n; +} + +static void +procclose(Chan * c) +{ + if(QID(c->qid) == Qtrace){ + lock(&tlock); + if(topens > 0) + topens--; + if(topens == 0) + proctrace = nil; + unlock(&tlock); + } + if(QID(c->qid) == Qns && c->aux != 0) + free(c->aux); +} + +static void +int2flag(int flag, char *s) +{ + if(flag == 0){ + *s = '\0'; + return; + } + *s++ = '-'; + if(flag & MAFTER) + *s++ = 'a'; + if(flag & MBEFORE) + *s++ = 'b'; + if(flag & MCREATE) + *s++ = 'c'; + if(flag & MCACHE) + *s++ = 'C'; + *s = '\0'; +} + +static int +procargs(Proc *p, char *buf, int nbuf) +{ + int j, k, m; + char *a; + int n; + + a = p->args; + if(p->setargs){ + snprint(buf, nbuf, "%s [%s]", p->text, p->args); + return strlen(buf); + } + n = p->nargs; + for(j = 0; j < nbuf - 1; j += m){ + if(n <= 0) + break; + if(j != 0) + buf[j++] = ' '; + m = snprint(buf+j, nbuf-j, "%q", a); + k = strlen(a) + 1; + a += k; + n -= k; + } + return j; +} + +static int +eventsavailable(void *_) +{ + return tproduced > tconsumed; +} + +static long +procread(Chan *c, void *va, long n, vlong off) +{ + /* NSEG*32 was too small for worst cases */ + char *a, flag[10], *sps, *srv, statbuf[NSEG*64]; + int i, j, m, navail, ne, pid, rsize; + long l; + uchar *rptr; + ulong offset; + Mntwalk *mw; + Proc *p; + Segment *sg, *s; + Ureg kur; + Waitq *wq; + + a = va; + offset = off; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, procgen); + + if(QID(c->qid) == Qtrace){ + if(!eventsavailable(nil)) + return 0; + + rptr = (uchar*)va; + navail = tproduced - tconsumed; + if(navail > n / sizeof(Traceevent)) + navail = n / sizeof(Traceevent); + while(navail > 0) { + ne = ((tconsumed & Emask) + navail > Nevents)? + Nevents - (tconsumed & Emask): navail; + memmove(rptr, &tevents[tconsumed & Emask], + ne * sizeof(Traceevent)); + + tconsumed += ne; + rptr += ne * sizeof(Traceevent); + navail -= ne; + } + return rptr - (uchar*)va; + } + + p = proctab(SLOT(c->qid)); + if(p->pid != PID(c->qid)) + error(Eprocdied); + + switch(QID(c->qid)){ + case Qargs: + qlock(&p->debug); + j = procargs(p, up->genbuf, sizeof up->genbuf); + qunlock(&p->debug); + if(offset >= j) + return 0; + if(offset+n > j) + n = j-offset; + memmove(a, &up->genbuf[offset], n); + return n; + + case Qsyscall: + if(!p->syscalltrace) + return 0; + n = readstr(offset, a, n, p->syscalltrace); + return n; + + case Qmem: + if(offset < USTKTOP) + return procctlmemio(p, offset, n, va, 1); + error("no kernel memory access"); + case Qprofile: + s = p->seg[TSEG]; + if(s == 0 || s->profile == 0) + error("profile is off"); + i = (s->top-s->base)>>LRESPROF; + i *= sizeof(*s->profile); + if(offset >= i) + return 0; + if(offset+n > i) + n = i - offset; + memmove(a, ((char*)s->profile)+offset, n); + return n; + + case Qnote: + qlock(&p->debug); + if(waserror()){ + qunlock(&p->debug); + nexterror(); + } + if(p->pid != PID(c->qid)) + error(Eprocdied); + if(n < 1) /* must accept at least the '\0' */ + error(Etoosmall); + if(p->nnote == 0) + n = 0; + else { + m = strlen(p->note[0].msg) + 1; + if(m > n) + m = n; + memmove(va, p->note[0].msg, m); + ((char*)va)[m-1] = '\0'; + p->nnote--; + memmove(p->note, p->note+1, p->nnote*sizeof(Note)); + n = m; + } + if(p->nnote == 0) + p->notepending = 0; + poperror(); + qunlock(&p->debug); + return n; + + case Qproc: + if(offset >= sizeof(Proc)) + return 0; + if(offset+n > sizeof(Proc)) + n = sizeof(Proc) - offset; + memmove(a, ((char*)p)+offset, n); + return n; + + case Qregs: + rptr = (uchar*)p->dbgreg; + rsize = sizeof(Ureg); + goto regread; + + case Qkregs: + memset(&kur, 0, sizeof(Ureg)); + setkernur(&kur, p); + rptr = (uchar*)&kur; + rsize = sizeof(Ureg); + goto regread; + + case Qfpregs: + rptr = (uchar*)&p->fpsave; + rsize = sizeof(FPsave); + regread: + if(rptr == 0) + error(Enoreg); + if(offset >= rsize) + return 0; + if(offset+n > rsize) + n = rsize - offset; + memmove(a, rptr+offset, n); + return n; + + case Qstatus: + if(offset >= STATSIZE) + return 0; + if(offset+n > STATSIZE) + n = STATSIZE - offset; + + sps = p->psstate; + if(sps == 0) + sps = statename[p->state]; + memset(statbuf, ' ', sizeof statbuf); + memmove(statbuf+0*KNAMELEN, p->text, strlen(p->text)); + memmove(statbuf+1*KNAMELEN, p->user, strlen(p->user)); + memmove(statbuf+2*KNAMELEN, sps, strlen(sps)); + j = 2*KNAMELEN + 12; + + for(i = 0; i < 6; i++) { + l = p->time[i]; + if(i == TReal) + l = msec() - l; + l = TK2MS(l); + readnum(0, statbuf+j+NUMSIZE*i, NUMSIZE, l, NUMSIZE); + } + /* ignore stack, which is mostly non-existent */ + l = 0; + for(i=1; i<NSEG; i++){ + s = p->seg[i]; + if(s) + l += s->top - s->base; + } + readnum(0, statbuf+j+NUMSIZE*6, NUMSIZE, l>>10, NUMSIZE); + readnum(0, statbuf+j+NUMSIZE*7, NUMSIZE, p->basepri, NUMSIZE); + readnum(0, statbuf+j+NUMSIZE*8, NUMSIZE, p->priority, NUMSIZE); + memmove(a, statbuf+offset, n); + return n; + + case Qsegment: + j = 0; + for(i = 0; i < NSEG; i++) { + sg = p->seg[i]; + if(sg == 0) + continue; + j += sprint(statbuf+j, "%-6s %c%c %.8lux %.8lux %4ld\n", + sname[sg->type&SG_TYPE], + sg->type&SG_RONLY ? 'R' : ' ', + sg->profile ? 'P' : ' ', + sg->base, sg->top, sg->ref); + } + if(offset >= j) + return 0; + if(offset+n > j) + n = j-offset; + if(n == 0 && offset == 0) + exhausted("segments"); + memmove(a, &statbuf[offset], n); + return n; + + case Qwait: + if(!canqlock(&p->qwaitr)) + error(Einuse); + + if(waserror()) { + qunlock(&p->qwaitr); + nexterror(); + } + + lock(&p->exl); + if(up == p && p->nchild == 0 && p->waitq == 0) { + unlock(&p->exl); + error(Enochild); + } + pid = p->pid; + while(p->waitq == 0) { + unlock(&p->exl); + sleep(&p->waitr, haswaitq, p); + if(p->pid != pid) + error(Eprocdied); + lock(&p->exl); + } + wq = p->waitq; + p->waitq = wq->next; + p->nwait--; + unlock(&p->exl); + + qunlock(&p->qwaitr); + poperror(); + n = snprint(a, n, "%d %lud %lud %lud %q", + wq->w.pid, + wq->w.time[TUser], wq->w.time[TSys], wq->w.time[TReal], + wq->w.msg); + free(wq); + return n; + + case Qns: + qlock(&p->debug); + if(waserror()){ + qunlock(&p->debug); + nexterror(); + } + if(p->pgrp == nil || p->pid != PID(c->qid)) + error(Eprocdied); + mw = c->aux; + if(mw->cddone){ + qunlock(&p->debug); + poperror(); + return 0; + } + mntscan(mw, p); + if(mw->mh == 0){ + mw->cddone = 1; + i = snprint(a, n, "cd %s\n", p->dot->path->s); + qunlock(&p->debug); + poperror(); + return i; + } + int2flag(mw->cm->mflag, flag); + if(strcmp(mw->cm->to->path->s, "#M") == 0){ + srv = srvname(mw->cm->to->mchan); + i = snprint(a, n, "mount %s %s %s %s\n", flag, + srv==nil? mw->cm->to->mchan->path->s : srv, + mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : ""); + free(srv); + }else + i = snprint(a, n, "bind %s %s %s\n", flag, + mw->cm->to->path->s, mw->mh->from->path->s); + qunlock(&p->debug); + poperror(); + return i; + + case Qnoteid: + return readnum(offset, va, n, p->noteid, NUMSIZE); + case Qfd: + return procfds(p, va, n, offset); + } + error(Egreg); + return 0; /* not reached */ +} + +void +mntscan(Mntwalk *mw, Proc *p) +{ + Pgrp *pg; + Mount *t; + Mhead *f; + int nxt, i; + ulong last, bestmid; + + pg = p->pgrp; + rlock(&pg->ns); + + nxt = 0; + bestmid = ~0; + + last = 0; + if(mw->mh) + last = mw->cm->mountid; + + for(i = 0; i < MNTHASH; i++) { + for(f = pg->mnthash[i]; f; f = f->hash) { + for(t = f->mount; t; t = t->next) { + if(mw->mh == 0 || + (t->mountid > last && t->mountid < bestmid)) { + mw->cm = t; + mw->mh = f; + bestmid = mw->cm->mountid; + nxt = 1; + } + } + } + } + if(nxt == 0) + mw->mh = 0; + + runlock(&pg->ns); +} + +static long +procwrite(Chan *c, void *va, long n, vlong off) +{ + int id, m; + Proc *p, *t, *et; + char *a, *arg, buf[ERRMAX]; + ulong offset = off; + + a = va; + if(c->qid.type & QTDIR) + error(Eisdir); + + p = proctab(SLOT(c->qid)); + + /* Use the remembered noteid in the channel rather + * than the process pgrpid + */ + if(QID(c->qid) == Qnotepg) { + pgrpnote(NOTEID(c->pgrpid), va, n, NUser); + return n; + } + + qlock(&p->debug); + if(waserror()){ + qunlock(&p->debug); + nexterror(); + } + if(p->pid != PID(c->qid)) + error(Eprocdied); + + switch(QID(c->qid)){ + case Qargs: + if(n == 0) + error(Eshort); + if(n >= ERRMAX) + error(Etoobig); + arg = malloc(n+1); + if(arg == nil) + error(Enomem); + memmove(arg, va, n); + m = n; + if(arg[m-1] != 0) + arg[m++] = 0; + free(p->args); + p->nargs = m; + p->args = arg; + p->setargs = 1; + break; + + case Qmem: + if(p->state != Stopped) + error(Ebadctl); + + n = procctlmemio(p, offset, n, va, 0); + break; + + case Qregs: + if(offset >= sizeof(Ureg)) + n = 0; + else if(offset+n > sizeof(Ureg)) + n = sizeof(Ureg) - offset; + if(p->dbgreg == 0) + error(Enoreg); + setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n); + break; + + case Qfpregs: + if(offset >= sizeof(FPsave)) + n = 0; + else if(offset+n > sizeof(FPsave)) + n = sizeof(FPsave) - offset; + memmove((uchar*)&p->fpsave+offset, va, n); + break; + + case Qctl: + procctlreq(p, va, n); + break; + + case Qnote: + if(p->kp) + error(Eperm); + if(n >= ERRMAX-1) + error(Etoobig); + memmove(buf, va, n); + buf[n] = 0; + if(!postnote(p, 0, buf, NUser)) + error("note not posted"); + break; + case Qnoteid: + id = atoi(a); + if(id == p->pid) { + p->noteid = id; + break; + } + t = proctab(0); + for(et = t+conf.nproc; t < et; t++) { + if(t->state == Dead) + continue; + if(id == t->noteid) { + if(strcmp(p->user, t->user) != 0) + error(Eperm); + p->noteid = id; + break; + } + } + if(p->noteid != id) + error(Ebadarg); + break; + default: + pprint("unknown qid in procwrite\n"); + error(Egreg); + } + poperror(); + qunlock(&p->debug); + return n; +} + +Dev procdevtab = { + 'p', + "proc", + + devreset, + procinit, + devshutdown, + procattach, + procwalk, + procstat, + procopen, + devcreate, + procclose, + procread, + devbread, + procwrite, + devbwrite, + devremove, + procwstat, +}; + +Chan* +proctext(Chan *c, Proc *p) +{ + Chan *tc; + Image *i; + Segment *s; + + s = p->seg[TSEG]; + if(s == 0) + error(Enonexist); + if(p->state==Dead) + error(Eprocdied); + + lock(&s->ref.lk); + i = s->image; + if(i == 0) { + unlock(&s->ref.lk); + error(Eprocdied); + } + unlock(&s->ref.lk); + + lock(&i->ref.lk); + if(waserror()) { + unlock(&i->ref.lk); + nexterror(); + } + + tc = i->c; + if(tc == 0) + error(Eprocdied); + + if(incref(&tc->ref) == 1 || (tc->flag&COPEN) == 0 || tc->mode!=OREAD) { + cclose(tc); + error(Eprocdied); + } + + if(p->pid != PID(c->qid)) + error(Eprocdied); + + unlock(&i->ref.lk); + poperror(); + + return tc; +} + +void +procstopwait(Proc *p, int ctl) +{ + int pid; + + if(p->pdbg) + error(Einuse); + if(procstopped(p) || p->state == Broken) + return; + + if(ctl != 0) + p->procctl = ctl; + p->pdbg = up; + pid = p->pid; + qunlock(&p->debug); + up->psstate = "Stopwait"; + if(waserror()) { + p->pdbg = 0; + qlock(&p->debug); + nexterror(); + } + sleep(&up->sleep, procstopped, p); + poperror(); + qlock(&p->debug); + if(p->pid != pid) + error(Eprocdied); +} + +static void +procctlcloseone(Proc *p, Fgrp *f, int fd) +{ + Chan *c; + + c = f->fd[fd]; + if(c == nil) + return; + f->fd[fd] = nil; + unlock(&f->ref.lk); + qunlock(&p->debug); + cclose(c); + qlock(&p->debug); + lock(&f->ref.lk); +} + +void +procctlclosefiles(Proc *p, int all, int fd) +{ + int i; + Fgrp *f; + + f = p->fgrp; + if(f == nil) + error(Eprocdied); + + lock(&f->ref.lk); + f->ref.ref++; + if(all) + for(i = 0; i < f->maxfd; i++) + procctlcloseone(p, f, i); + else + procctlcloseone(p, f, fd); + unlock(&f->ref.lk); + closefgrp(f); +} + + +void +procctlreq(Proc *p, char *va, int n) +{ + Segment *s; + int npc, pri; + Cmdbuf *cb; + Cmdtab *ct; + + if(p->kp) /* no ctl requests to kprocs */ + error(Eperm); + + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, proccmd, nelem(proccmd)); + + switch(ct->index){ + case CMclose: + procctlclosefiles(p, 0, atoi(cb->f[1])); + break; + case CMclosefiles: + procctlclosefiles(p, 1, 0); + break; + case CMhang: + p->hang = 1; + break; + case CMkill: + switch(p->state) { + case Broken: + unbreak(p); + break; + case Stopped: + p->procctl = Proc_exitme; + postnote(p, 0, "sys: killed", NExit); + ready(p); + break; + default: + p->procctl = Proc_exitme; + postnote(p, 0, "sys: killed", NExit); + } + break; + case CMnohang: + p->hang = 0; + break; + case CMnoswap: + p->noswap = 1; + break; + case CMpri: + pri = atoi(cb->f[1]); + if(pri > PriNormal && !iseve()) + error(Eperm); + procpriority(p, pri, 0); + break; + case CMfixedpri: + pri = atoi(cb->f[1]); + if(pri > PriNormal && !iseve()) + error(Eperm); + procpriority(p, pri, 1); + break; + case CMprivate: + p->privatemem = 1; + break; + case CMprofile: + s = p->seg[TSEG]; + if(s == 0 || (s->type&SG_TYPE) != SG_TEXT) + error(Ebadctl); + if(s->profile != 0) + free(s->profile); + npc = (s->top-s->base)>>LRESPROF; + s->profile = malloc(npc*sizeof(*s->profile)); + if(s->profile == 0) + error(Enomem); + break; + case CMstart: + if(p->state != Stopped) + error(Ebadctl); + ready(p); + break; + case CMstartstop: + if(p->state != Stopped) + error(Ebadctl); + p->procctl = Proc_traceme; + ready(p); + procstopwait(p, Proc_traceme); + break; + case CMstartsyscall: + if(p->state != Stopped) + error(Ebadctl); + p->procctl = Proc_tracesyscall; + ready(p); + procstopwait(p, Proc_tracesyscall); + break; + case CMstop: + procstopwait(p, Proc_stopme); + break; + case CMwaitstop: + procstopwait(p, 0); + break; + case CMwired: + procwired(p, atoi(cb->f[1])); + break; + case CMtrace: + switch(cb->nf){ + case 1: + p->trace ^= 1; + break; + case 2: + p->trace = (atoi(cb->f[1]) != 0); + break; + default: + error("args"); + } + break; + } + + poperror(); + free(cb); +} + +int +procstopped(void *a) +{ + Proc *p = a; + return p->state == Stopped; +} + +int +procctlmemio(Proc *p, ulong offset, int n, void *va, int read) +{ + KMap *k; + Pte *pte; + Page *pg; + Segment *s; + ulong soff, l; + char *a = va, *b; + + for(;;) { + s = seg(p, offset, 1); + if(s == 0) + error(Ebadarg); + + if(offset+n >= s->top) + n = s->top-offset; + + if(!read && (s->type&SG_TYPE) == SG_TEXT) + s = txt2data(p, s); + + s->steal++; + soff = offset-s->base; + if(waserror()) { + s->steal--; + nexterror(); + } + if(fixfault(s, offset, read, 0) == 0) + break; + poperror(); + s->steal--; + } + poperror(); + pte = s->map[soff/PTEMAPMEM]; + if(pte == 0) + panic("procctlmemio"); + pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG]; + if(pagedout(pg)) + panic("procctlmemio1"); + + l = BY2PG - (offset&(BY2PG-1)); + if(n > l) + n = l; + + k = kmap(pg); + if(waserror()) { + s->steal--; + kunmap(k); + nexterror(); + } + b = (char*)VA(k); + b += offset&(BY2PG-1); + if(read == 1) + memmove(a, b, n); /* This can fault */ + else + memmove(b, a, n); + kunmap(k); + poperror(); + + /* Ensure the process sees text page changes */ + + s->steal--; + + if(read == 0) + p->newtlb = 1; + + return n; +} + +Segment* +txt2data(Proc *p, Segment *s) +{ + int i; + Segment *ps; + + ps = newseg(SG_DATA, s->base, s->size); + ps->image = s->image; + incref(&ps->image->ref); + ps->fstart = s->fstart; + ps->flen = s->flen; + ps->flushme = 1; + + qlock(&p->seglock); + for(i = 0; i < NSEG; i++) + if(p->seg[i] == s) + break; + if(i == NSEG) + panic("segment gone"); + + qunlock(&s->lk); + putseg(s); + qlock(&ps->lk); + p->seg[i] = ps; + qunlock(&p->seglock); + + return ps; +} + +Segment* +data2txt(Segment *s) +{ + Segment *ps; + + ps = newseg(SG_TEXT, s->base, s->size); + ps->image = s->image; + incref(&ps->image->ref); + ps->fstart = s->fstart; + ps->flen = s->flen; + ps->flushme = 1; + + return ps; +} diff --git a/a/devproc.ed b/a/devproc.ed @@ -0,0 +1,41 @@ +/^$/a +extern uchar _end[]; // Plan 9 VX + +. +,s!(ulong)end!(ulong)_end!g +g/CMfair/d +g/CMunfair/d +g/edf/d +g/^profclock/ s/Timer \*)/Timer *t)/ +g/^procgen/ s/int,/int _,/ +g/^_proctrace/ s/Tevent/int/ +g/^[a-z]/ s/(void \*)/(void *_)/ +,s!lock(f)!lock(\&f->ref.lk)!g +,s!lock(s)!lock(\&s->ref.lk)!g +,s!lock(i)!lock(\&i->ref.lk)!g +,s!ref(tc)!ref(\&tc->ref)!g +,s!ref++!ref.ref++!g +,s!ref(ps->image)!ref(\&ps->image->ref)!g +/^parsetime/-1,/^ return nil/+1d +/^ vlong time;/d +/^ char \*e;/d +/^ void (\*pt)/d +/^ CMperiod,/,/^ CMevent,/d +/^ CMperiod,/,/^ CMevent,/d +/^ case CMperiod:/,/^ case CMevent:/+4d +g/real time/d +/if(s->flushme)/d +d +,s/MACHP(0)->ticks/msec()/g +/{Qprofile}/a + "syscall", {Qsyscall}, 0, 0400, +. +/^profclock/;# +/^{/+1,/^}/-1d +/^procopen/;# +/^procread/;# +/Confmem/d +/KZERO/ s/KZERO/USTKTOP/ ++2,/case .*:/-1c + error("no kernel memory access"); +. diff --git a/a/devroot.c b/a/devroot.c @@ -0,0 +1,261 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Qdir = 0, + Qboot = 0x1000, + + Nrootfiles = 32, + Nbootfiles = 32, +}; + +typedef struct Dirlist Dirlist; +struct Dirlist +{ + uint base; + Dirtab *dir; + uchar **data; + int ndir; + int mdir; +}; + +static Dirtab rootdir[Nrootfiles] = { + "#/", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "boot", {Qboot, 0, QTDIR}, 0, DMDIR|0555, +}; +static uchar *rootdata[Nrootfiles]; +static Dirlist rootlist = +{ + 0, + rootdir, + rootdata, + 2, + Nrootfiles +}; + +static Dirtab bootdir[Nbootfiles] = { + "boot", {Qboot, 0, QTDIR}, 0, DMDIR|0555, +}; +static uchar *bootdata[Nbootfiles]; +static Dirlist bootlist = +{ + Qboot, + bootdir, + bootdata, + 1, + Nbootfiles +}; + +/* + * add a file to the list + */ +static void +addlist(Dirlist *l, char *name, uchar *contents, ulong len, int perm) +{ + Dirtab *d; + + if(l->ndir >= l->mdir) + panic("too many root files"); + l->data[l->ndir] = contents; + d = &l->dir[l->ndir]; + strcpy(d->name, name); + d->length = len; + d->perm = perm; + d->qid.type = 0; + d->qid.vers = 0; + d->qid.path = ++l->ndir + l->base; + if(perm & DMDIR) + d->qid.type |= QTDIR; +} + +/* + * add a root file + */ +void +addbootfile(char *name, uchar *contents, ulong len) +{ + addlist(&bootlist, name, contents, len, 0555); +} + +/* + * add a root directory + */ +static void +addrootdir(char *name) +{ + addlist(&rootlist, name, nil, 0, DMDIR|0555); +} + +static void +rootreset(void) +{ + addrootdir("bin"); + addrootdir("dev"); + addrootdir("env"); + addrootdir("fd"); + addrootdir("mnt"); + addrootdir("net"); + addrootdir("net.alt"); + addrootdir("proc"); + addrootdir("root"); + addrootdir("srv"); +} + +static Chan* +rootattach(char *spec) +{ + return devattach('/', spec); +} + +static int +rootgen(Chan *c, char *name, Dirtab *dt, int i, int s, Dir *dp) +{ + int t; + Dirtab *d; + Dirlist *l; + + switch((int)c->qid.path){ + case Qdir: + if(s == DEVDOTDOT){ + devdir(c, (Qid){Qdir, 0, QTDIR}, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, rootlist.dir, rootlist.ndir, s, dp); + case Qboot: + if(s == DEVDOTDOT){ + devdir(c, (Qid){Qdir, 0, QTDIR}, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, bootlist.dir, bootlist.ndir, s, dp); + default: + if(s == DEVDOTDOT){ + if((int)c->qid.path < Qboot) + devdir(c, (Qid){Qdir, 0, QTDIR}, "#/", 0, eve, 0555, dp); + else + devdir(c, (Qid){Qboot, 0, QTDIR}, "#/", 0, eve, 0555, dp); + return 1; + } + if(s != 0) + return -1; + if((int)c->qid.path < Qboot){ + t = c->qid.path-1; + l = &rootlist; + }else{ + t = c->qid.path - Qboot - 1; + l = &bootlist; + } + if(t >= l->ndir) + return -1; +if(t < 0){ +print("rootgen %llud %d %d\n", c->qid.path, s, t); +panic("whoops"); +} + d = &l->dir[t]; + devdir(c, d->qid, d->name, d->length, eve, d->perm, dp); + return 1; + } +} + +static Walkqid* +rootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, rootgen); +} + +static int +rootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, nil, 0, rootgen); +} + +static Chan* +rootopen(Chan *c, int omode) +{ + return devopen(c, omode, nil, 0, devgen); +} + +/* + * sysremove() knows this is a nop + */ +static void +rootclose(Chan *c) +{ +} + +static long +rootread(Chan *c, void *buf, long n, vlong off) +{ + ulong t; + Dirtab *d; + Dirlist *l; + uchar *data; + ulong offset = off; + + t = c->qid.path; + switch(t){ + case Qdir: + case Qboot: + return devdirread(c, buf, n, nil, 0, rootgen); + } + + if(t<Qboot) + l = &rootlist; + else{ + t -= Qboot; + l = &bootlist; + } + + t--; + if(t >= l->ndir) + error(Egreg); + + d = &l->dir[t]; + data = l->data[t]; + if(offset >= d->length) + return 0; + if(offset+n > d->length) + n = d->length - offset; +#ifdef asdf +print("[%d] kaddr %.8ulx base %.8ulx offset %ld (%.8ulx), n %d %.8ulx %.8ulx %.8ulx\n", + t, buf, data, offset, offset, n, + ((ulong*)(data+offset))[0], + ((ulong*)(data+offset))[1], + ((ulong*)(data+offset))[2]); +#endif + memmove(buf, data+offset, n); + return n; +} + +static long +rootwrite(Chan *c, void *v, long l, vlong vl) +{ + error(Egreg); + return 0; +} + +Dev rootdevtab = { + '/', + "root", + + rootreset, + devinit, + devshutdown, + rootattach, + rootwalk, + rootstat, + rootopen, + devcreate, + rootclose, + rootread, + devbread, + rootwrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/a/devroot.ed b/a/devroot.ed @@ -0,0 +1,4 @@ +g/^rootgen/ s/Dirtab\*, int/Dirtab *dt, int i/ +g/^rootclose/ s/(Chan\*)/(Chan *c)/ +,s/^#endif asdf/#endif/ +g/^rootwrite/ s/(Chan\*, void\*, long, vlong)/(Chan *c, void *v, long l, vlong vl)/ diff --git a/a/devsd.ed b/a/devsd.ed @@ -0,0 +1,28 @@ +/^sdaddpart/-1 s/static // +,s/pp->perm/pp->perm.perm/g +,s/pp->name/pp->perm.name/g +,s/pp->user/pp->perm.user/g +,s/unit->perm/unit->perm.perm/g +,s/unit->name/unit->perm.name/g +,s/unit->user/unit->perm.user/g +,s/pp->SDperm/pp->perm/g +/^sdgen/ s/char\*, Dirtab\*, int,/char *name, Dirtab *dt, int j,/ +/^sdinitpart/+2;/^$/d +/sdaddpart/+1;/^ }/-1c + partition(unit); +. +$a + +SDpart* +sdfindpart(SDunit *unit, char *name) +{ + int i; + + for(i=0; i<unit->npart; i++) { + if(strcmp(unit->part[i].perm.name, name) == 0){ + return &unit->part[i]; + } + } + return nil; +} +. diff --git a/a/devsrv.c b/a/devsrv.c @@ -0,0 +1,376 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + + +typedef struct Srv Srv; +struct Srv +{ + char *name; + char *owner; + ulong perm; + Chan *chan; + Srv *link; + ulong path; +}; + +static QLock srvlk; +static Srv *srv; +static int qidpath; + +static int +srvgen(Chan *c, char *name, Dirtab *dt, int i, int s, Dir *dp) +{ + Srv *sp; + Qid q; + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#s", 0, eve, 0555, dp); + return 1; + } + + qlock(&srvlk); + for(sp = srv; sp && s; sp = sp->link) + s--; +